[feat] add system language support across desktop and mobile

This commit is contained in:
dijunkun
2026-10-27 12:35:47 +08:00
parent b4acf0b597
commit b07f457e76
105 changed files with 21711 additions and 1627 deletions
@@ -6,11 +6,12 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application android:theme="@style/AppTheme" android:label="CrossDesk"
<application android:name=".CrossDeskApplication" android:localeConfig="@xml/locales_config"
android:theme="@style/AppTheme" android:label="CrossDesk"
android:icon="@mipmap/ic_launcher" android:allowBackup="false"
android:fullBackupContent="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:supportsRtl="true" android:usesCleartextTraffic="false">
android:supportsRtl="false" android:usesCleartextTraffic="false">
<service android:name=".RemoteSessionService" android:exported="false"
android:foregroundServiceType="specialUse">
<property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
@@ -26,18 +26,18 @@ final class AboutPages {
LicenseCatalog loaded;try{loaded=new LicenseCatalog(activity);}catch(Exception error){loaded=null;}catalog=loaded;
}
void show(){
LinearLayout content=activity.formPage("about","关于 CrossDesk",settings);
LinearLayout version=activity.section(content,"");value(version,"版本",applicationVersion());
LinearLayout content=activity.formPage("about",L10n.tr("关于 CrossDesk"),settings);
LinearLayout version=activity.section(content,"");value(version,L10n.tr("版本"),applicationVersion());
updateRow=ui.row();updateRow.setPadding(ui.dp(16),0,ui.dp(16),0);updateRow.addView(activity.updateDot(),ui.size(8,8));
updateLink=ui.action("",BLUE,()->open("https://crossdesk.cn"));updateLink.setGravity(Gravity.START|Gravity.CENTER_VERTICAL);updateLink.setPadding(ui.dp(8),ui.dp(12),0,ui.dp(12));updateRow.addView(updateLink,new LinearLayout.LayoutParams(0,-2,1));version.addView(updateRow);
updateStatus=ui.text("",13,false);updateStatus.setPadding(ui.dp(16),ui.dp(8),ui.dp(16),ui.dp(8));version.addView(updateStatus);
checkButton=ui.action("检查更新",BLUE,activity::checkAppUpdates);version.addView(checkButton);renderUpdates();ui.gap(content,28);
checkButton=ui.action(L10n.tr("检查更新"),BLUE,activity::checkAppUpdates);version.addView(checkButton);renderUpdates();ui.gap(content,28);
if(catalog==null){unavailable(content);return;}
LinearLayout links=activity.section(content,"");
activity.link(links,"软件许可","",()->component(catalog.find("crossdesk"),this::show));ui.divider(links);
activity.link(links,"开源组件","",this::components);ui.divider(links);
activity.link(links,"源码与构建说明","",this::source);
activity.footer(content,"许可与版权声明可离线查阅。");
activity.link(links,L10n.tr("软件许可"),"",()->component(catalog.find("crossdesk"),this::show));ui.divider(links);
activity.link(links,L10n.tr("开源组件"),"",this::components);ui.divider(links);
activity.link(links,L10n.tr("源码与构建说明"),"",this::source);
activity.footer(content,L10n.tr("许可与版权声明可离线查阅。"));
}
private String applicationVersion(){
return activity.applicationVersion();
@@ -45,11 +45,11 @@ final class AboutPages {
void renderUpdates(){
if(updateStatus==null)return;
updateRow.setVisibility(updates.updateAvailable()?View.VISIBLE:View.GONE);
updateLink.setText("新版本可用:v"+updates.availableVersion);
String status=!activity.hasNetworkConsent()?"完成隐私授权后可检查更新":switch(updates.status){
case CHECKING -> "正在检查更新…";
case UP_TO_DATE -> "当前已是最新版本";
case FAILED -> "检查失败,请稍后重试";
updateLink.setText(L10n.format("新版本可用:v{0}",updates.availableVersion));
String status=!activity.hasNetworkConsent()?L10n.tr("完成隐私授权后可检查更新"):switch(updates.status){
case CHECKING -> L10n.tr("正在检查更新…");
case UP_TO_DATE -> L10n.tr("当前已是最新版本");
case FAILED -> L10n.tr("检查失败,请稍后重试");
default -> "";
};
updateStatus.setText(status);updateStatus.setVisibility(status.isEmpty()?View.GONE:View.VISIBLE);
@@ -58,11 +58,11 @@ final class AboutPages {
checkButton.setAlpha(checkButton.isEnabled()?1f:.5f);
}
private void components(){
LinearLayout content=activity.formPage("open-source","开源组件",this::show);
LinearLayout notice=activity.section(content,"OpenFEC 告知");
paragraph(notice,"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。",SECONDARY);
LinearLayout content=activity.formPage("open-source",L10n.tr("开源组件"),this::show);
LinearLayout notice=activity.section(content,L10n.tr("OpenFEC 告知"));
paragraph(notice,L10n.tr("本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。"),SECONDARY);
ui.divider(notice);paragraph(notice,"(c) Copyright 2009 - 2012 INRIA - All rights reserved",INK);ui.divider(notice);
activity.link(notice,"OpenFEC 许可与源码","",()->component(catalog.find("openfec"),this::components));ui.gap(content,28);
activity.link(notice,L10n.tr("OpenFEC 许可与源码"),"",()->component(catalog.find("openfec"),this::components));ui.gap(content,28);
LinearLayout list=activity.section(content,"");boolean first=true;
for(LicenseCatalog.Component item:catalog.components){
if(item.id.equals("crossdesk")||item.id.equals("openfec"))continue;
@@ -73,41 +73,41 @@ final class AboutPages {
row.addView(labels,new LinearLayout.LayoutParams(0,-2,1));TextView chevron=ui.text(" ›",23,false);chevron.setTextColor(0xFFB7B7BD);row.addView(chevron);
row.setContentDescription(item.name+","+item.license);row.setClickable(true);row.setFocusable(true);row.setOnClickListener(v->component(item,this::components));list.addView(row);
}
activity.footer(content,"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。");
activity.footer(content,L10n.tr("各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。"));
}
private void component(LicenseCatalog.Component item,Runnable back){
LinearLayout content=activity.formPage("license:"+item.id,item.name,back);LinearLayout info=activity.section(content,"");
String version=item.id.equals("crossdesk")?applicationVersion():item.version.length()==40?item.version.substring(0,12):item.version;
value(info,"版本",version);ui.divider(info);value(info,"许可",item.license);ui.divider(info);
external(info,"查看源码",item.sourceURL);
value(info,L10n.tr("版本"),version);ui.divider(info);value(info,L10n.tr("许可"),item.license);ui.divider(info);
external(info,L10n.tr("查看源码"),item.sourceURL);
if(!item.buildDocuments.isEmpty()||!item.buildSourceURL.isEmpty()){
ui.divider(info);
if(!item.buildDocuments.isEmpty())activity.link(info,"构建配方与项目补丁","",()->buildSources(item,back));
else external(info,"构建配方与项目补丁",item.buildSourceURL);
if(!item.buildDocuments.isEmpty())activity.link(info,L10n.tr("构建配方与项目补丁"),"",()->buildSources(item,back));
else external(info,L10n.tr("构建配方与项目补丁"),item.buildSourceURL);
}
ui.gap(content,16);LinearLayout docs=activity.section(content,"许可与版权声明");
ui.gap(content,16);LinearLayout docs=activity.section(content,L10n.tr("许可与版权声明"));
for(int i=0;i<item.documents.size();i++){
if(i>0)ui.divider(docs);LicenseCatalog.Document document=item.documents.get(i);
activity.link(docs,item.documentTitle(document),"",()->document(item.documentTitle(document),document,()->component(item,back)));
}
}
private void source(){
LinearLayout content=activity.formPage("source","源码与构建说明",this::show);
LinearLayout info=activity.section(content,"本版本源码与构建说明");
if(catalog.source==null){paragraph(info,"本版本源码信息暂不可用。",SECONDARY);ui.divider(info);external(info,"项目主页","https://github.com/kunkundi/crossdesk");return;}
LinearLayout content=activity.formPage("source",L10n.tr("源码与构建说明"),this::show);
LinearLayout info=activity.section(content,L10n.tr("本版本源码与构建说明"));
if(catalog.source==null){paragraph(info,L10n.tr("本版本源码信息暂不可用。"),SECONDARY);ui.divider(info);external(info,L10n.tr("项目主页"),"https://github.com/kunkundi/crossdesk");return;}
String revision=catalog.source.optString("revision","");String tag=catalog.source.isNull("tag")?"":catalog.source.optString("tag","");
value(info,"源码版本",tag.isEmpty()?revision.substring(0,Math.min(12,revision.length())):tag);ui.divider(info);
if(catalog.source.optBoolean("isModified")){paragraph(info,"开发版本包含尚未发布的修改,以下链接为基础版本源码。",SECONDARY);ui.divider(info);}
external(info,"查看源码",catalog.source.optString("sourceURL",catalog.find("crossdesk").sourceURL));ui.divider(info);
value(info,L10n.tr("源码版本"),tag.isEmpty()?revision.substring(0,Math.min(12,revision.length())):tag);ui.divider(info);
if(catalog.source.optBoolean("isModified")){paragraph(info,L10n.tr("开发版本包含尚未发布的修改,以下链接为基础版本源码。"),SECONDARY);ui.divider(info);}
external(info,L10n.tr("查看源码"),catalog.source.optString("sourceURL",catalog.find("crossdesk").sourceURL));ui.divider(info);
try{
List<LicenseCatalog.Document> documents=LicenseCatalog.documents(catalog.source.optJSONArray("buildDocuments"));
if(!documents.isEmpty())actionLink(info,"构建说明",()->document("构建说明",documents.get(0),this::source));
else external(info,"构建说明",catalog.source.optString("buildInstructionsURL","https://github.com/kunkundi/crossdesk"));
}catch(org.json.JSONException error){paragraph(info,"本版本源码信息暂不可用。",SECONDARY);}
if(!documents.isEmpty())actionLink(info,L10n.tr("构建说明"),()->document(L10n.tr("构建说明"),documents.get(0),this::source));
else external(info,L10n.tr("构建说明"),catalog.source.optString("buildInstructionsURL","https://github.com/kunkundi/crossdesk"));
}catch(org.json.JSONException error){paragraph(info,L10n.tr("本版本源码信息暂不可用。"),SECONDARY);}
}
private void buildSources(LicenseCatalog.Component item,Runnable back){
LinearLayout content=activity.formPage("build:"+item.id,"构建配方与项目补丁",()->component(item,back));
if(!item.buildSourceURL.isEmpty()){LinearLayout upstream=activity.section(content,"");external(upstream,"查看源码",item.buildSourceURL);ui.gap(content,28);}
LinearLayout content=activity.formPage("build:"+item.id,L10n.tr("构建配方与项目补丁"),()->component(item,back));
if(!item.buildSourceURL.isEmpty()){LinearLayout upstream=activity.section(content,"");external(upstream,L10n.tr("查看源码"),item.buildSourceURL);ui.gap(content,28);}
LinearLayout docs=activity.section(content,"");
for(int i=0;i<item.buildDocuments.size();i++){
if(i>0)ui.divider(docs);LicenseCatalog.Document document=item.buildDocuments.get(i);
@@ -129,10 +129,10 @@ final class AboutPages {
private void actionLink(LinearLayout parent,String title,Runnable action){TextView link=ui.action(title,BLUE,action);link.setTypeface(android.graphics.Typeface.DEFAULT);link.setGravity(Gravity.START|Gravity.CENTER_VERTICAL);link.setPadding(ui.dp(16),ui.dp(12),ui.dp(16),ui.dp(12));parent.addView(link);}
private void open(String url){
Uri uri=Uri.parse(url);if(!"https".equals(uri.getScheme())&&!"http".equals(uri.getScheme()))return;
try{activity.startActivity(new Intent(Intent.ACTION_VIEW,uri).addCategory(Intent.CATEGORY_BROWSABLE));}catch(ActivityNotFoundException error){Toast.makeText(activity,"未找到可打开链接的浏览器",Toast.LENGTH_SHORT).show();}
try{activity.startActivity(new Intent(Intent.ACTION_VIEW,uri).addCategory(Intent.CATEGORY_BROWSABLE));}catch(ActivityNotFoundException error){Toast.makeText(activity,L10n.tr("未找到可打开链接的浏览器"),Toast.LENGTH_SHORT).show();}
}
private void unavailable(LinearLayout content){
content.setGravity(Gravity.CENTER);ui.gap(content,32);TextView icon=ui.text("▤",36,false);icon.setTextColor(SECONDARY);content.addView(icon);ui.gap(content,12);
content.addView(ui.text("无法读取开源许可",17,true));TextView message=ui.text("应用中的许可文件缺失或损坏,请重新安装应用。",14,false);message.setGravity(Gravity.CENTER);message.setTextColor(SECONDARY);content.addView(message);
content.addView(ui.text(L10n.tr("无法读取开源许可"),17,true));TextView message=ui.text(L10n.tr("应用中的许可文件缺失或损坏,请重新安装应用。"),14,false);message.setGravity(Gravity.CENTER);message.setTextColor(SECONDARY);content.addView(message);
}
}
@@ -51,15 +51,15 @@ final class AnnouncementView extends ScrollView {
TextView text=ui.text(value,14,false);text.setTextColor(color);text.setLineSpacing(ui.dp(3),1);return text;
}
private void renderList(){
if(inbox.deleteFailed){content.addView(message("无法删除这条公告,请重试。",RED));ui.gap(content,12);}
if(inbox.deleteFailed){content.addView(message(L10n.tr("无法删除这条公告,请重试。"),RED));ui.gap(content,12);}
if(!inbox.isConnected()){
content.addView(message("连接服务器后可获取最新公告。",SECONDARY));
content.addView(ui.action("连接服务器",BLUE,authorize));ui.gap(content,8);
content.addView(message(L10n.tr("连接服务器后可获取最新公告。"),SECONDARY));
content.addView(ui.action(L10n.tr("连接服务器"),BLUE,authorize));ui.gap(content,8);
}else if(inbox.failed){
LinearLayout error=ui.row();error.addView(message("公告加载失败,请重试。",SECONDARY),new LinearLayout.LayoutParams(0,-2,1));
error.addView(ui.action("重试",BLUE,inbox::refresh),ui.size(64,48));content.addView(error);ui.gap(content,8);
LinearLayout error=ui.row();error.addView(message(L10n.tr("公告加载失败,请重试。"),SECONDARY),new LinearLayout.LayoutParams(0,-2,1));
error.addView(ui.action(L10n.tr("重试"),BLUE,inbox::refresh),ui.size(64,48));content.addView(error);ui.gap(content,8);
}
countLabel=ui.text("共 "+inbox.total+" 条 · "+inbox.unread+" 条未读",13,false);
countLabel=ui.text(L10n.format("共 {0} 条 · {1} 条未读",inbox.total,inbox.unread),13,false);
countLabel.setTextColor(SECONDARY);countLabel.setPadding(ui.dp(16),ui.dp(8),0,ui.dp(8));content.addView(countLabel);
LinearLayout card=ui.column();ui.card(card,12);content.addView(card);
for(AnnouncementInbox.Item item:inbox.items){
@@ -69,11 +69,11 @@ final class AnnouncementView extends ScrollView {
if(inbox.loading){
LinearLayout loading=ui.row();loading.setGravity(Gravity.CENTER);loading.setPadding(0,ui.dp(18),0,ui.dp(18));
ProgressBar spinner=new ProgressBar(getContext());spinner.setIndeterminateTintList(ColorStateList.valueOf(BLUE));loading.addView(spinner,ui.size(20,20));
TextView label=message("加载中…",SECONDARY);label.setPadding(ui.dp(10),0,0,0);loading.addView(label);card.addView(loading);
TextView label=message(L10n.tr("加载中…"),SECONDARY);label.setPadding(ui.dp(10),0,0,0);loading.addView(label);card.addView(loading);
}else if(inbox.items.size()<inbox.total&&!inbox.failed){
TextView more=ui.action("加载更多",BLUE,inbox::loadMore);more.setEnabled(inbox.isConnected());card.addView(more,ui.size(-1,48));
TextView more=ui.action(L10n.tr("加载更多"),BLUE,inbox::loadMore);more.setEnabled(inbox.isConnected());card.addView(more,ui.size(-1,48));
}else if(inbox.loaded&&inbox.total==0&&!inbox.failed&&inbox.isConnected()){
TextView empty=message("暂无公告",SECONDARY);empty.setGravity(Gravity.CENTER);card.addView(empty,ui.size(-1,120));
TextView empty=message(L10n.tr("暂无公告"),SECONDARY);empty.setGravity(Gravity.CENTER);card.addView(empty,ui.size(-1,120));
}
}
private View itemRow(AnnouncementInbox.Item item){
@@ -86,23 +86,23 @@ final class AnnouncementView extends ScrollView {
words.addView(title,ui.size(-1,-2));ui.gap(words,7);TextView date=ui.text(date(item),12,false);date.setTextColor(SECONDARY);words.addView(date);
top.addView(words,new LinearLayout.LayoutParams(0,-2,1));row.addView(top,new LinearLayout.LayoutParams(0,-2,1));
TextView chevron=ui.text("›",23,false);chevron.setTextColor(0xFFB7B7BD);chevron.setPadding(ui.dp(10),0,0,0);row.addView(chevron);
row.setContentDescription(item.title+","+date(item)+","+(item.read?"已读":"未读"));row.setFocusable(true);
row.setContentDescription(item.title+","+date(item)+","+(item.read?L10n.tr("已读"):L10n.tr("未读")));row.setFocusable(true);
row.setOnClickListener(v->open.accept(item));row.setOnLongClickListener(v->{confirmDelete(item);return true;});
return new SwipeRow(row,()->confirmDelete(item));
}
private void confirmDelete(AnnouncementInbox.Item item){
if(deleteDialog!=null)deleteDialog.dismiss();
deleteDialog=new AlertDialog.Builder(getContext()).setTitle("删除公告").setMessage("删除这条公告?")
.setNegativeButton("取消",null).setPositiveButton("删除",(dialog,which)->inbox.dismiss(item)).show();
deleteDialog=new AlertDialog.Builder(getContext()).setTitle(L10n.tr("删除公告")).setMessage(L10n.tr("删除这条公告?"))
.setNegativeButton(L10n.tr("取消"),null).setPositiveButton(L10n.tr("删除"),(dialog,which)->inbox.dismiss(item)).show();
}
private void renderDetail(){
AnnouncementInbox.Item item=inbox.find(id,revision);
if(item==null){content.addView(message("这条公告已更新或删除,请返回公告列表查看。",SECONDARY));return;}
if(item==null){content.addView(message(L10n.tr("这条公告已更新或删除,请返回公告列表查看。"),SECONDARY));return;}
content.addView(ui.text(item.title,22,true));ui.gap(content,16);
TextView date=ui.text(date(item)+" · "+(item.read?"已读":"未读"),12,false);date.setTextColor(SECONDARY);content.addView(date);ui.gap(content,16);
TextView date=ui.text(date(item)+" · "+(item.read?L10n.tr("已读"):L10n.tr("未读")),12,false);date.setTextColor(SECONDARY);content.addView(date);ui.gap(content,16);
if(inbox.readSaveFailed){
content.addView(message("无法保存本地阅读状态,请重试。",RED));
content.addView(ui.action("重试保存阅读状态",BLUE,()->inbox.markRead(item)));ui.gap(content,16);
content.addView(message(L10n.tr("无法保存本地阅读状态,请重试。"),RED));
content.addView(ui.action(L10n.tr("重试保存阅读状态"),BLUE,()->inbox.markRead(item)));ui.gap(content,16);
}
TextView body=ui.text("",17,false);body.setText(AnnouncementText.format(item.body));body.setTextIsSelectable(true);
body.setMovementMethod(LinkMovementMethod.getInstance());body.setLinksClickable(true);body.setLinkTextColor(BLUE);body.setLineSpacing(ui.dp(4),1);
@@ -127,7 +127,7 @@ final class AnnouncementView extends ScrollView {
trackPull(event);
if(!pulling)return super.onTouchEvent(event);
boolean reached=event.getY()-downY>=ui.dp(72);
if(event.getActionMasked()==MotionEvent.ACTION_MOVE&&countLabel!=null)countLabel.setText(reached?"松开刷新":"下拉刷新");
if(event.getActionMasked()==MotionEvent.ACTION_MOVE&&countLabel!=null)countLabel.setText(reached?L10n.tr("松开刷新"):L10n.tr("下拉刷新"));
if(event.getActionMasked()==MotionEvent.ACTION_UP||event.getActionMasked()==MotionEvent.ACTION_CANCEL){
pulling=false;if(reached&&event.getActionMasked()==MotionEvent.ACTION_UP)inbox.refresh();else render();
}
@@ -142,7 +142,7 @@ final class AnnouncementView extends ScrollView {
private boolean dragging;
SwipeRow(View front,Runnable delete){
super(ui.context);this.front=front;setClipChildren(true);
remove=ui.action("删除",Color.WHITE,delete);remove.setBackgroundColor(RED);
remove=ui.action(L10n.tr("删除"),Color.WHITE,delete);remove.setBackgroundColor(RED);
remove.setVisibility(INVISIBLE);
remove.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);remove.setFocusable(false);
addView(remove,new FrameLayout.LayoutParams(ui.dp(76),-1,Gravity.END));addView(front,new FrameLayout.LayoutParams(-1,-2));
@@ -46,7 +46,7 @@ final class BackgroundSession implements NativeSession.Listener {
if (listener != null) listener.ended(reason);
}
@Override public void ended(String reason) { finish(reason); }
@Override public void passwordRejected() { finish("远端访问密码已失效,请重新连接"); }
@Override public void passwordRejected() { finish(L10n.tr("远端访问密码已失效,请重新连接")); }
@Override public void connected() { /* Already connected before the service takes ownership. */ }
@Override public void status(String message) { if (view != null) view.status(message); }
@Override public void signaling(int state, String id) {
@@ -75,8 +75,8 @@ final class BackgroundSession implements NativeSession.Listener {
@Override public void statistics(RemoteNetworkStatistics.Snapshot value) { if (view != null) view.statistics(value); }
@Override public void fileTransfer(String name, double progress, boolean sending, File received) {
if (received != null) receivedFile = received;
fileStatus = progress < 0 ? name + " 传输失败" : (sending ? "发送 " : "接收 ") + name
+ (progress >= 1 ? " · 已完成" : " · " + (int)(progress * 100) + "%");
fileStatus = progress < 0 ? L10n.format("{0} 传输失败", name) : (sending ? L10n.tr("发送 ") : L10n.tr("接收 ")) + name
+ (progress >= 1 ? L10n.tr(" · 已完成") : " · " + (int)(progress * 100) + "%");
if (view != null) view.fileTransfer(name, progress, sending, received);
}
@Override public void controlFeedback(String text) { if (view != null) view.controlFeedback(text); }
@@ -0,0 +1,11 @@
package cn.crossdesk.mobile;
import android.app.Application;
/** Initialize localization before any activity, background service or model. */
public final class CrossDeskApplication extends Application {
@Override public void onCreate() {
super.onCreate();
L10n.initialize(this);
}
}
@@ -0,0 +1,33 @@
package cn.crossdesk.mobile;
import android.content.Context;
import java.util.Locale;
/** Native resource lookup; never translates remote content, file names or protocol data. */
final class L10n {
private static volatile Context application;
static void initialize(Context context) { application = context.getApplicationContext(); }
static String tr(String key) {
Context context = application;
Integer id = LocalizationKeys.IDS.get(key);
return context == null || id == null ? key : context.getString(id);
}
static String format(String key, Object... args) {
java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("\\{(\\d+)\\}").matcher(tr(key));
StringBuffer result = new StringBuffer();
while (matcher.find()) {
int index = Integer.parseInt(matcher.group(1));
String value = index < args.length ? String.valueOf(args[index]) : matcher.group();
matcher.appendReplacement(result, java.util.regex.Matcher.quoteReplacement(value));
}
matcher.appendTail(result);
return result.toString();
}
static boolean isChinese() {
Context context = application;
Locale locale = context == null ? Locale.getDefault()
: context.getResources().getConfiguration().getLocales().get(0);
return locale.getLanguage().equals("zh");
}
private L10n() {}
}
@@ -28,9 +28,9 @@ final class LicenseCatalog {
if(documents.isEmpty())throw new JSONException("Missing documents");
}
String documentTitle(Document document){
if(document.title.equals("Source copyright and license notices"))return "版权与许可声明";
if(document.title.equals("apps/android/licenses/OPEN_SOURCE_NOTICE.txt"))return "开源软件权利说明";
if(id.equals("crossdesk")&&document.title.equals("LICENSE"))return "GNU GPL 第 3 版";
if(document.title.equals("Source copyright and license notices"))return L10n.tr("版权与许可声明");
if(document.title.equals("apps/android/licenses/OPEN_SOURCE_NOTICE.txt"))return L10n.tr("开源软件权利说明");
if(id.equals("crossdesk")&&document.title.equals("LICENSE"))return L10n.tr("GNU GPL 第 3 版");
return document.title;
}
}
@@ -0,0 +1,316 @@
package cn.crossdesk.mobile;
import java.util.HashMap;
import java.util.Map;
/** Generated resource keys; text and locale selection live in Android resources. */
final class LocalizationKeys {
static final Map<String, Integer> IDS = new HashMap<>();
static {
IDS.put(" · 已完成", R.string.text_58b6df1059088699);
IDS.put(" 传输失败", R.string.text_53af552fb3af7f6e);
IDS.put(" 台设备", R.string.text_23ebae95d26dfe5e);
IDS.put(" 条 · ", R.string.text_a7f4dfd234a43b16);
IDS.put(" 条未读", R.string.text_2c894057fc526576);
IDS.put("CrossDesk 远程连接", R.string.text_d0ac753c6681e578);
IDS.put("GNU GPL 第 3 版", R.string.text_0991c6fc1de4ffd6);
IDS.put("OpenFEC 告知", R.string.text_5ae7dc4079223967);
IDS.put("OpenFEC 许可与源码", R.string.text_69ad29820a259526);
IDS.put("P2P 直连", R.string.text_63ae4a0c7a448d21);
IDS.put("SRTP 已启用", R.string.text_70cae3c6aad8e152);
IDS.put("TURN 中继", R.string.text_ef813fec7d6d8a3b);
IDS.put("{0} 传输失败", R.string.text_ebede975edd52aee);
IDS.put("{0} 台设备", R.string.text_b3ffb6e982ecf5df);
IDS.put("{0} 条未读", R.string.text_fc66a78b9e27fecf);
IDS.put("{0},接收 {1},发送 {2},丢包率 {3}", R.string.text_dcb59cdcf66150a1);
IDS.put("● 已连接", R.string.text_01965b3249458d01);
IDS.put("下拉刷新", R.string.text_eb4236fe6a926a5a);
IDS.put("丢包率", R.string.text_37772187ab854191);
IDS.put("中", R.string.text_a567bdaa11367f26);
IDS.put("优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。", R.string.text_e6ff95a9fe6acc7f);
IDS.put("低", R.string.text_aa9e366f68d3d097);
IDS.put("使用自定义服务器", R.string.text_f03d03eb35e3330c);
IDS.put("保存密码", R.string.text_2e9f4819e064ce12);
IDS.put("保存收到的文件", R.string.text_10019cef593819ed);
IDS.put("保存远程画面预览", R.string.text_3bcdcd330bb3044c);
IDS.put("保存远程画面预览?", R.string.text_0f197074373e38fd);
IDS.put("信令连接中断,请重新连接", R.string.text_71ca53f95a8484da);
IDS.put("公告加载失败,请重试。", R.string.text_789f22a8b1b34797);
IDS.put("公告详情", R.string.text_00554b17c432937e);
IDS.put("共 ", R.string.text_2428420e407fe2e8);
IDS.put("共 {0} 条 · {1} 条未读", R.string.text_d977c7568cc5201e);
IDS.put("关于", R.string.text_52d25a9e30ba94f1);
IDS.put("关于 CrossDesk", R.string.text_459c1e980c1c8ce2);
IDS.put("关于,有新版本", R.string.text_390a95b65eb33052);
IDS.put("关闭", R.string.text_3fd47edce45b3603);
IDS.put("关闭控制栏", R.string.text_56a18c93e12fdc4e);
IDS.put("关闭隐私屏", R.string.text_4eb0565cc4658d7f);
IDS.put("兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。", R.string.text_7d466b69be42fc43);
IDS.put("分辨率", R.string.text_b92421822c213c2b);
IDS.put("删除", R.string.text_2f9daa828907b93f);
IDS.put("删除公告", R.string.text_0e46cdb3442edfba);
IDS.put("删除记录", R.string.text_f2cf9101bb2d8816);
IDS.put("删除这条公告?", R.string.text_d087671ca7df7f0f);
IDS.put("刷新公告", R.string.text_33e7584cd5c252d7);
IDS.put("剪贴板", R.string.text_8c10e2c1728ddf8e);
IDS.put("加载中…", R.string.text_4927a53bcc886afb);
IDS.put("加载更多", R.string.text_af90a08fec8ee28d);
IDS.put("升级提示", R.string.text_a739f24a9fd8a5d9);
IDS.put("原生连接初始化失败", R.string.text_861521c9e107c648);
IDS.put("发送", R.string.text_edecf0ae6e5144f9);
IDS.put("发送 ", R.string.text_43b7b60867ca51e7);
IDS.put("发送后,在远程键盘中点击“粘贴”。接收的远端文本可手动复制到本机。", R.string.text_da7df5a5543c4ba1);
IDS.put("发送文件", R.string.text_e7281fbca7af369c);
IDS.put("发送文本", R.string.text_ce6c02d19022e016);
IDS.put("取消", R.string.text_2cd0f3be8738a86c);
IDS.put("取消连接", R.string.text_3687f953cfe85fb8);
IDS.put("各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。", R.string.text_72814b15a6fa152c);
IDS.put("合计", R.string.text_e009c148d1890dac);
IDS.put("同意并继续", R.string.text_81c92013219dd115);
IDS.put("后台保持连接,点按返回远程桌面", R.string.text_409696774c9998ce);
IDS.put("后台保活未能启动,请保持应用在前台", R.string.text_dcb8d4e2e6973912);
IDS.put("后台保活未能启动,请保持应用在前台后重新连接", R.string.text_d96a21463557171d);
IDS.put("后台连接已结束,请重新连接", R.string.text_952f7300de0a7bd9);
IDS.put("后台连接服务已停止,请重新连接", R.string.text_da904f715e1cff87);
IDS.put("后续连接仍会保存新的预览图。", R.string.text_0812359e18082071);
IDS.put("在线", R.string.text_b9086662b1df1f20);
IDS.put("填写主机名或 IPv4 地址,不含协议和路径", R.string.text_39f2f444a042643e);
IDS.put("声音", R.string.text_473400815c0f687d);
IDS.put("复制远端文本", R.string.text_ab5ca1fab212f4ca);
IDS.put("始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。", R.string.text_c92269b2039e7aa3);
IDS.put("媒体加密", R.string.text_7ff73de85ec16d15);
IDS.put("字母数字", R.string.text_22dc6217af77bc37);
IDS.put("安全存储或证书初始化失败,请重试或清除应用数据", R.string.text_9ca1cdaf831182e3);
IDS.put("完成", R.string.text_c0b3fbff51ccc40b);
IDS.put("完成隐私授权后可检查更新", R.string.text_678328f6f02881c7);
IDS.put("完成隐私授权后登记本机身份", R.string.text_c4361f9abdba4882);
IDS.put("密码保存失败,本次连接仍可使用", R.string.text_c29b888e357d727d);
IDS.put("密码将加密保存在本机 Android Keystore 中", R.string.text_3f31300a23afb390);
IDS.put("对端 ID", R.string.text_8d3b052c4c67acda);
IDS.put("对端 ID ", R.string.text_0512084e130984bc);
IDS.put("对端 ID {0}", R.string.text_c5147d65960196b0);
IDS.put("将删除本机保存的所有远程预览图,保留连接记录和密码。", R.string.text_882b705d31ac7e2a);
IDS.put("屏幕方向", R.string.text_60cc99cc0013972e);
IDS.put("展开远程控制菜单", R.string.text_9e7294d264afecb7);
IDS.put("已从通知断开远程连接", R.string.text_9343612ee473edd6);
IDS.put("已保存密码", R.string.text_9d32476fe83aef60);
IDS.put("已保存密码无法读取,请重新输入", R.string.text_1cbd391383283924);
IDS.put("已保存的密码未能保留,请重新输入一次", R.string.text_6792b9f341380c68);
IDS.put("已发送 Ctrl+Alt+Del", R.string.text_74d3f0927d465047);
IDS.put("已启用后台保活,切换应用可继续保持连接", R.string.text_c28d86acbedba3b6);
IDS.put("已启用后台保活,可从连接通知返回或断开", R.string.text_506409ec08cb2a79);
IDS.put("已授权", R.string.text_521e65ffc7d0ff2d);
IDS.put("已读", R.string.text_efdd1a47e4beb2f9);
IDS.put("已连接 ", R.string.text_1f672b9b18d18e87);
IDS.put("已连接服务器", R.string.text_9c05d9d7fa1b0abd);
IDS.put("帧率", R.string.text_f9404bf335bc14cf);
IDS.put("帧率优先", R.string.text_9ae0c69e26db1d6a);
IDS.put("平衡", R.string.text_df544e46aa4d5c17);
IDS.put("应用中的许可文件缺失或损坏,请重新安装应用。", R.string.text_3362e8fa7f65cc2f);
IDS.put("开发版本包含尚未发布的修改,以下链接为基础版本源码。", R.string.text_3f2904435942cc0f);
IDS.put("开启保存", R.string.text_4338afaad52ea445);
IDS.put("开启隐私屏", R.string.text_c8bb8255390df3b6);
IDS.put("开源组件", R.string.text_c6ecaca362ae1c80);
IDS.put("开源软件权利说明", R.string.text_6099db946bdb5ee4);
IDS.put("当前已是最新版本", R.string.text_6e86570183c3cdd0);
IDS.put("当前远端不支持直接输入此文本,请使用支持文本输入的被控端版本,或通过剪贴板发送", R.string.text_24a52788cf474bf6);
IDS.put("成功连接后,这里会显示最近的远程设备。", R.string.text_33f25b500697a7d9);
IDS.put("我已阅读并同意《隐私政策》", R.string.text_70341256d7317e7d);
IDS.put("所选文件(无法读取或空间不足)", R.string.text_85c4c8fda0461f35);
IDS.put("找不到该设备 ID", R.string.text_9b58c9ff5f6a856f);
IDS.put("指令发送失败,请重试", R.string.text_97b7b18df07b9787);
IDS.put("指令已发送,等待电脑确认…", R.string.text_cb782027ca5986c7);
IDS.put("授权状态", R.string.text_3ca40a586103f6b7);
IDS.put("接收", R.string.text_6e684586884ebee6);
IDS.put("接收 ", R.string.text_84ae92e54e3078aa);
IDS.put("控制端解码器", R.string.text_d16488b987723161);
IDS.put("撤回", R.string.text_6b58198955259066);
IDS.put("撤回后将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。", R.string.text_c52e42ee856825db);
IDS.put("撤回联网授权", R.string.text_61f4788032ec596b);
IDS.put("撤回联网授权?", R.string.text_e736e419008ac10a);
IDS.put("收起远程控制菜单", R.string.text_73431dcf0afb7a65);
IDS.put("收起键盘", R.string.text_d826c4ece4dd4031);
IDS.put("收起鼠标", R.string.text_6e9a457c0c3c34ed);
IDS.put("数据", R.string.text_5440f7424f2865bf);
IDS.put("文件保存失败,请重试", R.string.text_94244f41d0a41378);
IDS.put("文件已保存", R.string.text_2553a785fef2a405);
IDS.put("文件选择已超时,会话已结束", R.string.text_d573b3f9a810ea04);
IDS.put("文本超过 128 KB", R.string.text_985a5c380be49f4f);
IDS.put("断开", R.string.text_f33ac04eece6a6a6);
IDS.put("断开后将返回首页", R.string.text_09b1395f50589327);
IDS.put("断开远程连接?", R.string.text_a73c0680db0d2e06);
IDS.put("断开连接", R.string.text_a33f3804768e68ee);
IDS.put("新版本", R.string.text_47ceaf502c5a05d6);
IDS.put("新版本可用:v", R.string.text_78823e6cf1a8afe5);
IDS.put("新版本可用:v{0}", R.string.text_b7e58b67fa8f7e78);
IDS.put("无未读公告", R.string.text_ef23787ec32c5588);
IDS.put("无法保存本地阅读状态,请重试。", R.string.text_dca2c418ac967366);
IDS.put("无法删除这条公告,请重试。", R.string.text_5c92da76360056cc);
IDS.put("无法发起远程连接", R.string.text_615709dd3129313c);
IDS.put("无法读取开源许可", R.string.text_a5c4f4ad79a640ae);
IDS.put("显示器", R.string.text_1a13ea5910a0c5e6);
IDS.put("显示密码", R.string.text_ffe21a671190323e);
IDS.put("暂不同意", R.string.text_a94460e8a04f04c5);
IDS.put("暂无公告", R.string.text_6d9a7758610a2432);
IDS.put("最近连接", R.string.text_0b290c6600d92f43);
IDS.put("服务器", R.string.text_2388cbc6a486af9f);
IDS.put("服务器 TLS 证书校验失败", R.string.text_dfe408e686175095);
IDS.put("服务器地址", R.string.text_d3716cc5a2f5a810);
IDS.put("服务器登录失败,请检查服务器地址和网络", R.string.text_2a9608d4465d0f70);
IDS.put("服务器端口", R.string.text_d270868960e68629);
IDS.put("服务器连接超时,请检查服务器地址和网络", R.string.text_7a4e4a6a1b479a52);
IDS.put("未启用", R.string.text_f95ea7f4c063eea7);
IDS.put("未找到可打开链接的浏览器", R.string.text_c8f7ed6f2186fb98);
IDS.put("未授权", R.string.text_94bc3d40defef3e9);
IDS.put("未读", R.string.text_4d0680f9efaef147);
IDS.put("未连接服务器 · 点击重试", R.string.text_d1fcb66a323a16f5);
IDS.put("本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。", R.string.text_7eea7966c0b746c5);
IDS.put("本机 ID ", R.string.text_c611b4e50b0dbc08);
IDS.put("本机 ID {0}", R.string.text_97d10220f08dd7fc);
IDS.put("本版本源码与构建说明", R.string.text_91f15d505770d7a0);
IDS.put("本版本源码信息暂不可用。", R.string.text_5b2c8ca2c5a87241);
IDS.put("松开刷新", R.string.text_f0e274aa130e08a2);
IDS.put("构建说明", R.string.text_47077144715b7074);
IDS.put("构建配方与项目补丁", R.string.text_837ab2d0b2f4cb98);
IDS.put("查看源码", R.string.text_921c3b62f1f7d6f4);
IDS.put("检查失败,请稍后重试", R.string.text_fdd1059f10c4b979);
IDS.put("检查更新", R.string.text_7f68ebad19ba6bcd);
IDS.put("正在关闭隐私屏…", R.string.text_4f4d1e704e38c161);
IDS.put("正在建立安全连接…", R.string.text_2a05f6fbd3f4436e);
IDS.put("正在开启隐私屏…", R.string.text_0da575e3c92de73a);
IDS.put("正在检查更新…", R.string.text_91834aebecabcbc3);
IDS.put("正在清除…", R.string.text_0571a5f5f8ec4c83);
IDS.put("正在获取本机 ID…", R.string.text_0e6336c8fe918730);
IDS.put("正在读取所选文件…", R.string.text_faebd20c2801fd0f);
IDS.put("正在连接", R.string.text_694186911ae3e4da);
IDS.put("正在连接信令服务器…", R.string.text_c3ccc7f7d37533e3);
IDS.put("正在连接服务器…", R.string.text_4024d3c0f467a4f6);
IDS.put("正在重连服务器…", R.string.text_46fe1ef52ec3a10f);
IDS.put("此设备没有可用的文件选择器", R.string.text_4e4fce649fc00238);
IDS.put("清除", R.string.text_bce2377283c2455a);
IDS.put("清除预览图", R.string.text_ee0e76b8507212de);
IDS.put("清除预览图?", R.string.text_ecc809e44637de18);
IDS.put("源码与构建说明", R.string.text_2782e4485ad23d34);
IDS.put("源码版本", R.string.text_1f566fe8f8b718f3);
IDS.put("版本", R.string.text_5f76b2bf82dd2c5d);
IDS.put("版权与许可声明", R.string.text_16410ee9663fad10);
IDS.put("电脑键盘", R.string.text_11de77e1e41e680f);
IDS.put("画质优先", R.string.text_20f57900234debd7);
IDS.put("画面偏好", R.string.text_43b04aba53a54cf5);
IDS.put("画面延时", R.string.text_82a5600c470179fa);
IDS.put("画面设置", R.string.text_33b458faaf967e15);
IDS.put("画面质量", R.string.text_afb5acdf8f07fadb);
IDS.put("画面采集帧率", R.string.text_12b41b7a3fbcf8ca);
IDS.put("相对位置", R.string.text_66062971234f422d);
IDS.put("相对鼠标", R.string.text_c421dc051bea3f11);
IDS.put("知道了", R.string.text_de32e20193ad1042);
IDS.put("确定", R.string.text_fac2a67ad87807c4);
IDS.put("确认", R.string.text_36f33adaf0942634);
IDS.put("确认连接", R.string.text_a5b7fba4dbd8f622);
IDS.put("离线", R.string.text_be1b4f3c6c1cda1e);
IDS.put("空格", R.string.text_bfa4a6e0fe42ac28);
IDS.put("端口范围为 1–65535", R.string.text_471a579e8d87faac);
IDS.put("符号功能", R.string.text_c7d0c77862e32b52);
IDS.put("等待电脑返回隐私屏状态;旧版电脑端可能不支持", R.string.text_a562f07bd38c1a49);
IDS.put("等待远端屏幕信息", R.string.text_5ee880e2524a983a);
IDS.put("等待远端画面…", R.string.text_674c723a85ee7f57);
IDS.put("等待隐私授权", R.string.text_119f0483da4756c4);
IDS.put("系统键盘", R.string.text_7916071c1a8ee458);
IDS.put("绝对位置", R.string.text_02e9ba6fda719e2e);
IDS.put("绝对鼠标", R.string.text_8527c8c6d1ccc1ca);
IDS.put("网络状态", R.string.text_872428a7210d5e0c);
IDS.put("自定义", R.string.text_4eafa9e925b30bcd);
IDS.put("虚拟鼠标", R.string.text_dffd0c12b8f27247);
IDS.put("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。", R.string.text_62c199f37bf95869);
IDS.put("被控端版本过低,请升级到最新版本。", R.string.text_a569ce729c1d6e19);
IDS.put("被控端编码器", R.string.text_d814476826827b78);
IDS.put("要发送到远端的文本", R.string.text_488402883e27f6f0);
IDS.put("视频", R.string.text_c20f7618d330a854);
IDS.put("许可", R.string.text_310c625425f014d8);
IDS.put("许可与版权声明", R.string.text_5c34f7d0e2124dca);
IDS.put("许可与版权声明可离线查阅。", R.string.text_e24134b643a17975);
IDS.put("设备身份保存失败,请检查安全存储", R.string.text_5e7628f043ce71d0);
IDS.put("设备身份及选择保存的访问密码使用 Android Keystore 加密。连接记录仅保存在本机,不纳入设备备份。剪贴板内容在结束会话时清除。离开应用后通过画中画或连接通知保持会话,可在应用或通知中断开。", R.string.text_5d35ee0d7945fcbc);
IDS.put("设置", R.string.text_df3d58c7d84b85f2);
IDS.put("设置,有新版本", R.string.text_4f8771952222a011);
IDS.put("访问密码", R.string.text_f4f712d5be91721a);
IDS.put("访问密码错误,请重新输入", R.string.text_dd6c465847af1b60);
IDS.put("证书校验失败 · 点击重试", R.string.text_defba1d2989bfab4);
IDS.put("请先完成联网授权", R.string.text_44f3d4d52347095a);
IDS.put("请在远端启用 CrossDesk 系统服务后重试", R.string.text_2a07ca002bc5093d);
IDS.put("请求超时,尚未确认电脑的隐私屏状态", R.string.text_990fd23c62de5ccf);
IDS.put("请等待当前文件发送完成", R.string.text_e5c8fa6dcf7b2a8b);
IDS.put("请输入 1–65535 范围内的端口", R.string.text_9305d9d72b06110d);
IDS.put("请输入 9 位设备 ID", R.string.text_3c7223dfb8dde5cf);
IDS.put("请输入有效访问密码", R.string.text_285a0714894d2899);
IDS.put("调整失败,请重试。", R.string.text_c808bb77ffd2b4cd);
IDS.put("软件许可", R.string.text_a762e7e650e285fa);
IDS.put("返回", R.string.text_572cf45ba43634b3);
IDS.put("返回控制栏", R.string.text_7565f94031582c7c);
IDS.put("还没有连接记录", R.string.text_55113b6925c362fd);
IDS.put("这条公告已更新或删除,请返回公告列表查看。", R.string.text_3aa527b2106cc987);
IDS.put("远程任务已关闭", R.string.text_e357aa9a0fb28c16);
IDS.put("远程桌面", R.string.text_c255841f68096d3b);
IDS.put("远程桌面触控区域", R.string.text_133f4c7dabd88b97);
IDS.put("远程桌面:轻触点击,拖动移动,长按右键,双指捏合缩放,放大后双指拖动画面,原始比例下双指上下滑动滚动", R.string.text_5931aeb3778f28f1);
IDS.put("远程电脑", R.string.text_3535087994d050de);
IDS.put("远程窗口已关闭,或后台保活未能启动,会话已结束", R.string.text_ed2e03072ce131de);
IDS.put("远程连接", R.string.text_678b783fb578172b);
IDS.put("远端密码错误", R.string.text_deccbd3eb9ba45f2);
IDS.put("远端已关闭连接", R.string.text_a2ce499295e358ae);
IDS.put("远端当前不可用", R.string.text_176e117df3ace734);
IDS.put("远端访问密码已失效,请重新连接", R.string.text_d2c0694243f93249);
IDS.put("连接", R.string.text_a5574109f0208e89);
IDS.put("连接 ", R.string.text_1d54bfbae5f8ac08);
IDS.put("连接失败", R.string.text_89c4766afc85125f);
IDS.put("连接失败 · 点击重试", R.string.text_04bf1a61ba1369f6);
IDS.put("连接已断开", R.string.text_2ab7d89557e779ab);
IDS.put("连接已结束", R.string.text_c1d20bf4c3e471c1);
IDS.put("连接延时(RTT)", R.string.text_e57d2cbc0e9908e6);
IDS.put("连接建立后可调整,需被控端支持。", R.string.text_db4d4f0fab78498a);
IDS.put("连接建立后锁定横屏", R.string.text_2123a9ed718f2c40);
IDS.put("连接提示", R.string.text_f741e0536210eafa);
IDS.put("连接方式", R.string.text_485a26050ce57431);
IDS.put("连接服务器", R.string.text_5a048351a77fe88d);
IDS.put("连接服务器后可获取最新公告。", R.string.text_5854c4add7ab0f7a);
IDS.put("连接超时,请检查设备 ID、服务器及远端在线状态", R.string.text_32cd27b5a72515f3);
IDS.put("连接远程桌面", R.string.text_6edc4b54cc1a9838);
IDS.put("退格", R.string.text_d5407b4f3c666681);
IDS.put("通知公告", R.string.text_e02926784fcedb91);
IDS.put("部分预览图未能清除,请重试。", R.string.text_465a092680d132b3);
IDS.put("重试", R.string.text_b8784c8dd5636ff2);
IDS.put("重试保存阅读状态", R.string.text_a504fb6f162bdb20);
IDS.put("锁定", R.string.text_ff9872a023e3a8aa);
IDS.put("键盘", R.string.text_864200bbb241e067);
IDS.put("阅读并授权", R.string.text_1f0f014d4177a0cc);
IDS.put("隐私", R.string.text_86651d17a401c55b);
IDS.put("隐私与授权", R.string.text_26c6e011e343708c);
IDS.put("隐私屏", R.string.text_6cad42fd3da85585);
IDS.put("隐私屏已关闭", R.string.text_e157fafb76aad954);
IDS.put("隐私屏已开启", R.string.text_bbef0eac0c04f214);
IDS.put("隐私屏已被解锁", R.string.text_0c7535f7c5e43b6f);
IDS.put("隐私屏异常", R.string.text_b62ac3dfb606c00b);
IDS.put("隐私屏暂不可用", R.string.text_598a2747dbd21dd5);
IDS.put("隐私屏状态已过期,等待电脑确认", R.string.text_4d59be3c75189032);
IDS.put("隐私屏状态待确认", R.string.text_1674532c70ed1db2);
IDS.put("隐私政策", R.string.text_e464b41fd5c45ba5);
IDS.put("隐私政策暂时无法读取,请拒绝联网并重新安装应用。", R.string.text_554e957e8e3c00af);
IDS.put("静音", R.string.text_7db2e189ecb01065);
IDS.put("音频", R.string.text_296c632ec857a0ba);
IDS.put("项目主页", R.string.text_0cadb49096a382e0);
IDS.put("预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。", R.string.text_3beca60bd0c654f7);
IDS.put("高", R.string.text_b1c27820fec23edb);
IDS.put("默认", R.string.text_844b8cc8dff7c1d8);
IDS.put("默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。", R.string.text_4db8737054f3b0cc);
IDS.put("鼠标右键,按住并拖动,松开结束拖拽", R.string.text_1184a97edd8e1646);
IDS.put("鼠标左键,按住并拖动,松开结束拖拽", R.string.text_926eaa3af0b5e48e);
IDS.put("鼠标控制", R.string.text_269f39a07b4a4e01);
IDS.put("鼠标滚轮,上下滑动,可滑出按键范围", R.string.text_cba62aeeb4a63a23);
IDS.put(",丢包率 ", R.string.text_9aa238fc6121dede);
IDS.put(",发送 ", R.string.text_0b5d16c21095fe10);
IDS.put(",已保存密码", R.string.text_ba9a792d063a8c86);
IDS.put(",接收 ", R.string.text_f4c1f9d7896760a7);
IDS.put(",点击重试", R.string.text_42237b5ec70c26f3);
}
private LocalizationKeys() {}
}
@@ -89,7 +89,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
private static final int PICK_FILE=41,SAVE_FILE=42;
private NativeSession documentSession;
private java.io.File receivedFile,documentToSave;
private final Runnable documentTimeout=()->{if(documentSession!=null&&!foreground)end("文件选择已超时,会话已结束");};
private final Runnable documentTimeout=()->{if(documentSession!=null&&!foreground)end(L10n.tr("文件选择已超时,会话已结束"));};
private RemoteVideoSettings videoSettings=new RemoteVideoSettings(1);
private final android.os.Handler mainHandler=new android.os.Handler(android.os.Looper.getMainLooper());
private Runnable videoSettingsTimeout;
@@ -158,7 +158,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
}
private LinearLayout navigation(String title,Runnable back){
LinearLayout bar=row();bar.setPadding(dp(12),0,dp(12),0);root.addView(bar,ui.size(-1,56));
bar.addView(ui.iconButton("back","返回",this::navigateBack),ui.size(44,44));
bar.addView(ui.iconButton("back",L10n.tr("返回"),this::navigateBack),ui.size(44,44));
TextView label=text(title,17,true);label.setGravity(Gravity.CENTER);label.setSingleLine();label.setEllipsize(TextUtils.TruncateAt.END);bar.addView(label,new LinearLayout.LayoutParams(0,-1,1));
bar.addView(new View(this),ui.size(44,44));backAction=back;return bar;
}
@@ -200,34 +200,34 @@ public final class MainActivity extends Activity implements NativeSession.Listen
signalBadge=text("",12,true);signalBadge.setGravity(Gravity.CENTER);signalBadge.setPadding(dp(11),0,dp(11),0);signalBadge.setSingleLine(true);
signalBadge.setOnClickListener(v->{
if(!preferences.getBoolean("networkConsent",false))consent(this::renderSignaling);
else if(signalState==1)toast("已连接 "+serverKey());
else if(signalState==1)toast(L10n.tr("已连接 ")+serverKey());
else {stopSignaling();ensureSignaling();}
});renderSignaling();toolbar.addView(signalBadge,ui.size(-2,34));toolbar.addView(new View(this),new LinearLayout.LayoutParams(0,1,1));
FrameLayout announcementButton=(FrameLayout)ui.iconButton("envelope","通知公告",this::announcements);announcementButton.setClipChildren(false);toolbar.setClipChildren(false);toolbar.setClipToPadding(false);
FrameLayout announcementButton=(FrameLayout)ui.iconButton("envelope",L10n.tr("通知公告"),this::announcements);announcementButton.setClipChildren(false);toolbar.setClipChildren(false);toolbar.setClipToPadding(false);
announcementBadge=text("",10,true);announcementBadge.setTextColor(Color.WHITE);announcementBadge.setGravity(Gravity.CENTER);announcementBadge.setMinWidth(dp(16));announcementBadge.setPadding(dp(4),0,dp(4),0);announcementBadge.setBackground(ui.background(RED,12));announcementBadge.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
FrameLayout.LayoutParams badgePosition=new FrameLayout.LayoutParams(-2,dp(16),Gravity.TOP|Gravity.END);badgePosition.topMargin=-dp(3);badgePosition.setMarginEnd(-dp(5));announcementButton.addView(announcementBadge,badgePosition);renderAnnouncements();
toolbar.addView(announcementButton,ui.size(40,40));View spacer=new View(this);toolbar.addView(spacer,ui.size(12,1));
FrameLayout settingsButton=(FrameLayout)ui.iconButton("settings","设置",this::settings);settingsButton.setClipChildren(false);settingsUpdateButton=settingsButton;
FrameLayout settingsButton=(FrameLayout)ui.iconButton("settings",L10n.tr("设置"),this::settings);settingsButton.setClipChildren(false);settingsUpdateButton=settingsButton;
TextView updateBadge=text("!",10,true);updateBadge.setTextColor(Color.WHITE);updateBadge.setGravity(Gravity.CENTER);updateBadge.setIncludeFontPadding(false);updateBadge.setBackground(ui.background(RED,8));updateBadge.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);settingsUpdateDot=updateBadge;
FrameLayout.LayoutParams updatePosition=new FrameLayout.LayoutParams(dp(16),dp(16),Gravity.TOP|Gravity.END);updatePosition.topMargin=-dp(3);updatePosition.setMarginEnd(-dp(5));settingsButton.addView(settingsUpdateDot,updatePosition);
toolbar.addView(settingsButton,ui.size(40,40));renderAppUpdates();
HomeLayout layout=new HomeLayout(ui);root.addView(layout,new LinearLayout.LayoutParams(-1,0,1));LinearLayout content=layout.panels;
LinearLayout connection=column();connection.setPadding(dp(18),dp(18),dp(18),dp(18));content.addView(ui.shadowCard(connection,20));
connection.addView(text("远程桌面",20,true));ui.gap(connection,16);
connection.addView(text(L10n.tr("远程桌面"),20,true));ui.gap(connection,16);
LinearLayout entry=row();connection.addView(entry,ui.size(-1,54));
remote=field("对端 ID",InputType.TYPE_CLASS_NUMBER);remote.setTextSize(20);remote.setTypeface(Typeface.create("sans-serif-medium",Typeface.NORMAL));
remote=field(L10n.tr("对端 ID"),InputType.TYPE_CLASS_NUMBER);remote.setTextSize(20);remote.setTypeface(Typeface.create("sans-serif-medium",Typeface.NORMAL));
remote.setOnFocusChangeListener((view,focused)->{GradientDrawable shape=ui.background(BACKGROUND,12);shape.setStroke(dp(focused?2:1),focused?BLUE:LINE);remote.setBackground(shape);});
remote.setFilters(new InputFilter[]{new InputFilter.LengthFilter(11)});remote.setText(formatId(preferences.getString("lastRemote","")));
entry.addView(remote,new LinearLayout.LayoutParams(0,-1,1));
TextView connect=ui.primary("连接",()->promptConnection(id()));connect.setContentDescription("连接");LinearLayout.LayoutParams buttonParams=ui.size(96,54);buttonParams.leftMargin=dp(10);entry.addView(connect,buttonParams);
TextView connect=ui.primary(L10n.tr("连接"),()->promptConnection(id()));connect.setContentDescription(L10n.tr("连接"));LinearLayout.LayoutParams buttonParams=ui.size(96,54);buttonParams.leftMargin=dp(10);entry.addView(connect,buttonParams);
Runnable update=()->{connect.setEnabled(!id().isEmpty());connect.setAlpha(id().isEmpty()?.45f:1);};update.run();
remote.addTextChangedListener(new TextWatcher(){boolean editing;public void beforeTextChanged(CharSequence s,int start,int count,int after){}public void onTextChanged(CharSequence s,int start,int before,int count){update.run();}public void afterTextChanged(Editable value){if(editing)return;String formatted=formatId(value.toString());if(!formatted.equals(value.toString())){editing=true;int cursor=remote.getSelectionStart(),digits=value.subSequence(0,Math.max(0,cursor)).toString().replaceAll("[^0-9]","").length();remote.setText(formatted);remote.setSelection(Math.min(formatted.length(),digits+Math.max(0,digits-1)/3));editing=false;}}});
LinearLayout recent=column();recent.setPadding(dp(16),dp(16),dp(16),dp(16));ui.card(recent,18);LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(-1,0,1);p.topMargin=dp(20);content.addView(recent,p);
JSONArray records=history.list(serverKey());recentPresence.watch(records,android.os.SystemClock.elapsedRealtime());LinearLayout heading=row();heading.addView(text("最近连接",20,true),new LinearLayout.LayoutParams(0,-2,1));
if(records.length()>0){TextView count=text(records.length()+" 台设备",12,false);count.setTextColor(SECONDARY);heading.addView(count);}recent.addView(heading);
JSONArray records=history.list(serverKey());recentPresence.watch(records,android.os.SystemClock.elapsedRealtime());LinearLayout heading=row();heading.addView(text(L10n.tr("最近连接"),20,true),new LinearLayout.LayoutParams(0,-2,1));
if(records.length()>0){TextView count=text(L10n.format("{0} 台设备",records.length()),12,false);count.setTextColor(SECONDARY);heading.addView(count);}recent.addView(heading);
if(records.length()==0){
LinearLayout empty=column();empty.setGravity(Gravity.CENTER);recent.addView(empty,new LinearLayout.LayoutParams(-1,0,1));empty.addView(ui.icon("history",0xFFB6B6BB),ui.size(34,34));ui.gap(empty,12);
TextView emptyTitle=text("还没有连接记录",17,true);emptyTitle.setGravity(Gravity.CENTER);empty.addView(emptyTitle);ui.gap(empty,8);TextView hint=text("成功连接后,这里会显示最近的远程设备。",13,false);hint.setTextColor(SECONDARY);hint.setGravity(Gravity.CENTER);empty.addView(hint);
TextView emptyTitle=text(L10n.tr("还没有连接记录"),17,true);emptyTitle.setGravity(Gravity.CENTER);empty.addView(emptyTitle);ui.gap(empty,8);TextView hint=text(L10n.tr("成功连接后,这里会显示最近的远程设备。"),13,false);hint.setTextColor(SECONDARY);hint.setGravity(Gravity.CENTER);empty.addView(hint);
}else{
ui.gap(recent,14);ScrollView scroll=new ScrollView(this);recent.addView(scroll,new LinearLayout.LayoutParams(-1,0,1));recentGrid=column();scroll.addView(recentGrid);
recentGrid.addOnLayoutChangeListener((view,left,top,right,bottom,oldLeft,oldTop,oldRight,oldBottom)->{
@@ -243,7 +243,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
private void showConnectionMessage(){
if(pendingConnectionMessage.isEmpty()||!foreground||isFinishing()||isDestroyed())return;
if(connectionDialog!=null)connectionDialog.dismiss();
connectionDialog=new AlertDialog.Builder(this).setTitle("连接提示").setMessage(pendingConnectionMessage).setPositiveButton("确定",null).create();
connectionDialog=new AlertDialog.Builder(this).setTitle(L10n.tr("连接提示")).setMessage(pendingConnectionMessage).setPositiveButton(L10n.tr("确定"),null).create();
connectionDialog.setOnDismissListener(dialog->connectionDialog=null);
pendingConnectionMessage="";connectionDialog.show();
}
@@ -259,27 +259,27 @@ public final class MainActivity extends Activity implements NativeSession.Listen
if(item==null)return new View(this);String id=item.optString("id"),platform=item.optString("platform");
LinearLayout card=column();ui.card(card,14);FrameLayout preview=new FrameLayout(this){@Override protected void onMeasure(int w,int h){super.onMeasure(w,MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(w)*9/16,MeasureSpec.EXACTLY));}};
int color=platform.equals("windows")?0xFF316AB7:platform.equals("macos")?0xFF8057B9:platform.equals("linux")?0xFFAE642B:0xFF677587;preview.setBackgroundColor(color);
TextView label=text(platform.equals("windows")?"Windows":platform.equals("macos")?"macOS":platform.equals("linux")?"Linux":"远程电脑",19,true);label.setGravity(Gravity.CENTER);label.setTextColor(Color.WHITE);preview.addView(label,new FrameLayout.LayoutParams(-1,-1));card.addView(preview,ui.size(-1,80));
TextView label=text(platform.equals("windows")?"Windows":platform.equals("macos")?"macOS":platform.equals("linux")?"Linux":L10n.tr("远程电脑"),19,true);label.setGravity(Gravity.CENTER);label.setTextColor(Color.WHITE);preview.addView(label,new FrameLayout.LayoutParams(-1,-1));card.addView(preview,ui.size(-1,80));
ImageView image=new ImageView(this);image.setScaleType(ImageView.ScaleType.CENTER_CROP);image.setVisibility(View.GONE);image.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);preview.addView(image,new FrameLayout.LayoutParams(-1,-1));
String server=serverKey();Runnable load=()->{if(!image.isAttachedToWindow())return;Object binding=new Object();image.setTag(binding);image.setImageDrawable(null);image.setVisibility(View.GONE);label.setVisibility(View.VISIBLE);
previews.load(server,id,bitmap->{if(image.getTag()!=binding||!image.isAttachedToWindow()){if(bitmap!=null)bitmap.recycle();return;}if(bitmap!=null){image.setImageBitmap(bitmap);image.setVisibility(View.VISIBLE);label.setVisibility(View.GONE);}});};previewCards.add(load);image.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener(){public void onViewAttachedToWindow(View view){load.run();}public void onViewDetachedFromWindow(View view){image.setTag(null);}});if(image.isAttachedToWindow())load.run();
LinearLayout caption=column();caption.setPadding(dp(9),dp(9),dp(9),dp(9));TextView name=text(item.optString("name",id),12,true);name.setSingleLine();name.setEllipsize(TextUtils.TruncateAt.END);caption.addView(name);ui.gap(caption,4);
LinearLayout details=row();TextView detail=text("ID "+id,10,false);detail.setTypeface(Typeface.create("sans-serif",Typeface.NORMAL));detail.setTextColor(SECONDARY);detail.setSingleLine();detail.setEllipsize(TextUtils.TruncateAt.END);details.addView(detail,new LinearLayout.LayoutParams(0,-2,1));
if(item.optBoolean("remember")){View key=ui.icon("key",SECONDARY);key.setContentDescription("已保存密码");LinearLayout.LayoutParams keySize=ui.size(10,10);keySize.leftMargin=dp(4);details.addView(key,keySize);}
boolean online=recentPresence.isOnline(id,android.os.SystemClock.elapsedRealtime());String availability=online?"在线":"离线";TextView presence=text(availability,10,true);presence.setSingleLine();presence.setTextColor(online?GREEN:SECONDARY);LinearLayout.LayoutParams presenceSize=ui.size(-2,-2);presenceSize.leftMargin=dp(5);details.addView(presence,presenceSize);caption.addView(details);card.addView(caption);
card.setContentDescription("连接 "+item.optString("name",id)+",ID "+id+(item.optBoolean("remember")?",已保存密码":"")+","+availability);card.setClickable(true);card.setFocusable(true);card.setOnClickListener(v->{remote.setText(formatId(id));promptConnection(id);});
card.setOnLongClickListener(v->{new AlertDialog.Builder(this).setTitle(item.optString("name",id)).setItems(new String[]{"连接","删除记录"},(d,w)->{if(w==0)promptConnection(id);else{history.delete(serverKey(),id);home("");}}).show();return true;});return card;
if(item.optBoolean("remember")){View key=ui.icon("key",SECONDARY);key.setContentDescription(L10n.tr("已保存密码"));LinearLayout.LayoutParams keySize=ui.size(10,10);keySize.leftMargin=dp(4);details.addView(key,keySize);}
boolean online=recentPresence.isOnline(id,android.os.SystemClock.elapsedRealtime());String availability=online?L10n.tr("在线"):L10n.tr("离线");TextView presence=text(availability,10,true);presence.setSingleLine();presence.setTextColor(online?GREEN:SECONDARY);LinearLayout.LayoutParams presenceSize=ui.size(-2,-2);presenceSize.leftMargin=dp(5);details.addView(presence,presenceSize);caption.addView(details);card.addView(caption);
card.setContentDescription(L10n.tr("连接 ")+item.optString("name",id)+",ID "+id+(item.optBoolean("remember")?L10n.tr(",已保存密码"):"")+","+availability);card.setClickable(true);card.setFocusable(true);card.setOnClickListener(v->{remote.setText(formatId(id));promptConnection(id);});
card.setOnLongClickListener(v->{new AlertDialog.Builder(this).setTitle(item.optString("name",id)).setItems(new String[]{L10n.tr("连接"),L10n.tr("删除记录")},(d,w)->{if(w==0)promptConnection(id);else{history.delete(serverKey(),id);home("");}}).show();return true;});return card;
}
private void promptConnection(String id){
hideKeyboard();if(!id.matches("[0-9]{9}")){remote.setError("请输入 9 位设备 ID");remote.requestFocus();return;}
hideKeyboard();if(!id.matches("[0-9]{9}")){remote.setError(L10n.tr("请输入 9 位设备 ID"));remote.requestFocus();return;}
if(!preferences.getBoolean("networkConsent",false)){consent(()->connectOrPrompt(id));return;}connectOrPrompt(id);
}
private void connectOrPrompt(String id){
String stored;
try{stored=history.password(serverKey(),id);}catch(Exception error){passwordSheet(id,"已保存密码无法读取,请重新输入",true);return;}
try{stored=history.password(serverKey(),id);}catch(Exception error){passwordSheet(id,L10n.tr("已保存密码无法读取,请重新输入"),true);return;}
if(!stored.isEmpty()){rememberPassword=true;begin(id,stored);return;}
boolean remember=history.remembersPassword(serverKey(),id);
passwordSheet(id,remember?"已保存的密码未能保留,请重新输入一次":"",remember);
passwordSheet(id,remember?L10n.tr("已保存的密码未能保留,请重新输入一次"):"",remember);
}
private void consent(Runnable accepted){
if(privacyDialog!=null&&privacyDialog.isShowing())return;
@@ -291,15 +291,15 @@ public final class MainActivity extends Activity implements NativeSession.Listen
private void passwordSheet(String id,String error,boolean rememberDefault){
Dialog dialog=new Dialog(this);passwordDialog=dialog;LinearLayout sheet=column();sheet.setPadding(dp(22),dp(10),dp(22),dp(22));sheet.setBackground(ui.background(BACKGROUND,24));
View handle=new View(this);handle.setBackground(ui.background(0xFFC6C6CB,3));LinearLayout.LayoutParams hp=ui.size(36,5);hp.gravity=Gravity.CENTER;sheet.addView(handle,hp);ui.gap(sheet,20);
LinearLayout header=row(),titles=column();titles.addView(text("连接远程桌面",22,true));ui.gap(titles,5);TextView subtitle=text("对端 ID "+id,15,false);subtitle.setTextColor(SECONDARY);titles.addView(subtitle);header.addView(titles,new LinearLayout.LayoutParams(0,-2,1));header.addView(ui.iconButton("close","关闭",dialog::dismiss),ui.size(36,36));sheet.addView(header);ui.gap(sheet,18);
LinearLayout passwordRow=row();ui.card(passwordRow,10);EditText password=field("访问密码",InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);password.setBackgroundColor(Color.TRANSPARENT);passwordRow.addView(password,new LinearLayout.LayoutParams(0,dp(50),1));
View eye=ui.iconButton("eye","显示密码",()->{boolean hidden=password.getTransformationMethod()!=null;password.setTransformationMethod(hidden?null:PasswordTransformationMethod.getInstance());password.setSelection(password.length());});passwordRow.addView(eye,ui.size(44,44));sheet.addView(passwordRow);ui.gap(sheet,18);
LinearLayout save=row(),saveText=column();saveText.addView(text("保存密码",15,true));ui.gap(saveText,3);TextView security=text("密码将加密保存在本机 Android Keystore 中",11,false);security.setTextColor(SECONDARY);saveText.addView(security);save.addView(saveText,new LinearLayout.LayoutParams(0,-2,1));Switch remember=new Switch(this);remember.setContentDescription("保存密码");save.addView(remember);sheet.addView(save);
LinearLayout header=row(),titles=column();titles.addView(text(L10n.tr("连接远程桌面"),22,true));ui.gap(titles,5);TextView subtitle=text(L10n.format("对端 ID {0}",id),15,false);subtitle.setTextColor(SECONDARY);titles.addView(subtitle);header.addView(titles,new LinearLayout.LayoutParams(0,-2,1));header.addView(ui.iconButton("close",L10n.tr("关闭"),dialog::dismiss),ui.size(36,36));sheet.addView(header);ui.gap(sheet,18);
LinearLayout passwordRow=row();ui.card(passwordRow,10);EditText password=field(L10n.tr("访问密码"),InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_PASSWORD);password.setBackgroundColor(Color.TRANSPARENT);passwordRow.addView(password,new LinearLayout.LayoutParams(0,dp(50),1));
View eye=ui.iconButton("eye",L10n.tr("显示密码"),()->{boolean hidden=password.getTransformationMethod()!=null;password.setTransformationMethod(hidden?null:PasswordTransformationMethod.getInstance());password.setSelection(password.length());});passwordRow.addView(eye,ui.size(44,44));sheet.addView(passwordRow);ui.gap(sheet,18);
LinearLayout save=row(),saveText=column();saveText.addView(text(L10n.tr("保存密码"),15,true));ui.gap(saveText,3);TextView security=text(L10n.tr("密码将加密保存在本机 Android Keystore 中"),11,false);security.setTextColor(SECONDARY);saveText.addView(security);save.addView(saveText,new LinearLayout.LayoutParams(0,-2,1));Switch remember=new Switch(this);remember.setContentDescription(L10n.tr("保存密码"));save.addView(remember);sheet.addView(save);
remember.setChecked(rememberDefault);
ui.gap(sheet,18);TextView connect=ui.primary("连接",()->{
String secret=password.getText().toString();if(secret.isEmpty()||secret.length()>128||secret.contains("@")){password.setError("请输入有效访问密码");return;}
ui.gap(sheet,18);TextView connect=ui.primary(L10n.tr("连接"),()->{
String secret=password.getText().toString();if(secret.isEmpty()||secret.length()>128||secret.contains("@")){password.setError(L10n.tr("请输入有效访问密码"));return;}
rememberPassword=remember.isChecked();password.setText("");dialog.dismiss();begin(id,secret);
});connect.setContentDescription("确认连接");sheet.addView(connect,ui.size(-1,48));
});connect.setContentDescription(L10n.tr("确认连接"));sheet.addView(connect,ui.size(-1,48));
ScrollView scroll=new ScrollView(this);scroll.addView(sheet);
dialog.setContentView(scroll);Window window=dialog.getWindow();if(window!=null){window.setBackgroundDrawableResource(android.R.color.transparent);window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);window.setDimAmount(.22f);}dialog.show();sizePasswordDialog();password.requestFocus();if(!error.isEmpty())password.setError(error);
dialog.setOnDismissListener(d->{password.setText("");passwordDialog=null;});
@@ -319,12 +319,12 @@ public final class MainActivity extends Activity implements NativeSession.Listen
previewCapture=previews.begin(serverKey(),id);previewCopying=false;previewAttempts=0;
connectedRemote=id;pendingPassword=rememberPassword?secret:"";ready=false;muted=false;selectedDisplay=0;hostPlatform="";remoteClipboard="";displays=new JSONArray();preferences.edit().putString("lastRemote",id).apply();
ensureSignaling();
if(signaling==null){pendingPassword="";toast("请先完成联网授权");return;}
if(signaling==null){pendingPassword="";toast(L10n.tr("请先完成联网授权"));return;}
session=signaling;showProgress();session.connect(id,secret);
}
private void showProgress(){
Dialog dialog=new Dialog(this);progressDialog=dialog;LinearLayout card=column();ui.card(card,24);card.setGravity(Gravity.CENTER_HORIZONTAL);LinearLayout body=column();body.setGravity(Gravity.CENTER);body.setPadding(dp(26),dp(24),dp(26),dp(20));
ProgressBar spinner=new ProgressBar(this);spinner.setIndeterminateTintList(ColorStateList.valueOf(BLUE));body.addView(spinner,ui.size(34,34));ui.gap(body,14);TextView title=text("正在连接",20,true);title.setGravity(Gravity.CENTER);body.addView(title);ui.gap(body,6);status=text("正在连接信令服务器…",14,false);status.setGravity(Gravity.CENTER);status.setTextColor(SECONDARY);status.setMinHeight(dp(36));body.addView(status);card.addView(body,ui.size(-1,-2));ui.divider(card);card.addView(ui.action("取消连接",RED,()->end("")),ui.size(-1,46));
ProgressBar spinner=new ProgressBar(this);spinner.setIndeterminateTintList(ColorStateList.valueOf(BLUE));body.addView(spinner,ui.size(34,34));ui.gap(body,14);TextView title=text(L10n.tr("正在连接"),20,true);title.setGravity(Gravity.CENTER);body.addView(title);ui.gap(body,6);status=text(L10n.tr("正在连接信令服务器…"),14,false);status.setGravity(Gravity.CENTER);status.setTextColor(SECONDARY);status.setMinHeight(dp(36));body.addView(status);card.addView(body,ui.size(-1,-2));ui.divider(card);card.addView(ui.action(L10n.tr("取消连接"),RED,()->end("")),ui.size(-1,46));
dialog.setContentView(card);dialog.setCanceledOnTouchOutside(false);dialog.setOnCancelListener(d->end(""));Window w=dialog.getWindow();if(w!=null){w.setBackgroundDrawableResource(android.R.color.transparent);w.setDimAmount(.08f);}dialog.show();if(w!=null)w.setLayout(dp(292),-2);
}
private void sessionScreen(boolean autoLandscape){
@@ -349,7 +349,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
});
pictureInPicture.connected();updatePictureInPicture();
}
private void confirmDisconnect(){disconnectDialog=new AlertDialog.Builder(this).setTitle("断开远程连接?").setMessage("断开后将返回首页").setNegativeButton("取消",null).setPositiveButton("断开连接",(d,w)->end("")).show();}
private void confirmDisconnect(){disconnectDialog=new AlertDialog.Builder(this).setTitle(L10n.tr("断开远程连接?")).setMessage(L10n.tr("断开后将返回首页")).setNegativeButton(L10n.tr("取消"),null).setPositiveButton(L10n.tr("断开连接"),(d,w)->end("")).show();}
private void end(String reason){
dismissPrivacyUnlockDialog();
remoteOrientation.end();
@@ -367,48 +367,48 @@ public final class MainActivity extends Activity implements NativeSession.Listen
appUpdates.checkNow();
}
private void settings(){
LinearLayout content=formPage("settings","设置",()->home(""));
LinearLayout mouse=section(content,"鼠标控制",MobileUi.SETTINGS_CARD_RADIUS);mouse.setPadding(dp(12),dp(12),dp(12),dp(12));boolean relative=preferences.getBoolean("relativeMouse",true);
mouse.addView(segments(new String[]{"相对位置","绝对位置"},relative?0:1,true,index->preferences.edit().putBoolean("relativeMouse",index==0).apply()));
LinearLayout picture=section(content,"画面偏好",MobileUi.SETTINGS_CARD_RADIUS);picture.setPadding(dp(12),dp(12),dp(12),dp(12));int preference=Math.max(0,Math.min(2,preferences.getInt("videoPreference",1)));
picture.addView(segments(RemoteVideoSettings.PREFERENCES,preference,true,index->preferences.edit().putInt("videoPreference",index).apply()));ui.gap(content,28);
LinearLayout direction=section(content,"屏幕方向",MobileUi.SETTINGS_CARD_RADIUS);LinearLayout directionRow=row();directionRow.setPadding(dp(16),dp(10),dp(16),dp(10));directionRow.addView(text("连接建立后锁定横屏",16,false),new LinearLayout.LayoutParams(0,-2,1));
Switch autoLandscape=new Switch(this);autoLandscape.setContentDescription("连接建立后锁定横屏");autoLandscape.setChecked(preferences.getBoolean("autoLandscape",false));autoLandscape.setOnCheckedChangeListener((button,checked)->preferences.edit().putBoolean("autoLandscape",checked).apply());directionRow.addView(autoLandscape);direction.addView(directionRow);
footer(content,"默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。");
LinearLayout content=formPage("settings",L10n.tr("设置"),()->home(""));
LinearLayout mouse=section(content,L10n.tr("鼠标控制"),MobileUi.SETTINGS_CARD_RADIUS);mouse.setPadding(dp(12),dp(12),dp(12),dp(12));boolean relative=preferences.getBoolean("relativeMouse",true);
mouse.addView(segments(new String[]{L10n.tr("相对位置"),L10n.tr("绝对位置")},relative?0:1,true,index->preferences.edit().putBoolean("relativeMouse",index==0).apply()));
LinearLayout picture=section(content,L10n.tr("画面偏好"),MobileUi.SETTINGS_CARD_RADIUS);picture.setPadding(dp(12),dp(12),dp(12),dp(12));int preference=Math.max(0,Math.min(2,preferences.getInt("videoPreference",1)));
picture.addView(segments(RemoteVideoSettings.preferences(),preference,true,index->preferences.edit().putInt("videoPreference",index).apply()));ui.gap(content,28);
LinearLayout direction=section(content,L10n.tr("屏幕方向"),MobileUi.SETTINGS_CARD_RADIUS);LinearLayout directionRow=row();directionRow.setPadding(dp(16),dp(10),dp(16),dp(10));directionRow.addView(text(L10n.tr("连接建立后锁定横屏"),16,false),new LinearLayout.LayoutParams(0,-2,1));
Switch autoLandscape=new Switch(this);autoLandscape.setContentDescription(L10n.tr("连接建立后锁定横屏"));autoLandscape.setChecked(preferences.getBoolean("autoLandscape",false));autoLandscape.setOnCheckedChangeListener((button,checked)->preferences.edit().putBoolean("autoLandscape",checked).apply());directionRow.addView(autoLandscape);direction.addView(directionRow);
footer(content,L10n.tr("默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。"));
LinearLayout server=section(content,"",MobileUi.SETTINGS_CARD_RADIUS);LinearLayout fields=column();fields.setVisibility(View.GONE);boolean custom=!host.equals("api.crossdesk.cn")||port!=9099;
link(server,"服务器",custom?"自定义":"默认",()->fields.setVisibility(fields.getVisibility()==View.VISIBLE?View.GONE:View.VISIBLE));server.addView(fields);
LinearLayout toggleRow=row();toggleRow.setPadding(dp(16),dp(8),dp(16),dp(8));toggleRow.addView(text("使用自定义服务器",16,false),new LinearLayout.LayoutParams(0,-2,1));Switch toggle=new Switch(this);toggle.setContentDescription("使用自定义服务器");toggle.setChecked(custom);toggleRow.addView(toggle);fields.addView(toggleRow);
LinearLayout inputs=column();inputs.setPadding(dp(12),0,dp(12),dp(12));EditText address=field("服务器地址",InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_URI),number=field("服务器端口",InputType.TYPE_CLASS_NUMBER);address.setText(custom?host:"");number.setText(custom?String.valueOf(port):"");inputs.addView(address,ui.size(-1,48));ui.gap(inputs,8);inputs.addView(number,ui.size(-1,48));ui.gap(inputs,8);
inputs.addView(ui.action("完成",BLUE,()->{if(saveServer(address,number)){hideKeyboard();settings();}}));fields.addView(inputs);inputs.setVisibility(custom?View.VISIBLE:View.GONE);
link(server,L10n.tr("服务器"),custom?L10n.tr("自定义"):L10n.tr("默认"),()->fields.setVisibility(fields.getVisibility()==View.VISIBLE?View.GONE:View.VISIBLE));server.addView(fields);
LinearLayout toggleRow=row();toggleRow.setPadding(dp(16),dp(8),dp(16),dp(8));toggleRow.addView(text(L10n.tr("使用自定义服务器"),16,false),new LinearLayout.LayoutParams(0,-2,1));Switch toggle=new Switch(this);toggle.setContentDescription(L10n.tr("使用自定义服务器"));toggle.setChecked(custom);toggleRow.addView(toggle);fields.addView(toggleRow);
LinearLayout inputs=column();inputs.setPadding(dp(12),0,dp(12),dp(12));EditText address=field(L10n.tr("服务器地址"),InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_VARIATION_URI),number=field(L10n.tr("服务器端口"),InputType.TYPE_CLASS_NUMBER);address.setText(custom?host:"");number.setText(custom?String.valueOf(port):"");inputs.addView(address,ui.size(-1,48));ui.gap(inputs,8);inputs.addView(number,ui.size(-1,48));ui.gap(inputs,8);
inputs.addView(ui.action(L10n.tr("完成"),BLUE,()->{if(saveServer(address,number)){hideKeyboard();settings();}}));fields.addView(inputs);inputs.setVisibility(custom?View.VISIBLE:View.GONE);
toggle.setOnCheckedChangeListener((button,checked)->{inputs.setVisibility(checked?View.VISIBLE:View.GONE);if(!checked){host="api.crossdesk.cn";port=9099;preferences.edit().putString("host",host).putInt("port",port).apply();ensureSignaling();} });
backAction=()->{if(toggle.isChecked()&&!saveServer(address,number))return;hideKeyboard();home("");};
identitySummary=text("",12,false);identitySummary.setTextColor(SECONDARY);identitySummary.setGravity(Gravity.CENTER);identitySummary.setPadding(dp(16),dp(12),dp(16),dp(16));identitySummary.setTextIsSelectable(true);content.addView(identitySummary);renderSignaling();
LinearLayout privacy=section(content,"隐私",MobileUi.SETTINGS_CARD_RADIUS);link(privacy,"隐私与授权",preferences.getBoolean("networkConsent",false)?"已授权":"未授权",this::privacyPage);ui.divider(privacy);
LinearLayout preview=row();preview.setPadding(dp(16),dp(10),dp(16),dp(10));preview.addView(text("保存远程画面预览",16,false),new LinearLayout.LayoutParams(0,-2,1));previewSwitch=new Switch(this);previewSwitch.setContentDescription("保存远程画面预览");preview.addView(previewSwitch);privacy.addView(preview);
LinearLayout privacy=section(content,L10n.tr("隐私"),MobileUi.SETTINGS_CARD_RADIUS);link(privacy,L10n.tr("隐私与授权"),preferences.getBoolean("networkConsent",false)?L10n.tr("已授权"):L10n.tr("未授权"),this::privacyPage);ui.divider(privacy);
LinearLayout preview=row();preview.setPadding(dp(16),dp(10),dp(16),dp(10));preview.addView(text(L10n.tr("保存远程画面预览"),16,false),new LinearLayout.LayoutParams(0,-2,1));previewSwitch=new Switch(this);previewSwitch.setContentDescription(L10n.tr("保存远程画面预览"));preview.addView(previewSwitch);privacy.addView(preview);
previewSwitch.setOnCheckedChangeListener((button,checked)->{if(updatingPreviewSwitch)return;if(checked){renderPreviews();confirmPreviewSaving();}else previews.setEnabled(false);});
ui.divider(privacy);clearPreviewsButton=ui.action("清除预览图",RED,this::confirmClearPreviews);clearPreviewsButton.setGravity(Gravity.START|Gravity.CENTER_VERTICAL);clearPreviewsButton.setTypeface(Typeface.DEFAULT);clearPreviewsButton.setPadding(dp(16),0,dp(16),0);privacy.addView(clearPreviewsButton,ui.size(-1,48));
ui.divider(privacy);clearPreviewsButton=ui.action(L10n.tr("清除预览图"),RED,this::confirmClearPreviews);clearPreviewsButton.setGravity(Gravity.START|Gravity.CENTER_VERTICAL);clearPreviewsButton.setTypeface(Typeface.DEFAULT);clearPreviewsButton.setPadding(dp(16),0,dp(16),0);privacy.addView(clearPreviewsButton,ui.size(-1,48));
previewCleanupError=text("",12,false);previewCleanupError.setTextColor(RED);previewCleanupError.setPadding(dp(16),dp(8),dp(16),dp(12));privacy.addView(previewCleanupError);renderPreviews();
ui.gap(content,28);LinearLayout about=section(content,"",MobileUi.SETTINGS_CARD_RADIUS);LinearLayout aboutLink=link(about,"关于","新版本",this::about);aboutUpdateRow=aboutLink;
ui.gap(content,28);LinearLayout about=section(content,"",MobileUi.SETTINGS_CARD_RADIUS);LinearLayout aboutLink=link(about,L10n.tr("关于"),L10n.tr("新版本"),this::about);aboutUpdateRow=aboutLink;
aboutUpdateLabel=(TextView)aboutLink.getChildAt(1);aboutUpdateLabel.setPadding(0,0,dp(8),0);
aboutUpdateDot=updateDot();LinearLayout.LayoutParams dotPosition=new LinearLayout.LayoutParams(dp(8),dp(8));dotPosition.setMarginEnd(dp(8));aboutLink.addView(aboutUpdateDot,2,dotPosition);renderAppUpdates();
}
private void confirmPreviewSaving(){
previewDialog=new AlertDialog.Builder(this).setTitle("保存远程画面预览?")
.setMessage("预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。")
.setNegativeButton("取消",null).setPositiveButton("开启保存",(d,w)->previews.setEnabled(true)).show();
previewDialog=new AlertDialog.Builder(this).setTitle(L10n.tr("保存远程画面预览?"))
.setMessage(L10n.tr("预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。"))
.setNegativeButton(L10n.tr("取消"),null).setPositiveButton(L10n.tr("开启保存"),(d,w)->previews.setEnabled(true)).show();
previewDialog.setOnDismissListener(d->{previewDialog=null;renderPreviews();});
}
private void confirmClearPreviews(){
previewDialog=new AlertDialog.Builder(this).setTitle("清除预览图?")
.setMessage("将删除本机保存的所有远程预览图,保留连接记录和密码。"+(previews.enabled()?"后续连接仍会保存新的预览图。":""))
.setNegativeButton("取消",null).setPositiveButton("清除",(d,w)->previews.clear()).show();
previewDialog=new AlertDialog.Builder(this).setTitle(L10n.tr("清除预览图?"))
.setMessage(L10n.tr("将删除本机保存的所有远程预览图,保留连接记录和密码。")+(previews.enabled()?L10n.tr("后续连接仍会保存新的预览图。"):""))
.setNegativeButton(L10n.tr("取消"),null).setPositiveButton(L10n.tr("清除"),(d,w)->previews.clear()).show();
previewDialog.setOnDismissListener(d->previewDialog=null);
}
private void renderPreviews(){
if(previews==null)return;
if(!previews.enabled()||previews.cleaning)previewCapture=null;
if(previewSwitch!=null){updatingPreviewSwitch=true;previewSwitch.setChecked(previews.enabled());previewSwitch.setEnabled(preferences.getBoolean("networkConsent",false)&&!previews.cleaning);updatingPreviewSwitch=false;}
if(clearPreviewsButton!=null){clearPreviewsButton.setText(previews.cleaning?"正在清除…":"清除预览图");clearPreviewsButton.setEnabled(!previews.cleaning);}
if(clearPreviewsButton!=null){clearPreviewsButton.setText(previews.cleaning?L10n.tr("正在清除…"):L10n.tr("清除预览图"));clearPreviewsButton.setEnabled(!previews.cleaning);}
if(previewCleanupError!=null){previewCleanupError.setText(previews.cleanupError);previewCleanupError.setVisibility(previews.cleanupError.isEmpty()?View.GONE:View.VISIBLE);}
for(Runnable card:previewCards)card.run();
}
@@ -427,17 +427,17 @@ public final class MainActivity extends Activity implements NativeSession.Listen
return new SegmentedControl(ui,titles,selected,enabled,changed);
}
private boolean saveServer(EditText address,EditText number){
String name=address.getText().toString().trim();int value;try{value=Integer.parseInt(number.getText().toString());}catch(NumberFormatException e){number.setError("请输入 1–65535 范围内的端口");return false;}
if(name.length()>253||!name.matches("[A-Za-z0-9.-]+")||name.startsWith(".")||name.endsWith(".")){address.setError("填写主机名或 IPv4 地址,不含协议和路径");return false;}if(value<1||value>65535){number.setError("端口范围为 1–65535");return false;}host=name;port=value;preferences.edit().putString("host",host).putInt("port",port).apply();ensureSignaling();return true;
String name=address.getText().toString().trim();int value;try{value=Integer.parseInt(number.getText().toString());}catch(NumberFormatException e){number.setError(L10n.tr("请输入 1–65535 范围内的端口"));return false;}
if(name.length()>253||!name.matches("[A-Za-z0-9.-]+")||name.startsWith(".")||name.endsWith(".")){address.setError(L10n.tr("填写主机名或 IPv4 地址,不含协议和路径"));return false;}if(value<1||value>65535){number.setError(L10n.tr("端口范围为 1–65535"));return false;}host=name;port=value;preferences.edit().putString("host",host).putInt("port",port).apply();ensureSignaling();return true;
}
private void announcements(){
installRoot("announcements",false);LinearLayout bar=navigation("通知公告",()->home(""));
bar.removeViewAt(bar.getChildCount()-1);announcementRefresh=ui.iconButton("refresh","刷新公告",announcementInbox::refresh);bar.addView(announcementRefresh,ui.size(44,44));
installRoot("announcements",false);LinearLayout bar=navigation(L10n.tr("通知公告"),()->home(""));
bar.removeViewAt(bar.getChildCount()-1);announcementRefresh=ui.iconButton("refresh",L10n.tr("刷新公告"),announcementInbox::refresh);bar.addView(announcementRefresh,ui.size(44,44));
announcementView=new AnnouncementView(ui,announcementInbox,null,this::announcementDetail,this::connectAnnouncements);
root.addView(announcementView,new LinearLayout.LayoutParams(-1,0,1));announcementInbox.refresh();renderAnnouncements();
}
private void announcementDetail(AnnouncementInbox.Item item){
installRoot("announcement-detail",false);navigation("公告详情",this::announcements);
installRoot("announcement-detail",false);navigation(L10n.tr("公告详情"),this::announcements);
announcementView=new AnnouncementView(ui,announcementInbox,item,this::announcementDetail,this::connectAnnouncements);
root.addView(announcementView,new LinearLayout.LayoutParams(-1,0,1));announcementInbox.markRead(item);
}
@@ -447,7 +447,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
}
private void renderAnnouncements(){
if(announcementBadge!=null){int unread=announcementInbox.unread;announcementBadge.setText(unread>99?"99+":String.valueOf(unread));announcementBadge.setVisibility(unread>0?View.VISIBLE:View.GONE);
if(android.os.Build.VERSION.SDK_INT>=30)((View)announcementBadge.getParent()).setStateDescription(unread>0?unread+" 条未读":"无未读公告");}
if(android.os.Build.VERSION.SDK_INT>=30)((View)announcementBadge.getParent()).setStateDescription(unread>0?L10n.format("{0} 条未读",unread):L10n.tr("无未读公告"));}
if(announcementRefresh!=null){boolean enabled=announcementInbox.isConnected()&&!announcementInbox.loading;announcementRefresh.setEnabled(enabled);announcementRefresh.setAlpha(enabled?1:.4f);}
if(announcementView!=null)announcementView.render();
}
@@ -457,12 +457,12 @@ public final class MainActivity extends Activity implements NativeSession.Listen
}
@Override public void announcementFailed(String requestId){announcementInbox.failed(requestId);}
private void privacyPage(){
LinearLayout content=formPage("privacy","隐私与授权",this::settings);LinearLayout card=section(content,"");boolean consent=preferences.getBoolean("networkConsent",false);link(card,"授权状态",consent?"已授权":"未授权",()->{});ui.divider(card);link(card,"隐私政策","",this::privacyPolicyPage);
footer(content,"设备身份及选择保存的访问密码使用 Android Keystore 加密。连接记录仅保存在本机,不纳入设备备份。剪贴板内容在结束会话时清除。离开应用后通过画中画或连接通知保持会话,可在应用或通知中断开。");
if(consent)content.addView(ui.action("撤回联网授权",RED,()->new AlertDialog.Builder(this).setTitle("撤回联网授权?").setMessage("撤回后将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。").setNegativeButton("取消",null).setPositiveButton("撤回",(d,w)->{preferences.edit().putBoolean("networkConsent",false).apply();appUpdates.setEnabled(false);previews.setEnabled(false);stopSignaling();announcementInbox.reset();privacyPage();}).show()));else content.addView(ui.primary("阅读并授权",()->consent(this::privacyPage)));
LinearLayout content=formPage("privacy",L10n.tr("隐私与授权"),this::settings);LinearLayout card=section(content,"");boolean consent=preferences.getBoolean("networkConsent",false);link(card,L10n.tr("授权状态"),consent?L10n.tr("已授权"):L10n.tr("未授权"),()->{});ui.divider(card);link(card,L10n.tr("隐私政策"),"",this::privacyPolicyPage);
footer(content,L10n.tr("设备身份及选择保存的访问密码使用 Android Keystore 加密。连接记录仅保存在本机,不纳入设备备份。剪贴板内容在结束会话时清除。离开应用后通过画中画或连接通知保持会话,可在应用或通知中断开。"));
if(consent)content.addView(ui.action(L10n.tr("撤回联网授权"),RED,()->new AlertDialog.Builder(this).setTitle(L10n.tr("撤回联网授权?")).setMessage(L10n.tr("撤回后将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。")).setNegativeButton(L10n.tr("取消"),null).setPositiveButton(L10n.tr("撤回"),(d,w)->{preferences.edit().putBoolean("networkConsent",false).apply();appUpdates.setEnabled(false);previews.setEnabled(false);stopSignaling();announcementInbox.reset();privacyPage();}).show()));else content.addView(ui.primary(L10n.tr("阅读并授权"),()->consent(this::privacyPage)));
}
private void privacyPolicyPage(){
installRoot("privacy-policy",false);navigation("隐私政策",this::privacyPage);
installRoot("privacy-policy",false);navigation(L10n.tr("隐私政策"),this::privacyPage);
root.addView(new PrivacyPolicyDocument(this).view(ui),new LinearLayout.LayoutParams(-1,0,1));
}
String applicationVersion(){
@@ -479,8 +479,8 @@ public final class MainActivity extends Activity implements NativeSession.Listen
if(settingsUpdateDot!=null)settingsUpdateDot.setVisibility(available?View.VISIBLE:View.GONE);
if(aboutUpdateDot!=null)aboutUpdateDot.setVisibility(available?View.VISIBLE:View.GONE);
if(aboutUpdateLabel!=null)aboutUpdateLabel.setVisibility(available?View.VISIBLE:View.GONE);
if(settingsUpdateButton!=null)settingsUpdateButton.setContentDescription(available?"设置,有新版本":"设置");
if(aboutUpdateRow!=null)aboutUpdateRow.setContentDescription(available?"关于,有新版本":"关于");
if(settingsUpdateButton!=null)settingsUpdateButton.setContentDescription(available?L10n.tr("设置,有新版本"):L10n.tr("设置"));
if(aboutUpdateRow!=null)aboutUpdateRow.setContentDescription(available?L10n.tr("关于,有新版本"):L10n.tr("关于"));
if(aboutPages!=null&&page.equals("about"))aboutPages.renderUpdates();
}
private void about(){
@@ -490,16 +490,16 @@ public final class MainActivity extends Activity implements NativeSession.Listen
private boolean sendTextEdit(int count,String value){
if(session==null)return false;
if(session.textEdit(count,value))return true;
toast("当前远端不支持直接输入此文本,请使用支持文本输入的被控端版本,或通过剪贴板发送");return false;
toast(L10n.tr("当前远端不支持直接输入此文本,请使用支持文本输入的被控端版本,或通过剪贴板发送"));return false;
}
private void tapKey(int code){if(session!=null){session.key(code,true);session.key(code,false);}}
private void shortcut(int code){if(session!=null){int modifier=hostPlatform.equals("macos")?0x5B:0x11;session.key(modifier,true);tapKey(code);session.key(modifier,false);}}
private void selectDisplay(){
if(!ready)return;int count=Math.min(displays.length(),8);if(count==0){toast("等待远端屏幕信息");return;}String[] labels=new String[count];for(int i=0;i<count;i++){JSONObject display=displays.optJSONObject(i);labels[i]=(i+1)+" · "+(display==null?"显示器":display.optString("name","显示器"));}new AlertDialog.Builder(this).setTitle("显示器").setItems(labels,(d,w)->{if(session!=null){if(w!=selectedDisplay&&video!=null)video.resetViewport();selectedDisplay=w;session.control(4,w);if(controls!=null)controls.resetVideoStatistics();}}).show();
if(!ready)return;int count=Math.min(displays.length(),8);if(count==0){toast(L10n.tr("等待远端屏幕信息"));return;}String[] labels=new String[count];for(int i=0;i<count;i++){JSONObject display=displays.optJSONObject(i);labels[i]=(i+1)+" · "+(display==null?L10n.tr("显示器"):display.optString("name",L10n.tr("显示器")));}new AlertDialog.Builder(this).setTitle(L10n.tr("显示器")).setItems(labels,(d,w)->{if(session!=null){if(w!=selectedDisplay&&video!=null)video.resetViewport();selectedDisplay=w;session.control(4,w);if(controls!=null)controls.resetVideoStatistics();}}).show();
}
private void secureAttention(){
if(!ready||session==null||!hostPlatform.equals("windows"))return;
if(!remoteServiceAvailable){controlFeedback("请在远端启用 CrossDesk 系统服务后重试");return;}
if(!remoteServiceAvailable){controlFeedback(L10n.tr("请在远端启用 CrossDesk 系统服务后重试"));return;}
session.secureAttention();
}
private void chooseFile(){
@@ -516,7 +516,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
documentSession=session;releaseKeys();
pictureInPicture.choosingDocument(true);updatePictureInPicture();
try{startActivityForResult(intent,request);mainHandler.postDelayed(documentTimeout,5*60*1000);}
catch(android.content.ActivityNotFoundException error){documentSession=null;documentToSave=null;pictureInPicture.choosingDocument(false);updatePictureInPicture();controlFeedback("此设备没有可用的文件选择器");}
catch(android.content.ActivityNotFoundException error){documentSession=null;documentToSave=null;pictureInPicture.choosingDocument(false);updatePictureInPicture();controlFeedback(L10n.tr("此设备没有可用的文件选择器"));}
}
@Override protected void onActivityResult(int request,int result,Intent data){
super.onActivityResult(request,result,data);
@@ -530,7 +530,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
@Override public void fileTransfer(String name,double progress,boolean sending,java.io.File received){
if(!ready)return;
if(received!=null)receivedFile=received;
String fileStatus=progress<0?name+" 传输失败":(sending?"发送 ":"接收 ")+name+(progress>=1?" · 已完成":" · "+(int)(progress*100)+"%");
String fileStatus=progress<0?L10n.format("{0} 传输失败",name):(sending?L10n.tr("发送 "):L10n.tr("接收 "))+name+(progress>=1?L10n.tr(" · 已完成"):" · "+(int)(progress*100)+"%");
if(controls!=null)controls.fileStatus(fileStatus,receivedFile!=null);
if(progress<0||progress>=1)toast(fileStatus);
}
@@ -539,14 +539,14 @@ public final class MainActivity extends Activity implements NativeSession.Listen
}
private void clipboard(){
if(!ready)return;
EditText input=field("要发送到远端的文本",InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_MULTI_LINE);input.setSingleLine(false);input.setMinLines(3);
new AlertDialog.Builder(this).setTitle("剪贴板").setView(input)
.setMessage("发送后,在远程键盘中点击“粘贴”。接收的远端文本可手动复制到本机。")
.setNegativeButton("关闭",null).setNeutralButton("复制远端文本",(d,w)->{
EditText input=field(L10n.tr("要发送到远端的文本"),InputType.TYPE_CLASS_TEXT|InputType.TYPE_TEXT_FLAG_MULTI_LINE);input.setSingleLine(false);input.setMinLines(3);
new AlertDialog.Builder(this).setTitle(L10n.tr("剪贴板")).setView(input)
.setMessage(L10n.tr("发送后,在远程键盘中点击“粘贴”。接收的远端文本可手动复制到本机。"))
.setNegativeButton(L10n.tr("关闭"),null).setNeutralButton(L10n.tr("复制远端文本"),(d,w)->{
if(!remoteClipboard.isEmpty())((ClipboardManager)getSystemService(CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("CrossDesk",remoteClipboard));
}).setPositiveButton("发送文本",(d,w)->{
}).setPositiveButton(L10n.tr("发送文本"),(d,w)->{
String value=input.getText().toString();
if(value.getBytes(java.nio.charset.StandardCharsets.UTF_8).length>128*1024){Toast.makeText(this,"文本超过 128 KB",Toast.LENGTH_SHORT).show();return;}
if(value.getBytes(java.nio.charset.StandardCharsets.UTF_8).length>128*1024){Toast.makeText(this,L10n.tr("文本超过 128 KB"),Toast.LENGTH_SHORT).show();return;}
if(session!=null)session.clipboard(value);
}).show();
}
@@ -581,17 +581,17 @@ public final class MainActivity extends Activity implements NativeSession.Listen
boolean consent=preferences.getBoolean("networkConsent",false);
boolean connected=consent&&signalError.isEmpty()&&signalState==1;
String label;int tint,background;
if(!consent){label="等待隐私授权";tint=0xFFB76A00;background=0xFFFFEBD2;}
else if(!signalError.isEmpty()){label=signalError.contains("TLS")?"证书校验失败 · 点击重试":"连接失败 · 点击重试";tint=RED;background=0xFFFFE8E6;}
else if(connected){label="已连接服务器";tint=GREEN;background=0xFFE5F4E8;}
else{label=signalState==0?"正在连接服务器…":signalState==4?"正在重连服务器…":"未连接服务器 · 点击重试";tint=0xFFB76A00;background=0xFFFFEBD2;}
if(!consent){label=L10n.tr("等待隐私授权");tint=0xFFB76A00;background=0xFFFFEBD2;}
else if(!signalError.isEmpty()){label=signalError.contains("TLS")?L10n.tr("证书校验失败 · 点击重试"):L10n.tr("连接失败 · 点击重试");tint=RED;background=0xFFFFE8E6;}
else if(connected){label=L10n.tr("已连接服务器");tint=GREEN;background=0xFFE5F4E8;}
else{label=signalState==0?L10n.tr("正在连接服务器…"):signalState==4?L10n.tr("正在重连服务器…"):L10n.tr("未连接服务器 · 点击重试");tint=0xFFB76A00;background=0xFFFFEBD2;}
if(signalBadge!=null){
android.graphics.drawable.Drawable icon=connected?getDrawable(R.drawable.ic_check_circle):null;
if(icon!=null){int size=dp(14);icon.setBounds(0,0,size,size);}
signalBadge.setCompoundDrawablesRelative(icon,null,null,null);signalBadge.setCompoundDrawablePadding(dp(6));
signalBadge.setText(label);signalBadge.setTextColor(tint);signalBadge.setBackground(ui.background(background,20));signalBadge.setContentDescription(signalError.isEmpty()?label:signalError+",点击重试");
signalBadge.setText(label);signalBadge.setTextColor(tint);signalBadge.setBackground(ui.background(background,20));signalBadge.setContentDescription(signalError.isEmpty()?label:signalError+L10n.tr(",点击重试"));
}
if(identitySummary!=null)identitySummary.setText(!consent?"完成隐私授权后登记本机身份":!localIdentity.isEmpty()?"本机 ID "+localIdentity:!signalError.isEmpty()?signalError:"正在获取本机 ID…");
if(identitySummary!=null)identitySummary.setText(!consent?L10n.tr("完成隐私授权后登记本机身份"):!localIdentity.isEmpty()?L10n.format("本机 ID {0}",localIdentity):!signalError.isEmpty()?signalError:L10n.tr("正在获取本机 ID…"));
}
@Override public void signaling(int state,String deviceId){
signalState=state;localIdentity=deviceId;signalError="";renderSignaling();
@@ -606,7 +606,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
// Transport readiness can be reported again after ICE changes its selected path.
// The first success consumes pendingPassword; a duplicate must not save it again.
if(session==null||ready)return;ready=true;if(progressDialog!=null){progressDialog.dismiss();progressDialog=null;}
try{history.save(serverKey(),connectedRemote,connectedRemote,hostPlatform,rememberPassword,pendingPassword);}catch(Exception error){toast("密码保存失败,本次连接仍可使用");}pendingPassword="";sessionScreen(preferences.getBoolean("autoLandscape",false));
try{history.save(serverKey(),connectedRemote,connectedRemote,hostPlatform,rememberPassword,pendingPassword);}catch(Exception error){toast(L10n.tr("密码保存失败,本次连接仍可使用"));}pendingPassword="";sessionScreen(preferences.getBoolean("autoLandscape",false));
if(!canUsePictureInPicture())prepareBackgroundSession(true);
if(videoSettings.supported&&videoSettings.requestId==0)updateVideoSettings(videoSettings.selection);
if(foreground&&preferences.getBoolean("networkConsent",false))remoteVersionCheck.connected();
@@ -620,12 +620,12 @@ public final class MainActivity extends Activity implements NativeSession.Listen
String id=connectedRemote;boolean remember=rememberPassword;
end("");
// Let the user replace an expired credential; never retry it automatically.
if(foreground)passwordSheet(id,"访问密码错误,请重新输入",remember);
if(foreground)passwordSheet(id,L10n.tr("访问密码错误,请重新输入"),remember);
}
private void cancelVideoSettingsTimeout(){if(videoSettingsTimeout!=null)mainHandler.removeCallbacks(videoSettingsTimeout);videoSettingsTimeout=null;}
private void showRemoteUpdate(){
if(!ready||session==null||!foreground||pictureInPictureUi||isInPictureInPictureMode()||isFinishing()||!preferences.getBoolean("networkConsent",false))return;
remoteUpdateDialog=new AlertDialog.Builder(this).setTitle("升级提示").setMessage(RemoteVersionCheck.NOTICE).setPositiveButton("知道了",null).create();
remoteUpdateDialog=new AlertDialog.Builder(this).setTitle(L10n.tr("升级提示")).setMessage(L10n.tr(RemoteVersionCheck.NOTICE)).setPositiveButton(L10n.tr("知道了"),null).create();
remoteUpdateDialog.setOnDismissListener(dialog->remoteUpdateDialog=null);
remoteUpdateDialog.setCanceledOnTouchOutside(false);
remoteUpdateDialog.show();
@@ -673,12 +673,12 @@ public final class MainActivity extends Activity implements NativeSession.Listen
releaseKeys();
if (controls != null) { controls.closeKeyboard(); controls.cancelMouseInput(); }
if (video != null) video.releaseMouse();
privacyUnlockDialog = new AlertDialog.Builder(this).setTitle("隐私屏已被解锁")
.setMessage("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。")
.setNegativeButton("确认", (dialog, which) -> {
privacyUnlockDialog = new AlertDialog.Builder(this).setTitle(L10n.tr("隐私屏已被解锁"))
.setMessage(L10n.tr("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。"))
.setNegativeButton(L10n.tr("确认"), (dialog, which) -> {
if (session != null) session.privacyScreen.unlockNotice = false;
dismissPrivacyUnlockDialog();
}).setPositiveButton("锁定", null).create();
}).setPositiveButton(L10n.tr("锁定"), null).create();
privacyUnlockDialog.setCancelable(false);
privacyUnlockDialog.show();
privacyUnlockDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(view -> {
@@ -686,7 +686,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
});
}
String feedback = session.privacyScreen.feedback(android.os.SystemClock.elapsedRealtime());
privacyUnlockDialog.setMessage("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。"
privacyUnlockDialog.setMessage(L10n.tr("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。")
+ (feedback.isEmpty() ? "" : "\n\n" + feedback));
privacyUnlockDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(
session.privacyScreen.canLock(android.os.SystemClock.elapsedRealtime()));
@@ -744,10 +744,10 @@ public final class MainActivity extends Activity implements NativeSession.Listen
backgroundSession=retained;captureBackgroundSession();retained.attach(this);session.setListener(retained);
if(!RemoteSessionService.retain(this,retained)){
session.setListener(this);retained.detach(this);backgroundSession=null;
if(inform)toast("后台保活未能启动,请保持应用在前台");return false;
if(inform)toast(L10n.tr("后台保活未能启动,请保持应用在前台"));return false;
}
if(inform){
toast("已启用后台保活,可从连接通知返回或断开");
toast(L10n.tr("已启用后台保活,可从连接通知返回或断开"));
requestBackgroundNotifications();
}
return true;
@@ -762,7 +762,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
BackgroundSession retained=backgroundSession!=null?backgroundSession:RemoteSessionService.current();
if(retained==null)return false;
if(retained.ended||retained.session.isClosed()||!hasNetworkConsent()||!host.equals(retained.host)||port!=retained.port){
retained.detach(this);retained.finish("后台连接已结束,请重新连接");backgroundSession=null;
retained.detach(this);retained.finish(L10n.tr("后台连接已结束,请重新连接"));backgroundSession=null;
if(session==retained.session)end(retained.endReason);return false;
}
boolean rebuild=session!=retained.session||video==null||!page.equals("session");
@@ -832,7 +832,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
mainHandler.removeCallbacks(presenceTick);recentPresence.setConnected(false,android.os.SystemClock.elapsedRealtime());announcementInbox.setConnected(false);
cancelVideoSettingsTimeout();if(videoSettings.pending)videoSettings.expire(videoSettings.requestId);
resetRemoteVersionCheck();if(disconnectDialog!=null)disconnectDialog.dismiss();
}else if(documentSession==null||documentSession!=session||isFinishing()){if(session!=null)end(isFinishing()?"":"远程窗口已关闭,或后台保活未能启动,会话已结束");stopSignaling();}
}else if(documentSession==null||documentSession!=session||isFinishing()){if(session!=null)end(isFinishing()?"":L10n.tr("远程窗口已关闭,或后台保活未能启动,会话已结束"));stopSignaling();}
super.onStop();}
@Override protected void onDestroy(){dismissPrivacyUnlockDialog();mainHandler.removeCallbacks(documentTimeout);documentSession=null;documentToSave=null;appUpdates.setEnabled(false);pageHost.finish();resetRemoteVersionCheck();cancelVideoSettingsTimeout();previews.removeListener(previewChanged);if(privacyDialog!=null)privacyDialog.dismiss();if(previewDialog!=null)previewDialog.dismiss();announcementInbox.close();if(disconnectDialog!=null)disconnectDialog.dismiss();if(passwordDialog!=null)passwordDialog.dismiss();if(progressDialog!=null)progressDialog.dismiss();
if(connectionDialog!=null)connectionDialog.dismiss();
@@ -52,6 +52,7 @@ final class MobileUi {
}
TextView primary(String title,Runnable click) {
TextView v=action(title,Color.WHITE,click);
v.setMaxLines(2);v.setAutoSizeTextTypeUniformWithConfiguration(11,16,1,android.util.TypedValue.COMPLEX_UNIT_SP);
GradientDrawable gradient=new GradientDrawable(GradientDrawable.Orientation.TL_BR,new int[]{BLUE,0xFF1F61C7});gradient.setCornerRadius(dp(12));v.setBackground(gradient);return v;
}
View iconButton(String icon,String label,Runnable click) {
@@ -35,7 +35,7 @@ final class NativeSession implements AutoCloseable {
default void presence(JSONObject message) { }
default void announcement(JSONObject message) { }
default void announcementFailed(String requestId) { }
default void passwordRejected() { ended("远端密码错误"); }
default void passwordRejected() { ended(L10n.tr("远端密码错误")); }
}
// Serializes all peer creation/API calls/destruction, including across activities.
private static final ExecutorService RTC = Executors.newSingleThreadExecutor(r -> new Thread(r, "CrossDesk RTC"));
@@ -71,8 +71,8 @@ final class NativeSession implements AutoCloseable {
if(!closed)main.postDelayed(this,1000);
}};
private final Runnable timeout = () -> fail(remote.isEmpty()
? "服务器连接超时,请检查服务器地址和网络"
: "连接超时,请检查设备 ID、服务器及远端在线状态");
? L10n.tr("服务器连接超时,请检查服务器地址和网络")
: L10n.tr("连接超时,请检查设备 ID、服务器及远端在线状态"));
NativeSession(Context context, String host, int port, String remote, String password, Listener listener) {
this.context = context.getApplicationContext(); this.listener = listener;
@@ -92,10 +92,10 @@ final class NativeSession implements AutoCloseable {
File logs = new File(context.getNoBackupFilesDir(), "logs");
if (!logs.isDirectory() && !logs.mkdirs()) throw new IllegalStateException("Log directory unavailable");
handle = nCreate(this, host, port, identity, logs.getAbsolutePath(), certificates.getAbsolutePath());
if (handle == 0) { fail("原生连接初始化失败"); return; }
if (handle == 0) { fail(L10n.tr("原生连接初始化失败")); return; }
nFileDirectory(handle,new File(documents.directory,"received").getAbsolutePath());
if (surface != null && surface.isValid()) nSurface(handle, surface);
} catch (Exception error) { fail("安全存储或证书初始化失败,请重试或清除应用数据"); }
} catch (Exception error) { fail(L10n.tr("安全存储或证书初始化失败,请重试或清除应用数据")); }
});
}
/** Promote the already registered identity peer to an outgoing desktop session. */
@@ -147,7 +147,7 @@ final class NativeSession implements AutoCloseable {
if (handle == 0 || joining || remote.isEmpty() || !signalReady || identity.isEmpty()) return;
joining = true;
String base = identity.split("@", 2)[0];
if (!nConnect(handle, base, remote, password)) fail("无法发起远程连接");
if (!nConnect(handle, base, remote, password)) fail(L10n.tr("无法发起远程连接"));
password = "";
}
// Called on arbitrary native callback threads; borrowed payloads were copied by JNI.
@@ -175,10 +175,10 @@ final class NativeSession implements AutoCloseable {
publishSignaling();
tryJoin();
}
if (status == 6) fail("服务器 TLS 证书校验失败");
else if (status == 2) fail("服务器登录失败,请检查服务器地址和网络");
else if (status >= 3 && connected) fail("信令连接中断,请重新连接");
else if (status == 0 || status == 4) ui(() -> listener.status("正在连接信令服务器…"));
if (status == 6) fail(L10n.tr("服务器 TLS 证书校验失败"));
else if (status == 2) fail(L10n.tr("服务器登录失败,请检查服务器地址和网络"));
else if (status >= 3 && connected) fail(L10n.tr("信令连接中断,请重新连接"));
else if (status == 0 || status == 4) ui(() -> listener.status(L10n.tr("正在连接信令服务器…")));
} else if (type == 2) {
int status = event.getInt("status");
if (status == 1) {
@@ -187,12 +187,12 @@ final class NativeSession implements AutoCloseable {
audio = new AudioPlayer();
nControl(handle, 2, 1);
ui(() -> { listener.connected(); if(!closed){statisticsTick.run();fileTick.run();} });
} else if (status == 0 || status == 2) ui(() -> listener.status("正在建立安全连接…"));
} else if (status == 0 || status == 2) ui(() -> listener.status(L10n.tr("正在建立安全连接…")));
else if (status == 6) ui(() -> { listener.passwordRejected(); close(); });
else {
String[] reasons = {"", "", "", "连接已断开", "连接失败", "远端已关闭连接",
"远端密码错误", "找不到该设备 ID", "远端当前不可用"};
fail(status >= 0 && status < reasons.length ? reasons[status] : "连接已结束");
String[] reasons = {"", "", "", L10n.tr("连接已断开"), L10n.tr("连接失败"), L10n.tr("远端已关闭连接"),
L10n.tr("远端密码错误"), L10n.tr("找不到该设备 ID"), L10n.tr("远端当前不可用")};
fail(status >= 0 && status < reasons.length ? reasons[status] : L10n.tr("连接已结束"));
}
} else if (type == 4) {
if(event.optInt("type",-1)==14){
@@ -228,7 +228,7 @@ final class NativeSession implements AutoCloseable {
listener.fileTransfer(name,progress,sending,path.isEmpty()?null:new File(path));});
}
} catch (Exception error) {
if (type == 3) fail("设备身份保存失败,请检查安全存储");
if (type == 3) fail(L10n.tr("设备身份保存失败,请检查安全存储"));
}
});
}
@@ -303,24 +303,24 @@ final class NativeSession implements AutoCloseable {
}
void secureAttention(){execute(()->{
boolean sent=connected&&nSecureAttention(handle);
ui(()->listener.controlFeedback(sent?"已发送 Ctrl+Alt+Del":"指令发送失败,请重试"));
ui(()->listener.controlFeedback(sent?L10n.tr("已发送 Ctrl+Alt+Del"):L10n.tr("指令发送失败,请重试")));
});}
void sendFile(android.net.Uri uri){
if(closed)return;
if(sendingFile){listener.controlFeedback("请等待当前文件发送完成");return;}
sendingFile=true;listener.fileTransfer("正在读取所选文件…",0,true,null);
if(sendingFile){listener.controlFeedback(L10n.tr("请等待当前文件发送完成"));return;}
sendingFile=true;listener.fileTransfer(L10n.tr("正在读取所选文件…"),0,true,null);
documents.prepare(uri,new SessionDocuments.Prepared(){
public void ready(File file,String name){execute(()->{
if(!connected||!nSendFile(handle,file.getAbsolutePath(),name.getBytes(StandardCharsets.UTF_8))){
file.delete();ui(()->{sendingFile=false;listener.fileTransfer(name,-1,true,null);});
}
});}
public void failed(){ui(()->{sendingFile=false;listener.fileTransfer("所选文件(无法读取或空间不足)",-1,true,null);});}
public void failed(){ui(()->{sendingFile=false;listener.fileTransfer(L10n.tr("所选文件(无法读取或空间不足)"),-1,true,null);});}
});
}
void saveFile(File file,android.net.Uri destination){
if(closed)return;
documents.save(file,destination,success->ui(()->listener.controlFeedback(success?"文件已保存":"文件保存失败,请重试")));
documents.save(file,destination,success->ui(()->listener.controlFeedback(success?L10n.tr("文件已保存"):L10n.tr("文件保存失败,请重试"))));
}
@Override public void close() {
if (closed) return;
@@ -20,16 +20,16 @@ final class PrivacyConsentDialog extends Dialog {
PrivacyConsentDialog(Activity activity,MobileUi ui,PrivacyPolicyDocument policy,Runnable accepted){
super(activity);this.activity=activity;this.ui=ui;requestWindowFeature(Window.FEATURE_NO_TITLE);setCancelable(false);setCanceledOnTouchOutside(false);
LinearLayout sheet=ui.column();sheet.setBackground(ui.background(android.graphics.Color.WHITE,20));sheet.setClipToOutline(true);
TextView title=ui.text("隐私政策",17,true);title.setGravity(Gravity.CENTER);sheet.addView(title,ui.size(-1,56));ui.divider(sheet);
TextView title=ui.text(L10n.tr("隐私政策"),17,true);title.setGravity(Gravity.CENTER);sheet.addView(title,ui.size(-1,56));ui.divider(sheet);
sheet.addView(policy.view(ui),new LinearLayout.LayoutParams(-1,0,1));ui.divider(sheet);
LinearLayout actions=ui.column();actions.setPadding(ui.dp(20),ui.dp(12),ui.dp(20),ui.dp(12));sheet.addView(actions);
CheckBox hasReadPolicy=new CheckBox(activity);hasReadPolicy.setText("我已阅读并同意《隐私政策》");hasReadPolicy.setTextColor(INK);hasReadPolicy.setTextSize(14);hasReadPolicy.setMinHeight(ui.dp(44));actions.addView(hasReadPolicy,ui.size(-1,-2));ui.gap(actions,12);
TextView agree=ui.primary("同意并继续",()->{
CheckBox hasReadPolicy=new CheckBox(activity);hasReadPolicy.setText(L10n.tr("我已阅读并同意《隐私政策》"));hasReadPolicy.setTextColor(INK);hasReadPolicy.setTextSize(14);hasReadPolicy.setMinHeight(ui.dp(44));actions.addView(hasReadPolicy,ui.size(-1,-2));ui.gap(actions,12);
TextView agree=ui.primary(L10n.tr("同意并继续"),()->{
if(!hasReadPolicy.isChecked()||!policy.available)return;
dismiss();accepted.run();
});agree.setEnabled(false);agree.setAlpha(.45f);actions.addView(agree,ui.size(-1,48));
hasReadPolicy.setOnCheckedChangeListener((button,checked)->{boolean enabled=checked&&policy.available;agree.setEnabled(enabled);agree.setAlpha(enabled?1:.45f);});
ui.gap(actions,12);actions.addView(ui.action("暂不同意",SECONDARY,this::dismiss),ui.size(-1,44));
ui.gap(actions,12);actions.addView(ui.action(L10n.tr("暂不同意"),SECONDARY,this::dismiss),ui.size(-1,44));
setContentView(sheet);Window window=getWindow();if(window!=null){window.setBackgroundDrawableResource(android.R.color.transparent);window.setDimAmount(.22f);}
}
@Override public void show(){
@@ -13,12 +13,14 @@ final class PrivacyPolicyDocument {
final boolean available;
final String text;
PrivacyPolicyDocument(Context context){this(load(context));}
PrivacyPolicyDocument(String document){
PrivacyPolicyDocument(Context context){this(load(context),L10n.isChinese());}
PrivacyPolicyDocument(String document){this(document,true);}
private PrivacyPolicyDocument(String document,boolean chinese){
available=document!=null&&!document.trim().isEmpty();
String value=available?document:"隐私政策暂时无法读取,请拒绝联网并重新安装应用。";
String value=available?document:L10n.tr("隐私政策暂时无法读取,请拒绝联网并重新安装应用。");
int start=value.indexOf("## 中文\n"),end=start<0?-1:value.indexOf("## English",start);
text=start>=0&&end>start?value.substring(start+"## 中文\n".length(),end).trim():value;
text=chinese&&start>=0&&end>start?value.substring(start+"## 中文\n".length(),end).trim():
!chinese&&end>=0?value.substring(end+"## English".length()).trim():value;
}
private static String load(Context context){
try{return LicenseCatalog.read(context,"PRIVACY.md");}catch(IOException error){return null;}
@@ -28,7 +30,7 @@ final class PrivacyPolicyDocument {
LinearLayout content=ui.column();content.setPadding(ui.dp(20),ui.dp(20),ui.dp(20),ui.dp(20));scroll.addView(content);
for(String paragraph:text.split("\n\n")){
paragraph=paragraph.trim();if(paragraph.isEmpty()||paragraph.equals("---"))continue;
boolean heading=paragraph.startsWith("#"),date=paragraph.startsWith("更新日期:");
boolean heading=paragraph.startsWith("#"),date=paragraph.startsWith("更新日期:")||paragraph.startsWith("Last updated:");
String value=heading?paragraph.replaceFirst("^#+\\s*",""):paragraph.replace("**","");
TextView label=ui.text(value,date?12:16,heading);label.setTextIsSelectable(true);label.setLineSpacing(ui.dp(4),1);
if(date)label.setTextColor(SECONDARY);
@@ -39,12 +39,12 @@ final class RemoteKeyboard extends LinearLayout {
header.addView(editor,new FrameLayout.LayoutParams(1,1,Gravity.BOTTOM));
toolbar=ui.row();toolbar.setGravity(Gravity.CENTER_VERTICAL);toolbar.setPadding(ui.dp(6),0,ui.dp(6),0);
header.addView(toolbar,new FrameLayout.LayoutParams(-1,-1));
switchButton=button("电脑键盘",this::switchKeyboard);switchButton.setPadding(ui.dp(8),0,ui.dp(8),0);
switchButton=button(L10n.tr("电脑键盘"),this::switchKeyboard);switchButton.setPadding(ui.dp(8),0,ui.dp(8),0);
toolbar.addView(switchButton,new LayoutParams(-2,-1));
LinearLayout pages=ui.row();pages.setGravity(Gravity.CENTER);toolbar.addView(pages,new LayoutParams(0,-1,1));
lettersButton=button("字母数字",()->page(false));functionsButton=button("符号功能",()->page(true));
lettersButton=button(L10n.tr("字母数字"),()->page(false));functionsButton=button(L10n.tr("符号功能"),()->page(true));
pages.addView(lettersButton,new LayoutParams(-2,ui.dp(32)));pages.addView(functionsButton,new LayoutParams(-2,ui.dp(32)));
FrameLayout hide=new FrameLayout(context);hide.setContentDescription("收起键盘");hide.setClickable(true);hide.setFocusable(true);
FrameLayout hide=new FrameLayout(context);hide.setContentDescription(L10n.tr("收起键盘"));hide.setClickable(true);hide.setFocusable(true);
hide.addView(ui.icon("keyboard_hide",foreground()),new FrameLayout.LayoutParams(ui.dp(24),ui.dp(24),Gravity.CENTER));
hide.setOnClickListener(v->{feedback(v);close();});toolbar.addView(hide,new LayoutParams(ui.dp(44),ui.dp(44)));
keys=ui.column();keys.setPadding(ui.dp(3),ui.dp(4),ui.dp(3),ui.dp(4));addView(keys,new LayoutParams(-1,0,1));
@@ -157,7 +157,7 @@ final class RemoteKeyboard extends LinearLayout {
}
private void render(){
setBackgroundColor(dark()?0xFF202124:0xFFE8EAED);
switchButton.setText(computer?"系统键盘":"电脑键盘");switchButton.setTextColor(foreground());
switchButton.setText(computer?L10n.tr("系统键盘"):L10n.tr("电脑键盘"));switchButton.setTextColor(foreground());
lettersButton.setVisibility(computer?VISIBLE:GONE);functionsButton.setVisibility(computer?VISIBLE:GONE);
for(TextView view:new TextView[]{lettersButton,functionsButton}){
boolean selected=(view==functionsButton)==functions;view.setSelected(selected);
@@ -185,12 +185,12 @@ final class RemoteKeyboard extends LinearLayout {
LinearLayout line=ui.row();line.setGravity(Gravity.CENTER);
for(String value:values){
boolean shift=modifiers.contains(16);String shown=shift&&value.length()==1?shifted(value):value;
TextView key=button(shown,()->key(value));key.setPadding(0,0,0,0);
TextView key=button(L10n.tr(shown),()->key(value));key.setPadding(0,0,0,0);
key.setTextSize(value.length()>1?12:18);key.setSingleLine(true);
int modifier=modifier(value);boolean selected=modifier!=0&&modifiers.contains(modifier);key.setSelected(selected);
int color=selected?(dark()?0xFF596D8C:0xFFA8C7FA):value.length()>1||value.equals("⇧")||value.equals("⌫")?(dark()?0xFF30343B:0xFFD0D5DC):(dark()?0xFF3C4043:Color.WHITE);
StateListDrawable background=new StateListDrawable();background.addState(new int[]{android.R.attr.state_pressed},ui.background(dark()?0xFF68717D:0xFFBDC9D8,7));background.addState(new int[]{},ui.background(color,7));key.setBackground(background);
if(value.equals("⇧"))key.setContentDescription("Shift");else if(value.equals("⌫"))key.setContentDescription("退格");
if(value.equals("⇧"))key.setContentDescription("Shift");else if(value.equals("⌫"))key.setContentDescription(L10n.tr("退格"));
// Both penultimate rows allocate exactly 1/8 to Shift and Backspace.
boolean penultimate=values[0].equals("⇧");float weight=value.equals("空格")?4:1;
if(penultimate)weight=value.equals("⇧")||value.equals("⌫")?1:(6f/(values.length-2));
@@ -24,7 +24,7 @@ final class RemoteNetworkStatistics {
final Report report;final int fps,width,height;final double latency,rtt;
final String remoteEncoder,localDecoder;
Snapshot(Report report,int fps,int width,int height,double latency,double rtt,String remoteEncoder,String localDecoder){this.report=report;this.fps=fps;this.width=width;this.height=height;this.latency=latency;this.rtt=rtt;this.remoteEncoder=remoteEncoder;this.localDecoder=localDecoder;}
String mode(){return report==null?"—":report.mode==0?"P2P 直连":report.mode==1?"TURN 中继":"—";}
String mode(){return report==null?"—":report.mode==0?L10n.tr("P2P 直连"):report.mode==1?L10n.tr("TURN 中继"):"—";}
String resolution(){return width>0&&height>0?width+" × "+height:"—";}
}
private static final class Frame {
@@ -127,7 +127,7 @@ final class RemotePreviewStore {
new AtomicFile(new File(directory,key+".jpg")).delete();
synchronized(this){
boolean saved=index.edit().putStringSet("files",new HashSet<>(files)).commit();
if(!saved||new File(directory,key+".jpg").exists())cleanupError="部分预览图未能清除,请重试。";
if(!saved||new File(directory,key+".jpg").exists())cleanupError=L10n.tr("部分预览图未能清除,请重试。");
}
changed();
});changed();
@@ -139,7 +139,7 @@ final class RemotePreviewStore {
try{deleteTree(directory);}catch(IOException error){success=false;}
synchronized(this){
if(epoch!=generation)return;
cleaning=false;cleanupError=success?"":"部分预览图未能清除,请重试。";
cleaning=false;cleanupError=success?"":L10n.tr("部分预览图未能清除,请重试。");
}
changed();
});
@@ -45,9 +45,9 @@ final class RemotePrivacyScreen {
boolean fresh(long now) { return received && now - receivedAt <= 5000; }
boolean enabled(long now) { return fresh(now) && state == 3 && overlay && !remotePaused; }
String buttonTitle(long now) {
if (enabled(now)) return "隐私屏已开启";
if (fresh(now) && supported && state == 1 && !active()) return "隐私屏已关闭";
return "隐私屏";
if (enabled(now)) return L10n.tr("隐私屏已开启");
if (fresh(now) && supported && state == 1 && !active()) return L10n.tr("隐私屏已关闭");
return L10n.tr("隐私屏");
}
String feedback(long now) {
if (!desktopAvailable) return "";
@@ -71,25 +71,25 @@ final class RemotePrivacyScreen {
return ++requestId;
}
void failed(long id) {
if (pending && requestId == id) { pending = false; failure = "指令发送失败,请重试"; }
if (pending && requestId == id) { pending = false; failure = L10n.tr("指令发送失败,请重试"); }
}
void tick(long now) {
if (pending && now - requestedAt >= 15000) {
pending = false; failure = "请求超时,尚未确认电脑的隐私屏状态";
pending = false; failure = L10n.tr("请求超时,尚未确认电脑的隐私屏状态");
}
}
String status(long now) {
if (!failure.isEmpty()) return failure;
if (!received) return "等待电脑返回隐私屏状态;旧版电脑端可能不支持";
if (!fresh(now)) return "隐私屏状态已过期,等待电脑确认";
if (state == 2) return "正在开启隐私屏…";
if (state == 4) return "正在关闭隐私屏…";
if (pending) return "指令已发送,等待电脑确认…";
if (state == 5) return "隐私屏异常";
if (!supported || state == 0) return "隐私屏暂不可用";
if (!received) return L10n.tr("等待电脑返回隐私屏状态;旧版电脑端可能不支持");
if (!fresh(now)) return L10n.tr("隐私屏状态已过期,等待电脑确认");
if (state == 2) return L10n.tr("正在开启隐私屏…");
if (state == 4) return L10n.tr("正在关闭隐私屏…");
if (pending) return L10n.tr("指令已发送,等待电脑确认…");
if (state == 5) return L10n.tr("隐私屏异常");
if (!supported || state == 0) return L10n.tr("隐私屏暂不可用");
if (state == 3 && overlay && !remotePaused)
return "隐私屏已开启";
return state == 1 && !active() ? "隐私屏已关闭" : "隐私屏状态待确认";
return L10n.tr("隐私屏已开启");
return state == 1 && !active() ? L10n.tr("隐私屏已关闭") : L10n.tr("隐私屏状态待确认");
}
String detail(long now) {
return fresh(now) && (state == 0 || state == 5 || !supported) ? reason : "";
@@ -51,7 +51,7 @@ public final class RemoteSessionService extends Service {
@Override public void onCreate() {
super.onCreate(); running = this;
getSystemService(NotificationManager.class).createNotificationChannel(
new NotificationChannel(CHANNEL, "远程连接", NotificationManager.IMPORTANCE_LOW));
new NotificationChannel(CHANNEL, L10n.tr("远程连接"), NotificationManager.IMPORTANCE_LOW));
wakeLock = getSystemService(PowerManager.class).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CrossDesk:RemoteSession");
wakeLock.setReferenceCounted(false);
}
@@ -60,7 +60,7 @@ public final class RemoteSessionService extends Service {
if (value == null || value.ended || value.session.isClosed()) { stopSelf(startId); return START_NOT_STICKY; }
if (intent == null || !value.token.equals(intent.getStringExtra("session"))) return START_NOT_STICKY;
owned = value;
if (STOP.equals(intent.getAction())) { value.finish("已从通知断开远程连接"); return START_NOT_STICKY; }
if (STOP.equals(intent.getAction())) { value.finish(L10n.tr("已从通知断开远程连接")); return START_NOT_STICKY; }
try {
Notification notification = notification();
if (Build.VERSION.SDK_INT >= 34) startForeground(NOTIFICATION, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE);
@@ -68,7 +68,7 @@ public final class RemoteSessionService extends Service {
update();
} catch (RuntimeException error) {
Log.w("CrossDesk", "Background session foreground promotion failed", error);
value.finish("后台保活未能启动,请保持应用在前台后重新连接");
value.finish(L10n.tr("后台保活未能启动,请保持应用在前台后重新连接"));
}
return START_NOT_STICKY;
}
@@ -80,12 +80,12 @@ public final class RemoteSessionService extends Service {
.setData(android.net.Uri.parse("crossdesk-session:" + owned.token)).putExtra("session", owned.token);
PendingIntent stop = PendingIntent.getService(this, 1, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
return new Notification.Builder(this, CHANNEL).setSmallIcon(R.drawable.ic_remote_session)
.setContentTitle("CrossDesk 远程连接")
.setContentText(owned.background ? "后台保持连接,点按返回远程桌面" : "已启用后台保活,切换应用可继续保持连接")
.setContentTitle(L10n.tr("CrossDesk 远程连接"))
.setContentText(owned.background ? L10n.tr("后台保持连接,点按返回远程桌面") : L10n.tr("已启用后台保活,切换应用可继续保持连接"))
.setContentIntent(open).setOngoing(true).setOnlyAlertOnce(true).setShowWhen(false)
.setCategory(Notification.CATEGORY_SERVICE).setVisibility(Notification.VISIBILITY_PRIVATE)
.addAction(new Notification.Action.Builder(null, "返回", open).build())
.addAction(new Notification.Action.Builder(null, "断开", stop).build()).build();
.addAction(new Notification.Action.Builder(null, L10n.tr("返回"), open).build())
.addAction(new Notification.Action.Builder(null, L10n.tr("断开"), stop).build()).build();
}
@android.annotation.SuppressLint("WakelockTimeout") // Released on foreground return, disconnect, task removal and service destruction.
private void update() {
@@ -96,14 +96,14 @@ public final class RemoteSessionService extends Service {
}
private void releaseWakeLock() { if (wakeLock != null && wakeLock.isHeld()) wakeLock.release(); }
@Override public void onTaskRemoved(Intent rootIntent) {
if (current == owned && owned != null) owned.finish("远程任务已关闭");
if (current == owned && owned != null) owned.finish(L10n.tr("远程任务已关闭"));
stopSelf();
}
@Override public void onDestroy() {
if (running == this) running = null;
releaseWakeLock(); stopForeground(STOP_FOREGROUND_REMOVE);
BackgroundSession value = owned;
if (current == value && value != null) { current = null; value.finish("后台连接服务已停止,请重新连接"); }
if (current == value && value != null) { current = null; value.finish(L10n.tr("后台连接服务已停止,请重新连接")); }
super.onDestroy();
}
}
@@ -4,11 +4,11 @@ import org.json.JSONObject;
/** Same request/acknowledgement and rollback rules as the iOS controller. */
final class RemoteVideoSettings {
static final String[] PREFERENCES={"帧率优先","画质优先","平衡"};
static final String[] DETAILS={
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。",
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。",
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。"};
static String[] preferences(){return new String[]{L10n.tr("帧率优先"),L10n.tr("画质优先"),L10n.tr("平衡")};}
static String[] details(){return new String[]{
L10n.tr("优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。"),
L10n.tr("始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。"),
L10n.tr("兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。")};}
static final class Values {
final int quality,frameRate,preference;
Values(int quality,int frameRate,int preference){this.quality=quality;this.frameRate=frameRate;this.preference=preference;}
@@ -54,5 +54,5 @@ final class RemoteVideoSettings {
}catch(org.json.JSONException ignored){ }
}
void expire(long expiredId){if(pending&&expiredId==requestId){selection=applied;pending=false;failed=true;}}
String feedback(){return !supported?"连接建立后可调整,需被控端支持。":failed?"调整失败,请重试。":"";}
String feedback(){return !supported?L10n.tr("连接建立后可调整,需被控端支持。"):failed?L10n.tr("调整失败,请重试。"):"";}
}
@@ -104,7 +104,7 @@ final class RemoteVideoView extends FrameLayout implements SurfaceHolder.Callbac
}
return true;
});
setContentDescription("远程桌面:轻触点击,拖动移动,长按右键,双指捏合缩放,放大后双指拖动画面,原始比例下双指上下滑动滚动");
setContentDescription(L10n.tr("远程桌面:轻触点击,拖动移动,长按右键,双指捏合缩放,放大后双指拖动画面,原始比例下双指上下滑动滚动"));
}
void videoSize(int width, int height) {
if (width <= 0 || height <= 0) return;
@@ -244,7 +244,7 @@ final class RemoteVideoView extends FrameLayout implements SurfaceHolder.Callbac
private final Path arrow = new Path();
private final float arrowWidth = 12, arrowHeight = 15;
Cursor(Context context) {
super(context); setClickable(true); setContentDescription("远程桌面触控区域");
super(context); setClickable(true); setContentDescription(L10n.tr("远程桌面触控区域"));
// Match the iOS RemoteCursorArrowShape in density-independent units.
arrow.moveTo(arrowWidth*.08f,arrowHeight*.04f);
arrow.lineTo(arrowWidth*.08f,arrowHeight*.78f);
@@ -31,7 +31,7 @@ final class SegmentedControl extends LinearLayout {
setGravity(Gravity.CENTER_VERTICAL);setPadding(inset,inset,inset,inset);setBackground(ui.background(0xFFE9E9EF,MobileUi.SEGMENT_HEIGHT/2));paint.setColor(Color.WHITE);
for(int i=0;i<titles.length;i++){
final int index=i;TextView choice=ui.action(titles[i],MobileUi.INK,()->select(index));
choice.setTextSize(13);choice.setMinHeight(height);choice.setSelected(i==selected);choice.setEnabled(enabled);choice.setAlpha(enabled?1:.45f);
choice.setTextSize(13);choice.setSingleLine();choice.setAutoSizeTextTypeUniformWithConfiguration(9,13,1,android.util.TypedValue.COMPLEX_UNIT_SP);choice.setMinHeight(height);choice.setSelected(i==selected);choice.setEnabled(enabled);choice.setAlpha(enabled?1:.45f);
addView(choice,new LayoutParams(0,height,1));
}
}
@@ -59,8 +59,8 @@ final class SessionControls extends FrameLayout {
addView(keyboard,new LayoutParams(-1,ui.dp(44),Gravity.BOTTOM));
// This container must remain transparent to remote touch outside its controls.
waiting=ui.column();waiting.setMinimumWidth(ui.dp(200));waiting.setGravity(Gravity.CENTER);waiting.setPadding(ui.dp(20),ui.dp(20),ui.dp(20),ui.dp(20));waiting.setBackground(ui.background(0xB0202024,14));
ProgressBar progress=new ProgressBar(context);progress.setIndeterminateTintList(android.content.res.ColorStateList.valueOf(Color.WHITE));waiting.addView(progress,ui.size(28,28));ui.gap(waiting,12);TextView message=ui.text("等待远端画面…",14,false);message.setTextColor(Color.WHITE);message.setGravity(Gravity.CENTER);waiting.addView(message);addView(waiting,new LayoutParams(-2,-2,Gravity.CENTER));
orb=ui.iconButton("display","展开远程控制菜单",()->{if(menu==null)openMenu("controls");else closeMenu();});
ProgressBar progress=new ProgressBar(context);progress.setIndeterminateTintList(android.content.res.ColorStateList.valueOf(Color.WHITE));waiting.addView(progress,ui.size(28,28));ui.gap(waiting,12);TextView message=ui.text(L10n.tr("等待远端画面…"),14,false);message.setTextColor(Color.WHITE);message.setGravity(Gravity.CENTER);waiting.addView(message);addView(waiting,new LayoutParams(-2,-2,Gravity.CENTER));
orb=ui.iconButton("display",L10n.tr("展开远程控制菜单"),()->{if(menu==null)openMenu("controls");else closeMenu();});
ImageView brand=new ImageView(context);brand.setImageResource(R.drawable.mobile_brand);brand.setScaleType(ImageView.ScaleType.CENTER_CROP);brand.setBackground(ui.background(Color.WHITE,23));brand.setClipToOutline(true);brand.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
((FrameLayout)orb).removeAllViews();((FrameLayout)orb).addView(brand,new LayoutParams(ui.dp(46),ui.dp(46),Gravity.CENTER));
orb.setBackground(ui.background(0xFFF5F5F7,27));orb.setElevation(ui.dp(7));addView(orb,new LayoutParams(ui.dp(54),ui.dp(54)));
@@ -117,15 +117,15 @@ final class SessionControls extends FrameLayout {
if(transferLabel!=null){transferLabel.setText(transferStatus);transferLabel.setVisibility(transferStatus.isEmpty()?GONE:VISIBLE);}
if(saveFileButton!=null)saveFileButton.setVisibility(hasReceivedFile?VISIBLE:GONE);
}
private void closeMenu(){if(menu!=null){removeView(menu);menu=null;menuScroll=null;}if(shield!=null){removeView(shield);shield=null;}videoFeedback=transferLabel=saveFileButton=null;privacyFeedback=privacyCaption=null;privacyButton=null;privacyIcon=null;trafficCells=null;trafficRows=null;networkDetails=null;encryptionLock=encryptionOpen=null;settingRows.clear();orb.setContentDescription("展开远程控制菜单");positionMouse();}
private void closeMenu(){if(menu!=null){removeView(menu);menu=null;menuScroll=null;}if(shield!=null){removeView(shield);shield=null;}videoFeedback=transferLabel=saveFileButton=null;privacyFeedback=privacyCaption=null;privacyButton=null;privacyIcon=null;trafficCells=null;trafficRows=null;networkDetails=null;encryptionLock=encryptionOpen=null;settingRows.clear();orb.setContentDescription(L10n.tr("展开远程控制菜单"));positionMouse();}
private void openMenu(String page){
cancelMouseInput();
closeMenu();menuPage=page;shield=new View(getContext());shield.setOnClickListener(v->closeMenu());addView(shield,new LayoutParams(-1,-1));
menu=ui.column();menu.setPadding(ui.dp(12),ui.dp(12),ui.dp(12),ui.dp(12));ui.card(menu,18);menu.setElevation(ui.dp(12));
LinearLayout header=ui.row();
if(!page.equals("controls"))header.addView(ui.iconButton("back","返回控制栏",()->openMenu("controls")),ui.size(28,32));
TextView title=ui.text(page.equals("controls")?"● 已连接":page.equals("video")?"画面设置":"网络状态",14,true);
header.addView(title,new LinearLayout.LayoutParams(0,-2,1));header.addView(ui.iconButton("close","关闭控制栏",this::closeMenu),ui.size(30,32));menu.addView(header);ui.gap(menu,9);
if(!page.equals("controls"))header.addView(ui.iconButton("back",L10n.tr("返回控制栏"),()->openMenu("controls")),ui.size(28,32));
TextView title=ui.text(page.equals("controls")?L10n.tr("● 已连接"):page.equals("video")?L10n.tr("画面设置"):L10n.tr("网络状态"),14,true);
header.addView(title,new LinearLayout.LayoutParams(0,-2,1));header.addView(ui.iconButton("close",L10n.tr("关闭控制栏"),this::closeMenu),ui.size(30,32));menu.addView(header);ui.gap(menu,9);
if(page.equals("video")){videoFeedback=ui.text("",12,false);videoFeedback.setTextColor(SECONDARY);videoFeedback.setPadding(0,0,0,ui.dp(9));menu.addView(videoFeedback);}
ScrollView scroll=new ScrollView(getContext());menuScroll=scroll;menu.addView(scroll,new LinearLayout.LayoutParams(-1,0,1));LinearLayout body=ui.column();scroll.addView(body);
if(page.equals("network")){
@@ -133,7 +133,7 @@ final class SessionControls extends FrameLayout {
scroll.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);body.setPadding(0,0,ui.dp(6),0);
}
if(page.equals("controls")){
String[] labels={"键盘",mouseVisible?"收起鼠标":"虚拟鼠标",relative?"相对鼠标":"绝对鼠标","显示器","画面设置",muted?"静音":"声音","发送文件","网络状态","Ctrl+Alt+Del","剪贴板","隐私屏"};
String[] labels={L10n.tr("键盘"),mouseVisible?L10n.tr("收起鼠标"):L10n.tr("虚拟鼠标"),relative?L10n.tr("相对鼠标"):L10n.tr("绝对鼠标"),L10n.tr("显示器"),L10n.tr("画面设置"),muted?L10n.tr("静音"):L10n.tr("声音"),L10n.tr("发送文件"),L10n.tr("网络状态"),"Ctrl+Alt+Del",L10n.tr("剪贴板"),L10n.tr("隐私屏")};
String[] icons={"keyboard","mouse","mouse","display","sliders","audio","folder","chart","lock","clipboard","eye"};
Runnable[] clicks={()->{closeMenu();toggleKeyboard();},this::toggleMouse,actions::mouse,actions::display,()->openMenu("video"),actions::mute,actions::sendFile,()->openMenu("network"),actions::secureAttention,actions::clipboard,actions::togglePrivacy};
menuColumns=controlColumns();
@@ -143,22 +143,22 @@ final class SessionControls extends FrameLayout {
visibleControls.add(index);
}
int controlCount=visibleControls.size();
for(int row=0;row<(controlCount+menuColumns-1)/menuColumns;row++){LinearLayout line=ui.row();for(int col=0;col<menuColumns;col++){int position=row*menuColumns+col;if(position>=controlCount){View spacer=new View(getContext());LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(spacer,p);continue;}int index=visibleControls.get(position);LinearLayout control=ui.column();control.setGravity(Gravity.CENTER);control.setBackground(ui.background(0xFFF0F0F2,10));MobileUi.Icon icon=ui.icon(icons[index],INK);control.addView(icon,ui.size(19,19));ui.gap(control,4);TextView caption=ui.text(labels[index],10,false);caption.setSingleLine();caption.setGravity(Gravity.CENTER);control.addView(caption);control.setContentDescription(labels[index]);control.setClickable(true);control.setFocusable(true);control.setOnClickListener(v->clicks[index].run());
for(int row=0;row<(controlCount+menuColumns-1)/menuColumns;row++){LinearLayout line=ui.row();for(int col=0;col<menuColumns;col++){int position=row*menuColumns+col;if(position>=controlCount){View spacer=new View(getContext());LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(spacer,p);continue;}int index=visibleControls.get(position);LinearLayout control=ui.column();control.setGravity(Gravity.CENTER);control.setBackground(ui.background(0xFFF0F0F2,10));MobileUi.Icon icon=ui.icon(icons[index],INK);control.addView(icon,ui.size(19,19));ui.gap(control,4);TextView caption=ui.text(labels[index],10,false);caption.setMaxLines(2);caption.setAutoSizeTextTypeUniformWithConfiguration(8,10,1,android.util.TypedValue.COMPLEX_UNIT_SP);caption.setGravity(Gravity.CENTER);control.addView(caption);control.setContentDescription(labels[index]);control.setClickable(true);control.setFocusable(true);control.setOnClickListener(v->clicks[index].run());
if(index==10){privacyButton=control;privacyIcon=icon;privacyCaption=caption;}
LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(control,p);
}body.addView(line);if((row+1)*menuColumns<controlCount)ui.gap(body,7);}
privacyFeedback=ui.text("",11,false);privacyFeedback.setTextColor(SECONDARY);privacyFeedback.setPadding(0,ui.dp(7),0,0);privacyFeedback.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);body.addView(privacyFeedback);renderPrivacy();
ui.gap(body,7);transferLabel=ui.text(transferStatus,11,false);transferLabel.setTextColor(SECONDARY);transferLabel.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE);body.addView(transferLabel);
saveFileButton=ui.action("保存收到的文件",BLUE,actions::saveFile);body.addView(saveFileButton);renderFileStatus();
saveFileButton=ui.action(L10n.tr("保存收到的文件"),BLUE,actions::saveFile);body.addView(saveFileButton);renderFileStatus();
}else if(page.equals("network")){
networkPage(body);
}else{
settingRows.add(new SettingRow(body,0,"画面质量",new String[]{"低","中","高"},new int[]{0,1,2}));ui.gap(body,12);
settingRows.add(new SettingRow(body,1,"画面采集帧率",new String[]{"30 fps","60 fps"},new int[]{30,60}));ui.gap(body,12);
settingRows.add(new SettingRow(body,2,"画面偏好",RemoteVideoSettings.PREFERENCES,new int[]{0,1,2}));renderVideoSettings();
settingRows.add(new SettingRow(body,0,L10n.tr("画面质量"),new String[]{L10n.tr("低"),L10n.tr("中"),L10n.tr("高")},new int[]{0,1,2}));ui.gap(body,12);
settingRows.add(new SettingRow(body,1,L10n.tr("画面采集帧率"),new String[]{"30 fps","60 fps"},new int[]{30,60}));ui.gap(body,12);
settingRows.add(new SettingRow(body,2,L10n.tr("画面偏好"),RemoteVideoSettings.preferences(),new int[]{0,1,2}));renderVideoSettings();
}
ui.gap(menu,9);TextView disconnect=ui.action("断开连接",Color.WHITE,()->{closeMenu();closeKeyboard();actions.disconnect();});disconnect.setMinHeight(ui.dp(34));disconnect.setTextSize(14);disconnect.setBackground(ui.background(RED,8));menu.addView(disconnect,ui.size(-1,34));
addView(menu,new LayoutParams(panelWidth(getWidth()),panelHeight(getHeight())));orb.bringToFront();orb.setContentDescription("收起远程控制菜单");
ui.gap(menu,9);TextView disconnect=ui.action(L10n.tr("断开连接"),Color.WHITE,()->{closeMenu();closeKeyboard();actions.disconnect();});disconnect.setMinHeight(ui.dp(34));disconnect.setTextSize(14);disconnect.setBackground(ui.background(RED,8));menu.addView(disconnect,ui.size(-1,34));
addView(menu,new LayoutParams(panelWidth(getWidth()),panelHeight(getHeight())));orb.bringToFront();orb.setContentDescription(L10n.tr("收起远程控制菜单"));
positionMouse();
}
private void renderPrivacy(){
@@ -168,7 +168,7 @@ final class SessionControls extends FrameLayout {
long now=android.os.SystemClock.elapsedRealtime();privacy.tick(now);
if(privacyFeedback!=null){String feedback=privacy.feedback(now);privacyFeedback.setText(feedback);privacyFeedback.setVisibility(feedback.isEmpty()?GONE:VISIBLE);}
if(privacyButton==null)return;
boolean enabled=privacy.enabled(now);String action=privacy.active()?"关闭隐私屏":"开启隐私屏";
boolean enabled=privacy.enabled(now);String action=privacy.active()?L10n.tr("关闭隐私屏"):L10n.tr("开启隐私屏");
privacyIcon.setName(enabled?"eye_off":"eye");
privacyCaption.setText(privacy.buttonTitle(now));
privacyButton.setSelected(enabled);
@@ -183,7 +183,7 @@ final class SessionControls extends FrameLayout {
group=new SegmentedControl(ui,labels,selectionIndex(),settings.supported,index->actions.videoSettings(field,values[index]));
group.setPadding(ui.dp(2),ui.dp(2),ui.dp(2),ui.dp(2));body.addView(group);
for(int i=0;i<labels.length;i++){
TextView button=(TextView)group.getChildAt(i);button.setTextSize(12);button.setMinHeight(ui.dp(28));button.setSingleLine();button.setContentDescription(title+","+labels[i]);
TextView button=(TextView)group.getChildAt(i);button.setTextSize(12);button.setMinHeight(ui.dp(28));button.setSingleLine();button.setAutoSizeTextTypeUniformWithConfiguration(9,12,1,android.util.TypedValue.COMPLEX_UNIT_SP);button.setContentDescription(title+","+labels[i]);
button.setLayoutParams(new LinearLayout.LayoutParams(0,ui.dp(28),1));
}
}
@@ -197,12 +197,12 @@ final class SessionControls extends FrameLayout {
for(SettingRow row:settingRows)row.render();
}
private void networkPage(LinearLayout body){
trafficCells=new TextView[4][3];trafficRows=new LinearLayout[4];String[] names={"视频","音频","数据","合计"};
LinearLayout heading=trafficRow(body,"",new TextView[3],false);((TextView)heading.getChildAt(1)).setText("接收");((TextView)heading.getChildAt(2)).setText("发送");((TextView)heading.getChildAt(3)).setText("丢包率");
trafficCells=new TextView[4][3];trafficRows=new LinearLayout[4];String[] names={L10n.tr("视频"),L10n.tr("音频"),L10n.tr("数据"),L10n.tr("合计")};
LinearLayout heading=trafficRow(body,"",new TextView[3],false);((TextView)heading.getChildAt(1)).setText(L10n.tr("接收"));((TextView)heading.getChildAt(2)).setText(L10n.tr("发送"));((TextView)heading.getChildAt(3)).setText(L10n.tr("丢包率"));
for(int i=0;i<heading.getChildCount();i++)((TextView)heading.getChildAt(i)).setTextColor(SECONDARY);
for(int i=0;i<4;i++)trafficRows[i]=trafficRow(body,names[i],trafficCells[i],i==3);
ui.gap(body,10);ui.divider(body);ui.gap(body,10);
String[] titles={"帧率","分辨率","画面延时","连接延时(RTT)","连接方式","媒体加密","被控端编码器","控制端解码器"};networkDetails=new TextView[titles.length];
String[] titles={L10n.tr("帧率"),L10n.tr("分辨率"),L10n.tr("画面延时"),L10n.tr("连接延时(RTT)"),L10n.tr("连接方式"),L10n.tr("媒体加密"),L10n.tr("被控端编码器"),L10n.tr("控制端解码器")};networkDetails=new TextView[titles.length];
for(int i=0;i<titles.length;i++){LinearLayout row=ui.row();TextView title=ui.text(titles[i],12,false);title.setTextColor(SECONDARY);row.addView(title,i>=6?new LinearLayout.LayoutParams(-2,-2):new LinearLayout.LayoutParams(0,-2,1));
if(i==5){encryptionLock=ui.icon("lock",GREEN);encryptionOpen=ui.icon("unlock",SECONDARY);for(View icon:new View[]{encryptionLock,encryptionOpen}){LinearLayout.LayoutParams p=ui.size(14,14);p.rightMargin=ui.dp(4);row.addView(icon,p);}}
TextView value=ui.text("—",12,false);value.setFontFeatureSettings("tnum");
@@ -219,13 +219,13 @@ final class SessionControls extends FrameLayout {
}
private void renderNetwork(){
if(trafficCells==null)return;
String[] names={"视频","音频","数据","合计"};
String[] names={L10n.tr("视频"),L10n.tr("音频"),L10n.tr("数据"),L10n.tr("合计")};
for(int i=0;i<4;i++){
RemoteNetworkStatistics.Traffic traffic=network.report==null?null:network.report.traffic[i];
String in=RemoteNetworkStatistics.bitrate(traffic==null?-1:traffic.inbound),out=RemoteNetworkStatistics.bitrate(traffic==null?-1:traffic.outbound),loss=RemoteNetworkStatistics.loss(traffic==null?Double.NaN:traffic.loss);
trafficCells[i][0].setText(in);trafficCells[i][1].setText(out);trafficCells[i][2].setText(loss);trafficRows[i].setContentDescription(names[i]+",接收 "+in+",发送 "+out+",丢包率 "+loss);
trafficCells[i][0].setText(in);trafficCells[i][1].setText(out);trafficCells[i][2].setText(loss);trafficRows[i].setContentDescription(L10n.format("{0},接收 {1},发送 {2},丢包率 {3}",names[i],in,out,loss));
}
String[] values={network.fps+" FPS",network.resolution(),RemoteNetworkStatistics.latency(network.latency),RemoteNetworkStatistics.latency(network.rtt),network.mode(),network.report==null?"—":network.report.srtp?"SRTP 已启用":"未启用",network.remoteEncoder.isEmpty()?"—":network.remoteEncoder,network.localDecoder.isEmpty()?"—":network.localDecoder};
String[] values={network.fps+" FPS",network.resolution(),RemoteNetworkStatistics.latency(network.latency),RemoteNetworkStatistics.latency(network.rtt),network.mode(),network.report==null?"—":network.report.srtp?L10n.tr("SRTP 已启用"):L10n.tr("未启用"),network.remoteEncoder.isEmpty()?"—":network.remoteEncoder,network.localDecoder.isEmpty()?"—":network.localDecoder};
// Reassigning identical text every statistics tick restarts the marquee.
for(int i=0;i<values.length;i++)if(!android.text.TextUtils.equals(networkDetails[i].getText(),values[i]))networkDetails[i].setText(values[i]);networkDetails[5].setTextColor(network.report!=null&&network.report.srtp?GREEN:SECONDARY);
encryptionLock.setVisibility(network.report!=null&&network.report.srtp?VISIBLE:GONE);encryptionOpen.setVisibility(network.report!=null&&!network.report.srtp?VISIBLE:GONE);
@@ -29,11 +29,11 @@ final class VirtualMouseBar extends LinearLayout {
shell.setStroke(ui.dp(.5f), 0xFFD4DAE2);
setBackground(shell);
setMotionEventSplittingEnabled(true);
keys[0] = key("鼠标左键,按住并拖动,松开结束拖拽", 0);
keys[1] = key("鼠标滚轮,上下滑动,可滑出按键范围", 1);
keys[2] = key("鼠标右键,按住并拖动,松开结束拖拽", 2);
keys[0] = key(L10n.tr("鼠标左键,按住并拖动,松开结束拖拽"), 0);
keys[1] = key(L10n.tr("鼠标滚轮,上下滑动,可滑出按键范围"), 1);
keys[2] = key(L10n.tr("鼠标右键,按住并拖动,松开结束拖拽"), 2);
input.didCancel = this::resetTouches;
setContentDescription("虚拟鼠标");
setContentDescription(L10n.tr("虚拟鼠标"));
}
@SuppressLint("ClickableViewAccessibility") // Touch sends real down/up; performClick serves accessibility only.
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · 已完成"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" 传输失败"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" 台设备"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" 条 · "</string>
<string name="text_2c894057fc526576" formatted="false">" 条未读"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDesk 远程连接"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL 第 3 版"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC 告知"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFEC 许可与源码"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P 直连"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP 已启用"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"TURN 中继"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0} 传输失败"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"{0} 台设备"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} 条未读"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0},接收 {1},发送 {2},丢包率 {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● 已连接"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"下拉刷新"</string>
<string name="text_37772187ab854191" formatted="false">"丢包率"</string>
<string name="text_a567bdaa11367f26" formatted="false">"中"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。"</string>
<string name="text_aa9e366f68d3d097" formatted="false">"低"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"使用自定义服务器"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"保存密码"</string>
<string name="text_10019cef593819ed" formatted="false">"保存收到的文件"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"保存远程画面预览"</string>
<string name="text_0f197074373e38fd" formatted="false">"保存远程画面预览?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"信令连接中断,请重新连接"</string>
<string name="text_789f22a8b1b34797" formatted="false">"公告加载失败,请重试。"</string>
<string name="text_00554b17c432937e" formatted="false">"公告详情"</string>
<string name="text_2428420e407fe2e8" formatted="false">"共 "</string>
<string name="text_d977c7568cc5201e" formatted="false">"共 {0} 条 · {1} 条未读"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"关于"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"关于 CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"关于,有新版本"</string>
<string name="text_3fd47edce45b3603" formatted="false">"关闭"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"关闭控制栏"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"关闭隐私屏"</string>
<string name="text_7d466b69be42fc43" formatted="false">"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。"</string>
<string name="text_b92421822c213c2b" formatted="false">"分辨率"</string>
<string name="text_2f9daa828907b93f" formatted="false">"删除"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"删除公告"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"删除记录"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"删除这条公告?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"刷新公告"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"剪贴板"</string>
<string name="text_4927a53bcc886afb" formatted="false">"加载中…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"加载更多"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"升级提示"</string>
<string name="text_861521c9e107c648" formatted="false">"原生连接初始化失败"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"发送"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"发送 "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"发送后,在远程键盘中点击“粘贴”。接收的远端文本可手动复制到本机。"</string>
<string name="text_e7281fbca7af369c" formatted="false">"发送文件"</string>
<string name="text_ce6c02d19022e016" formatted="false">"发送文本"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"取消"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"取消连接"</string>
<string name="text_72814b15a6fa152c" formatted="false">"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。"</string>
<string name="text_e009c148d1890dac" formatted="false">"合计"</string>
<string name="text_81c92013219dd115" formatted="false">"同意并继续"</string>
<string name="text_409696774c9998ce" formatted="false">"后台保持连接,点按返回远程桌面"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"后台保活未能启动,请保持应用在前台"</string>
<string name="text_d96a21463557171d" formatted="false">"后台保活未能启动,请保持应用在前台后重新连接"</string>
<string name="text_952f7300de0a7bd9" formatted="false">"后台连接已结束,请重新连接"</string>
<string name="text_da904f715e1cff87" formatted="false">"后台连接服务已停止,请重新连接"</string>
<string name="text_0812359e18082071" formatted="false">"后续连接仍会保存新的预览图。"</string>
<string name="text_b9086662b1df1f20" formatted="false">"在线"</string>
<string name="text_39f2f444a042643e" formatted="false">"填写主机名或 IPv4 地址,不含协议和路径"</string>
<string name="text_473400815c0f687d" formatted="false">"声音"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"复制远端文本"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。"</string>
<string name="text_7ff73de85ec16d15" formatted="false">"媒体加密"</string>
<string name="text_22dc6217af77bc37" formatted="false">"字母数字"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"安全存储或证书初始化失败,请重试或清除应用数据"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"完成"</string>
<string name="text_678328f6f02881c7" formatted="false">"完成隐私授权后可检查更新"</string>
<string name="text_c4361f9abdba4882" formatted="false">"完成隐私授权后登记本机身份"</string>
<string name="text_c29b888e357d727d" formatted="false">"密码保存失败,本次连接仍可使用"</string>
<string name="text_3f31300a23afb390" formatted="false">"密码将加密保存在本机 Android Keystore 中"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"对端 ID"</string>
<string name="text_0512084e130984bc" formatted="false">"对端 ID "</string>
<string name="text_c5147d65960196b0" formatted="false">"对端 ID {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"将删除本机保存的所有远程预览图,保留连接记录和密码。"</string>
<string name="text_60cc99cc0013972e" formatted="false">"屏幕方向"</string>
<string name="text_9e7294d264afecb7" formatted="false">"展开远程控制菜单"</string>
<string name="text_9343612ee473edd6" formatted="false">"已从通知断开远程连接"</string>
<string name="text_9d32476fe83aef60" formatted="false">"已保存密码"</string>
<string name="text_1cbd391383283924" formatted="false">"已保存密码无法读取,请重新输入"</string>
<string name="text_6792b9f341380c68" formatted="false">"已保存的密码未能保留,请重新输入一次"</string>
<string name="text_74d3f0927d465047" formatted="false">"已发送 Ctrl+Alt+Del"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"已启用后台保活,切换应用可继续保持连接"</string>
<string name="text_506409ec08cb2a79" formatted="false">"已启用后台保活,可从连接通知返回或断开"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"已授权"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"已读"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"已连接 "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"已连接服务器"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"帧率"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"帧率优先"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"平衡"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"应用中的许可文件缺失或损坏,请重新安装应用。"</string>
<string name="text_3f2904435942cc0f" formatted="false">"开发版本包含尚未发布的修改,以下链接为基础版本源码。"</string>
<string name="text_4338afaad52ea445" formatted="false">"开启保存"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"开启隐私屏"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"开源组件"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"开源软件权利说明"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"当前已是最新版本"</string>
<string name="text_24a52788cf474bf6" formatted="false">"当前远端不支持直接输入此文本,请使用支持文本输入的被控端版本,或通过剪贴板发送"</string>
<string name="text_33f25b500697a7d9" formatted="false">"成功连接后,这里会显示最近的远程设备。"</string>
<string name="text_70341256d7317e7d" formatted="false">"我已阅读并同意《隐私政策》"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"所选文件(无法读取或空间不足)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"找不到该设备 ID"</string>
<string name="text_97b7b18df07b9787" formatted="false">"指令发送失败,请重试"</string>
<string name="text_cb782027ca5986c7" formatted="false">"指令已发送,等待电脑确认…"</string>
<string name="text_3ca40a586103f6b7" formatted="false">"授权状态"</string>
<string name="text_6e684586884ebee6" formatted="false">"接收"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"接收 "</string>
<string name="text_d16488b987723161" formatted="false">"控制端解码器"</string>
<string name="text_6b58198955259066" formatted="false">"撤回"</string>
<string name="text_c52e42ee856825db" formatted="false">"撤回后将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。"</string>
<string name="text_61f4788032ec596b" formatted="false">"撤回联网授权"</string>
<string name="text_e736e419008ac10a" formatted="false">"撤回联网授权?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"收起远程控制菜单"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"收起键盘"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"收起鼠标"</string>
<string name="text_5440f7424f2865bf" formatted="false">"数据"</string>
<string name="text_94244f41d0a41378" formatted="false">"文件保存失败,请重试"</string>
<string name="text_2553a785fef2a405" formatted="false">"文件已保存"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"文件选择已超时,会话已结束"</string>
<string name="text_985a5c380be49f4f" formatted="false">"文本超过 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"断开"</string>
<string name="text_09b1395f50589327" formatted="false">"断开后将返回首页"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"断开远程连接?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"断开连接"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"新版本"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"新版本可用:v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"新版本可用:v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"无未读公告"</string>
<string name="text_dca2c418ac967366" formatted="false">"无法保存本地阅读状态,请重试。"</string>
<string name="text_5c92da76360056cc" formatted="false">"无法删除这条公告,请重试。"</string>
<string name="text_615709dd3129313c" formatted="false">"无法发起远程连接"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"无法读取开源许可"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"显示器"</string>
<string name="text_ffe21a671190323e" formatted="false">"显示密码"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"暂不同意"</string>
<string name="text_6d9a7758610a2432" formatted="false">"暂无公告"</string>
<string name="text_0b290c6600d92f43" formatted="false">"最近连接"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"服务器"</string>
<string name="text_dfe408e686175095" formatted="false">"服务器 TLS 证书校验失败"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"服务器地址"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"服务器登录失败,请检查服务器地址和网络"</string>
<string name="text_d270868960e68629" formatted="false">"服务器端口"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"服务器连接超时,请检查服务器地址和网络"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"未启用"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"未找到可打开链接的浏览器"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"未授权"</string>
<string name="text_4d0680f9efaef147" formatted="false">"未读"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"未连接服务器 · 点击重试"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。"</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"本机 ID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"本机 ID {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"本版本源码与构建说明"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"本版本源码信息暂不可用。"</string>
<string name="text_f0e274aa130e08a2" formatted="false">"松开刷新"</string>
<string name="text_47077144715b7074" formatted="false">"构建说明"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"构建配方与项目补丁"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"查看源码"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"检查失败,请稍后重试"</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"检查更新"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"正在关闭隐私屏…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"正在建立安全连接…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"正在开启隐私屏…"</string>
<string name="text_91834aebecabcbc3" formatted="false">"正在检查更新…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"正在清除…"</string>
<string name="text_0e6336c8fe918730" formatted="false">"正在获取本机 ID…"</string>
<string name="text_faebd20c2801fd0f" formatted="false">"正在读取所选文件…"</string>
<string name="text_694186911ae3e4da" formatted="false">"正在连接"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"正在连接信令服务器…"</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"正在连接服务器…"</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"正在重连服务器…"</string>
<string name="text_4e4fce649fc00238" formatted="false">"此设备没有可用的文件选择器"</string>
<string name="text_bce2377283c2455a" formatted="false">"清除"</string>
<string name="text_ee0e76b8507212de" formatted="false">"清除预览图"</string>
<string name="text_ecc809e44637de18" formatted="false">"清除预览图?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"源码与构建说明"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"源码版本"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"版本"</string>
<string name="text_16410ee9663fad10" formatted="false">"版权与许可声明"</string>
<string name="text_11de77e1e41e680f" formatted="false">"电脑键盘"</string>
<string name="text_20f57900234debd7" formatted="false">"画质优先"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"画面偏好"</string>
<string name="text_82a5600c470179fa" formatted="false">"画面延时"</string>
<string name="text_33b458faaf967e15" formatted="false">"画面设置"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"画面质量"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"画面采集帧率"</string>
<string name="text_66062971234f422d" formatted="false">"相对位置"</string>
<string name="text_c421dc051bea3f11" formatted="false">"相对鼠标"</string>
<string name="text_de32e20193ad1042" formatted="false">"知道了"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"确定"</string>
<string name="text_36f33adaf0942634" formatted="false">"确认"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"确认连接"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"离线"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"空格"</string>
<string name="text_471a579e8d87faac" formatted="false">"端口范围为 1–65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"符号功能"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"等待电脑返回隐私屏状态;旧版电脑端可能不支持"</string>
<string name="text_5ee880e2524a983a" formatted="false">"等待远端屏幕信息"</string>
<string name="text_674c723a85ee7f57" formatted="false">"等待远端画面…"</string>
<string name="text_119f0483da4756c4" formatted="false">"等待隐私授权"</string>
<string name="text_7916071c1a8ee458" formatted="false">"系统键盘"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"绝对位置"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"绝对鼠标"</string>
<string name="text_872428a7210d5e0c" formatted="false">"网络状态"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"自定义"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"虚拟鼠标"</string>
<string name="text_62c199f37bf95869" formatted="false">"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。"</string>
<string name="text_a569ce729c1d6e19" formatted="false">"被控端版本过低,请升级到最新版本。"</string>
<string name="text_d814476826827b78" formatted="false">"被控端编码器"</string>
<string name="text_488402883e27f6f0" formatted="false">"要发送到远端的文本"</string>
<string name="text_c20f7618d330a854" formatted="false">"视频"</string>
<string name="text_310c625425f014d8" formatted="false">"许可"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"许可与版权声明"</string>
<string name="text_e24134b643a17975" formatted="false">"许可与版权声明可离线查阅。"</string>
<string name="text_5e7628f043ce71d0" formatted="false">"设备身份保存失败,请检查安全存储"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"设备身份及选择保存的访问密码使用 Android Keystore 加密。连接记录仅保存在本机,不纳入设备备份。剪贴板内容在结束会话时清除。离开应用后通过画中画或连接通知保持会话,可在应用或通知中断开。"</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"设置"</string>
<string name="text_4f8771952222a011" formatted="false">"设置,有新版本"</string>
<string name="text_f4f712d5be91721a" formatted="false">"访问密码"</string>
<string name="text_dd6c465847af1b60" formatted="false">"访问密码错误,请重新输入"</string>
<string name="text_defba1d2989bfab4" formatted="false">"证书校验失败 · 点击重试"</string>
<string name="text_44f3d4d52347095a" formatted="false">"请先完成联网授权"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"请在远端启用 CrossDesk 系统服务后重试"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"请求超时,尚未确认电脑的隐私屏状态"</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"请等待当前文件发送完成"</string>
<string name="text_9305d9d72b06110d" formatted="false">"请输入 1–65535 范围内的端口"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"请输入 9 位设备 ID"</string>
<string name="text_285a0714894d2899" formatted="false">"请输入有效访问密码"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"调整失败,请重试。"</string>
<string name="text_a762e7e650e285fa" formatted="false">"软件许可"</string>
<string name="text_572cf45ba43634b3" formatted="false">"返回"</string>
<string name="text_7565f94031582c7c" formatted="false">"返回控制栏"</string>
<string name="text_55113b6925c362fd" formatted="false">"还没有连接记录"</string>
<string name="text_3aa527b2106cc987" formatted="false">"这条公告已更新或删除,请返回公告列表查看。"</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"远程任务已关闭"</string>
<string name="text_c255841f68096d3b" formatted="false">"远程桌面"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"远程桌面触控区域"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"远程桌面:轻触点击,拖动移动,长按右键,双指捏合缩放,放大后双指拖动画面,原始比例下双指上下滑动滚动"</string>
<string name="text_3535087994d050de" formatted="false">"远程电脑"</string>
<string name="text_ed2e03072ce131de" formatted="false">"远程窗口已关闭,或后台保活未能启动,会话已结束"</string>
<string name="text_678b783fb578172b" formatted="false">"远程连接"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"远端密码错误"</string>
<string name="text_a2ce499295e358ae" formatted="false">"远端已关闭连接"</string>
<string name="text_176e117df3ace734" formatted="false">"远端当前不可用"</string>
<string name="text_d2c0694243f93249" formatted="false">"远端访问密码已失效,请重新连接"</string>
<string name="text_a5574109f0208e89" formatted="false">"连接"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"连接 "</string>
<string name="text_89c4766afc85125f" formatted="false">"连接失败"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"连接失败 · 点击重试"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"连接已断开"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"连接已结束"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"连接延时(RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"连接建立后可调整,需被控端支持。"</string>
<string name="text_2123a9ed718f2c40" formatted="false">"连接建立后锁定横屏"</string>
<string name="text_f741e0536210eafa" formatted="false">"连接提示"</string>
<string name="text_485a26050ce57431" formatted="false">"连接方式"</string>
<string name="text_5a048351a77fe88d" formatted="false">"连接服务器"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"连接服务器后可获取最新公告。"</string>
<string name="text_32cd27b5a72515f3" formatted="false">"连接超时,请检查设备 ID、服务器及远端在线状态"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"连接远程桌面"</string>
<string name="text_d5407b4f3c666681" formatted="false">"退格"</string>
<string name="text_e02926784fcedb91" formatted="false">"通知公告"</string>
<string name="text_465a092680d132b3" formatted="false">"部分预览图未能清除,请重试。"</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"重试"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"重试保存阅读状态"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"锁定"</string>
<string name="text_864200bbb241e067" formatted="false">"键盘"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"阅读并授权"</string>
<string name="text_86651d17a401c55b" formatted="false">"隐私"</string>
<string name="text_26c6e011e343708c" formatted="false">"隐私与授权"</string>
<string name="text_6cad42fd3da85585" formatted="false">"隐私屏"</string>
<string name="text_e157fafb76aad954" formatted="false">"隐私屏已关闭"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"隐私屏已开启"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"隐私屏已被解锁"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"隐私屏异常"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"隐私屏暂不可用"</string>
<string name="text_4d59be3c75189032" formatted="false">"隐私屏状态已过期,等待电脑确认"</string>
<string name="text_1674532c70ed1db2" formatted="false">"隐私屏状态待确认"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"隐私政策"</string>
<string name="text_554e957e8e3c00af" formatted="false">"隐私政策暂时无法读取,请拒绝联网并重新安装应用。"</string>
<string name="text_7db2e189ecb01065" formatted="false">"静音"</string>
<string name="text_296c632ec857a0ba" formatted="false">"音频"</string>
<string name="text_0cadb49096a382e0" formatted="false">"项目主页"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。"</string>
<string name="text_b1c27820fec23edb" formatted="false">"高"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"默认"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。"</string>
<string name="text_1184a97edd8e1646" formatted="false">"鼠标右键,按住并拖动,松开结束拖拽"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"鼠标左键,按住并拖动,松开结束拖拽"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"鼠标控制"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"鼠标滚轮,上下滑动,可滑出按键范围"</string>
<string name="text_9aa238fc6121dede" formatted="false">",丢包率 "</string>
<string name="text_0b5d16c21095fe10" formatted="false">",发送 "</string>
<string name="text_ba9a792d063a8c86" formatted="false">",已保存密码"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">",接收 "</string>
<string name="text_42237b5ec70c26f3" formatted="false">",点击重试"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · 已完成"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" 傳輸失敗"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" 臺裝置"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" 條 · "</string>
<string name="text_2c894057fc526576" formatted="false">" 條未讀"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDesk 遠端連線"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL 第 3 版"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC 告知"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFEC 許可與原始碼"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P 直連"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP 已啟用"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"TURN 中繼"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0} 傳輸失敗"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"{0} 臺裝置"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} 條未讀"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0},接收 {1},傳送 {2},丟包率 {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● 已連線"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"下拉重新整理"</string>
<string name="text_37772187ab854191" formatted="false">"丟包率"</string>
<string name="text_a567bdaa11367f26" formatted="false">"中"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"優先保持畫面流暢,頻寬或編碼壓力較大時會更積極地降低解析度。"</string>
<string name="text_aa9e366f68d3d097" formatted="false">"低"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"使用自定義伺服器"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"儲存密碼"</string>
<string name="text_10019cef593819ed" formatted="false">"儲存收到的檔案"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"儲存遠端畫面預覽"</string>
<string name="text_0f197074373e38fd" formatted="false">"儲存遠端畫面預覽?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"信令連線中斷,請重新連線"</string>
<string name="text_789f22a8b1b34797" formatted="false">"公告載入失敗,請重試。"</string>
<string name="text_00554b17c432937e" formatted="false">"公告詳情"</string>
<string name="text_2428420e407fe2e8" formatted="false">"共 "</string>
<string name="text_d977c7568cc5201e" formatted="false">"共 {0} 條 · {1} 條未讀"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"關於"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"關於 CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"關於,有新版本"</string>
<string name="text_3fd47edce45b3603" formatted="false">"關閉"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"關閉控制欄"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"關閉隱私屏"</string>
<string name="text_7d466b69be42fc43" formatted="false">"兼顧清晰度和流暢度,只在持續壓力下逐步降低解析度。"</string>
<string name="text_b92421822c213c2b" formatted="false">"解析度"</string>
<string name="text_2f9daa828907b93f" formatted="false">"刪除"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"刪除公告"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"刪除記錄"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"刪除這條公告?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"重新整理公告"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"剪貼簿"</string>
<string name="text_4927a53bcc886afb" formatted="false">"載入中…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"載入更多"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"更新提示"</string>
<string name="text_861521c9e107c648" formatted="false">"原生連線初始化失敗"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"傳送"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"傳送 "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"傳送後,在遠端鍵盤中點選“貼上”。接收的遠端文字可手動複製到本機。"</string>
<string name="text_e7281fbca7af369c" formatted="false">"傳送檔案"</string>
<string name="text_ce6c02d19022e016" formatted="false">"傳送文字"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"取消"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"取消連線"</string>
<string name="text_72814b15a6fa152c" formatted="false">"各元件遵循所列開源許可證,其授予的權利不受本應用普通使用條款限制。檢視原始碼需要網路連線。"</string>
<string name="text_e009c148d1890dac" formatted="false">"合計"</string>
<string name="text_81c92013219dd115" formatted="false">"同意並繼續"</string>
<string name="text_409696774c9998ce" formatted="false">"後臺保持連線,點按返回遠端桌面"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"後臺保活未能啟動,請保持應用在前臺"</string>
<string name="text_d96a21463557171d" formatted="false">"後臺保活未能啟動,請保持應用在前臺後重新連線"</string>
<string name="text_952f7300de0a7bd9" formatted="false">"後臺連線已結束,請重新連線"</string>
<string name="text_da904f715e1cff87" formatted="false">"後臺連線服務已停止,請重新連線"</string>
<string name="text_0812359e18082071" formatted="false">"後續連線仍會儲存新的預覽圖。"</string>
<string name="text_b9086662b1df1f20" formatted="false">"線上"</string>
<string name="text_39f2f444a042643e" formatted="false">"填寫主機名或 IPv4 地址,不含協議和路徑"</string>
<string name="text_473400815c0f687d" formatted="false">"聲音"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"複製遠端文字"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"始終保持較高解析度,頻寬不足時透過降低幀率來保證畫面清晰度。"</string>
<string name="text_7ff73de85ec16d15" formatted="false">"媒體加密"</string>
<string name="text_22dc6217af77bc37" formatted="false">"字母數字"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"安全儲存或證書初始化失敗,請重試或清除應用資料"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"完成"</string>
<string name="text_678328f6f02881c7" formatted="false">"完成隱私授權後可檢查更新"</string>
<string name="text_c4361f9abdba4882" formatted="false">"完成隱私授權後登記本機身份"</string>
<string name="text_c29b888e357d727d" formatted="false">"密碼儲存失敗,本次連線仍可使用"</string>
<string name="text_3f31300a23afb390" formatted="false">"密碼將加密儲存在本機 Android Keystore 中"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"對端 ID"</string>
<string name="text_0512084e130984bc" formatted="false">"對端 ID "</string>
<string name="text_c5147d65960196b0" formatted="false">"對端 ID {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"將刪除本機儲存的所有遠端預覽圖,保留連線記錄和密碼。"</string>
<string name="text_60cc99cc0013972e" formatted="false">"螢幕方向"</string>
<string name="text_9e7294d264afecb7" formatted="false">"展開遠端控制選單"</string>
<string name="text_9343612ee473edd6" formatted="false">"已從通知斷開遠端連線"</string>
<string name="text_9d32476fe83aef60" formatted="false">"已儲存密碼"</string>
<string name="text_1cbd391383283924" formatted="false">"已儲存密碼無法讀取,請重新輸入"</string>
<string name="text_6792b9f341380c68" formatted="false">"已儲存的密碼未能保留,請重新輸入一次"</string>
<string name="text_74d3f0927d465047" formatted="false">"已傳送 Ctrl+Alt+Del"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"已啟用後臺保活,切換應用可繼續保持連線"</string>
<string name="text_506409ec08cb2a79" formatted="false">"已啟用後臺保活,可從連線通知返回或斷開"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"已授權"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"已讀"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"已連線 "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"已連線伺服器"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"幀率"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"影格率優先"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"平衡"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"應用中的許可檔案缺失或損壞,請重新安裝應用。"</string>
<string name="text_3f2904435942cc0f" formatted="false">"開發版本包含尚未釋出的修改,以下連結為基礎版本原始碼。"</string>
<string name="text_4338afaad52ea445" formatted="false">"啟用儲存"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"開啟隱私屏"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"開源元件"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"開源軟體權利說明"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"當前已是最新版本"</string>
<string name="text_24a52788cf474bf6" formatted="false">"當前遠端不支援直接輸入此文字,請使用支援文字輸入的被控端版本,或透過剪貼簿傳送"</string>
<string name="text_33f25b500697a7d9" formatted="false">"成功連線後,這裡會顯示最近的遠端裝置。"</string>
<string name="text_70341256d7317e7d" formatted="false">"我已閱讀並同意《隱私政策》"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"所選檔案(無法讀取或空間不足)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"找不到該裝置 ID"</string>
<string name="text_97b7b18df07b9787" formatted="false">"指令傳送失敗,請重試"</string>
<string name="text_cb782027ca5986c7" formatted="false">"指令已傳送,等待電腦確認…"</string>
<string name="text_3ca40a586103f6b7" formatted="false">"授權狀態"</string>
<string name="text_6e684586884ebee6" formatted="false">"接收"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"接收 "</string>
<string name="text_d16488b987723161" formatted="false">"控制端解碼器"</string>
<string name="text_6b58198955259066" formatted="false">"撤回"</string>
<string name="text_c52e42ee856825db" formatted="false">"撤回後將停止遠端連線、關閉畫面預覽儲存並清除已有預覽。連線記錄和密碼會保留,重新連線前需再次授權。"</string>
<string name="text_61f4788032ec596b" formatted="false">"撤回聯網授權"</string>
<string name="text_e736e419008ac10a" formatted="false">"撤回聯網授權?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"收起遠端控制選單"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"收起鍵盤"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"收起滑鼠"</string>
<string name="text_5440f7424f2865bf" formatted="false">"資料"</string>
<string name="text_94244f41d0a41378" formatted="false">"檔案儲存失敗,請重試"</string>
<string name="text_2553a785fef2a405" formatted="false">"檔案已儲存"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"檔案選擇已超時,會話已結束"</string>
<string name="text_985a5c380be49f4f" formatted="false">"文字超過 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"斷開"</string>
<string name="text_09b1395f50589327" formatted="false">"斷開後將返回首頁"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"斷開遠端連線?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"中斷連線"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"新版本"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"新版本可用:v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"新版本可用:v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"無未讀公告"</string>
<string name="text_dca2c418ac967366" formatted="false">"無法儲存本地閱讀狀態,請重試。"</string>
<string name="text_5c92da76360056cc" formatted="false">"無法刪除這條公告,請重試。"</string>
<string name="text_615709dd3129313c" formatted="false">"無法發起遠端連線"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"無法讀取開源許可"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"顯示器"</string>
<string name="text_ffe21a671190323e" formatted="false">"顯示密碼"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"暫不同意"</string>
<string name="text_6d9a7758610a2432" formatted="false">"暫無公告"</string>
<string name="text_0b290c6600d92f43" formatted="false">"最近連線"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"伺服器"</string>
<string name="text_dfe408e686175095" formatted="false">"伺服器 TLS 證書校驗失敗"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"伺服器地址"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"伺服器登入失敗,請檢查伺服器地址和網路"</string>
<string name="text_d270868960e68629" formatted="false">"伺服器埠"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"伺服器連線超時,請檢查伺服器地址和網路"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"未啟用"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"未找到可開啟連結的瀏覽器"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"未授權"</string>
<string name="text_4d0680f9efaef147" formatted="false">"未讀"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"未連線伺服器 · 點選重試"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"本應用使用 OpenFEC 提供前向糾錯功能,遵循 CeCILL-C 1.0 許可。該許可規定了有限保證及責任限制,詳情見許可全文。"</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"本機 ID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"本機 ID {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"本版本原始碼與構建說明"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"本版本原始碼資訊暫不可用。"</string>
<string name="text_f0e274aa130e08a2" formatted="false">"鬆開重新整理"</string>
<string name="text_47077144715b7074" formatted="false">"構建說明"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"構建配方與專案補丁"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"檢視原始碼"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"檢查失敗,請稍後重試"</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"檢查更新"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"正在關閉隱私屏…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"正在建立安全連線…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"正在開啟隱私屏…"</string>
<string name="text_91834aebecabcbc3" formatted="false">"正在檢查更新…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"正在清除…"</string>
<string name="text_0e6336c8fe918730" formatted="false">"正在獲取本機 ID…"</string>
<string name="text_faebd20c2801fd0f" formatted="false">"正在讀取所選檔案…"</string>
<string name="text_694186911ae3e4da" formatted="false">"正在連線"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"正在連線信令伺服器…"</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"正在連線伺服器…"</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"正在重連伺服器…"</string>
<string name="text_4e4fce649fc00238" formatted="false">"此裝置沒有可用的檔案選擇器"</string>
<string name="text_bce2377283c2455a" formatted="false">"清除"</string>
<string name="text_ee0e76b8507212de" formatted="false">"清除預覽圖"</string>
<string name="text_ecc809e44637de18" formatted="false">"清除預覽圖?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"原始碼與構建說明"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"原始碼版本"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"版本"</string>
<string name="text_16410ee9663fad10" formatted="false">"版權與許可宣告"</string>
<string name="text_11de77e1e41e680f" formatted="false">"電腦鍵盤"</string>
<string name="text_20f57900234debd7" formatted="false">"畫質優先"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"畫面偏好"</string>
<string name="text_82a5600c470179fa" formatted="false">"畫面延遲"</string>
<string name="text_33b458faaf967e15" formatted="false">"畫面設定"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"畫面質量"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"畫面採集幀率"</string>
<string name="text_66062971234f422d" formatted="false">"相對位置"</string>
<string name="text_c421dc051bea3f11" formatted="false">"相對滑鼠"</string>
<string name="text_de32e20193ad1042" formatted="false">"知道了"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"確定"</string>
<string name="text_36f33adaf0942634" formatted="false">"確認"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"確認連線"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"離線"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"空白鍵"</string>
<string name="text_471a579e8d87faac" formatted="false">"埠範圍為 1–65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"符號功能"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"等待電腦返回隱私屏狀態;舊版電腦端可能不支援"</string>
<string name="text_5ee880e2524a983a" formatted="false">"等待遠端螢幕資訊"</string>
<string name="text_674c723a85ee7f57" formatted="false">"等待遠端畫面…"</string>
<string name="text_119f0483da4756c4" formatted="false">"等待隱私授權"</string>
<string name="text_7916071c1a8ee458" formatted="false">"系統鍵盤"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"絕對位置"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"絕對滑鼠"</string>
<string name="text_872428a7210d5e0c" formatted="false">"網路狀態"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"自定義"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"虛擬滑鼠"</string>
<string name="text_62c199f37bf95869" formatted="false">"被控端已透過組合鍵解除隱私屏。點選“鎖定”將重新開啟隱私屏,並禁止本機使用組合鍵解鎖。"</string>
<string name="text_a569ce729c1d6e19" formatted="false">"被控端版本過低,請升級到最新版本。"</string>
<string name="text_d814476826827b78" formatted="false">"被控端編碼器"</string>
<string name="text_488402883e27f6f0" formatted="false">"要傳送到遠端的文字"</string>
<string name="text_c20f7618d330a854" formatted="false">"影片"</string>
<string name="text_310c625425f014d8" formatted="false">"許可"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"許可與版權宣告"</string>
<string name="text_e24134b643a17975" formatted="false">"許可與版權宣告可離線查閱。"</string>
<string name="text_5e7628f043ce71d0" formatted="false">"裝置身份儲存失敗,請檢查安全儲存"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"裝置身份及選擇儲存的訪問密碼使用 Android Keystore 加密。連線記錄僅儲存在本機,不納入裝置備份。剪貼簿內容在結束會話時清除。離開應用後透過畫中畫或連線通知保持會話,可在應用或通知中斷開。"</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"設定"</string>
<string name="text_4f8771952222a011" formatted="false">"設定,有新版本"</string>
<string name="text_f4f712d5be91721a" formatted="false">"訪問密碼"</string>
<string name="text_dd6c465847af1b60" formatted="false">"訪問密碼錯誤,請重新輸入"</string>
<string name="text_defba1d2989bfab4" formatted="false">"證書校驗失敗 · 點選重試"</string>
<string name="text_44f3d4d52347095a" formatted="false">"請先完成聯網授權"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"請在遠端啟用 CrossDesk 系統服務後重試"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"請求超時,尚未確認電腦的隱私屏狀態"</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"請等待當前檔案傳送完成"</string>
<string name="text_9305d9d72b06110d" formatted="false">"請輸入 1–65535 範圍內的埠"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"請輸入 9 位裝置 ID"</string>
<string name="text_285a0714894d2899" formatted="false">"請輸入有效訪問密碼"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"調整失敗,請重試。"</string>
<string name="text_a762e7e650e285fa" formatted="false">"軟體許可"</string>
<string name="text_572cf45ba43634b3" formatted="false">"返回"</string>
<string name="text_7565f94031582c7c" formatted="false">"返回控制欄"</string>
<string name="text_55113b6925c362fd" formatted="false">"還沒有連線記錄"</string>
<string name="text_3aa527b2106cc987" formatted="false">"這條公告已更新或刪除,請返回公告列表檢視。"</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"遠端任務已關閉"</string>
<string name="text_c255841f68096d3b" formatted="false">"遠端桌面"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"遠端桌面觸控區域"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"遠端桌面:輕觸點選,拖動移動,長按右鍵,雙指捏合縮放,放大後雙指拖動畫面,原始比例下雙指上下滑動滾動"</string>
<string name="text_3535087994d050de" formatted="false">"遠端電腦"</string>
<string name="text_ed2e03072ce131de" formatted="false">"遠端視窗已關閉,或後臺保活未能啟動,會話已結束"</string>
<string name="text_678b783fb578172b" formatted="false">"遠端連線"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"遠端密碼錯誤"</string>
<string name="text_a2ce499295e358ae" formatted="false">"遠端已關閉連線"</string>
<string name="text_176e117df3ace734" formatted="false">"遠端當前不可用"</string>
<string name="text_d2c0694243f93249" formatted="false">"遠端訪問密碼已失效,請重新連線"</string>
<string name="text_a5574109f0208e89" formatted="false">"連線"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"連線 "</string>
<string name="text_89c4766afc85125f" formatted="false">"連線失敗"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"連線失敗 · 點選重試"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"連線已斷開"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"連線已結束"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"連線延時(RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"連線建立後可調整,需被控端支援。"</string>
<string name="text_2123a9ed718f2c40" formatted="false">"連線建立後鎖定橫屏"</string>
<string name="text_f741e0536210eafa" formatted="false">"連線提示"</string>
<string name="text_485a26050ce57431" formatted="false">"連線方式"</string>
<string name="text_5a048351a77fe88d" formatted="false">"連線伺服器"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"連線伺服器後可獲取最新公告。"</string>
<string name="text_32cd27b5a72515f3" formatted="false">"連線超時,請檢查裝置 ID、伺服器及遠端線上狀態"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"連線遠端桌面"</string>
<string name="text_d5407b4f3c666681" formatted="false">"退格"</string>
<string name="text_e02926784fcedb91" formatted="false">"通知公告"</string>
<string name="text_465a092680d132b3" formatted="false">"部分預覽圖未能清除,請重試。"</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"重試"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"重試儲存閱讀狀態"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"鎖定"</string>
<string name="text_864200bbb241e067" formatted="false">"鍵盤"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"閱讀並授權"</string>
<string name="text_86651d17a401c55b" formatted="false">"隱私"</string>
<string name="text_26c6e011e343708c" formatted="false">"隱私與授權"</string>
<string name="text_6cad42fd3da85585" formatted="false">"隱私屏"</string>
<string name="text_e157fafb76aad954" formatted="false">"隱私屏已關閉"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"隱私屏已開啟"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"隱私屏已被解鎖"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"隱私屏異常"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"隱私屏暫不可用"</string>
<string name="text_4d59be3c75189032" formatted="false">"隱私屏狀態已過期,等待電腦確認"</string>
<string name="text_1674532c70ed1db2" formatted="false">"隱私屏狀態待確認"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"隱私政策"</string>
<string name="text_554e957e8e3c00af" formatted="false">"隱私政策暫時無法讀取,請拒絕聯網並重新安裝應用。"</string>
<string name="text_7db2e189ecb01065" formatted="false">"靜音"</string>
<string name="text_296c632ec857a0ba" formatted="false">"音訊"</string>
<string name="text_0cadb49096a382e0" formatted="false">"專案主頁"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"預覽可能包含遠端電腦上的個人或工作資訊。開啟後,每次連線會自動將一張畫面儲存在本機並顯示在最近連線中。你可以隨時關閉並清除。"</string>
<string name="text_b1c27820fec23edb" formatted="false">"高"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"預設"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"預設關閉。開啟後,手機連線時進入並保持橫屏;關閉時跟隨裝置方向。平板和多視窗不受影響。"</string>
<string name="text_1184a97edd8e1646" formatted="false">"滑鼠右鍵,按住並拖動,鬆開結束拖拽"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"滑鼠左鍵,按住並拖動,鬆開結束拖拽"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"滑鼠控制"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"滑鼠滾輪,上下滑動,可滑出按鍵範圍"</string>
<string name="text_9aa238fc6121dede" formatted="false">",丟包率 "</string>
<string name="text_0b5d16c21095fe10" formatted="false">",傳送 "</string>
<string name="text_ba9a792d063a8c86" formatted="false">",已儲存密碼"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">",接收 "</string>
<string name="text_42237b5ec70c26f3" formatted="false">",點選重試"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Abgeschlossen"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Übertragung fehlgeschlagen"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" Geräte"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Artikel · "</string>
<string name="text_2c894057fc526576" formatted="false">" ungelesene Artikel"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDesk Remote-Verbindung"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL Version 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC-Hinweis"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFEC Lizenz und Quelle"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P-Direktverbindung"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP enabled"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"TURN-Relais"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0}-Übertragung fehlgeschlagen"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Geräte: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} ungelesene Elemente"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, Empfangen von {1}, Senden von {2}, Paketverlustrate {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Verbunden"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Zum Aktualisieren nach unten ziehen"</string>
<string name="text_37772187ab854191" formatted="false">"Verlustrate"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Mittel"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Priorisiert die Aufrechterhaltung eines flüssigen Bildes und reduziert die Auflösung stärker, wenn die Bandbreite oder der Codierungsdruck hoch ist."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Niedrig"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Verwendung eines benutzerdefinierten Servers"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Passwort speichern"</string>
<string name="text_10019cef593819ed" formatted="false">"Empfangene Datei speichern"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Remote-Bildschirmvorschauen speichern"</string>
<string name="text_0f197074373e38fd" formatted="false">"Remote-Bildschirmvorschau speichern?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"Die Signalverbindung ist unterbrochen. Bitte stellen Sie die Verbindung wieder her."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Das Laden der Ankündigung ist fehlgeschlagen. Bitte versuchen Sie es erneut."</string>
<string name="text_00554b17c432937e" formatted="false">"Einzelheiten zur Ankündigung"</string>
<string name="text_2428420e407fe2e8" formatted="false">"Insgesamt "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Insgesamt {0} Artikel ·{1} ungelesene Artikel"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"Ungefähr"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"Über CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"Ungefähr, es gibt eine neue Version"</string>
<string name="text_3fd47edce45b3603" formatted="false">"Schließen"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Bedienbügel schließen"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Privatsphäre-Bildschirm deaktivieren"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Gleicht Klarheit und Glätte aus und verringert die Auflösung bei konstantem Druck nur allmählich."</string>
<string name="text_b92421822c213c2b" formatted="false">"Auflösung"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Löschen"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Ansage löschen"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Datensatz löschen"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"Diese Ankündigung löschen?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Ankündigung aktualisieren"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Zwischenablage"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Laden…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Mehr laden"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Update empfohlen"</string>
<string name="text_861521c9e107c648" formatted="false">"Die Initialisierung der nativen Verbindung ist fehlgeschlagen"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Senden"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Senden "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"Klicken Sie nach dem Senden auf der Remote-Tastatur auf „Einfügen“. Der empfangene Remote-Text kann manuell auf den lokalen Computer kopiert werden."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Datei senden"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Text senden"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Abbrechen"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Verbindung abbrechen"</string>
<string name="text_72814b15a6fa152c" formatted="false">"Jede Komponente unterliegt der aufgeführten Open-Source-Lizenz und die gewährten Rechte unterliegen nicht den allgemeinen Nutzungsbedingungen dieser Anwendung. Für die Anzeige des Quellcodes ist eine Internetverbindung erforderlich."</string>
<string name="text_e009c148d1890dac" formatted="false">"Insgesamt"</string>
<string name="text_81c92013219dd115" formatted="false">"Zustimmen und fortfahren"</string>
<string name="text_409696774c9998ce" formatted="false">"Bleiben Sie im Hintergrund verbunden. Klicken Sie, um zum Remote-Desktop zurückzukehren"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"Hintergrund-Keepalive konnte nicht gestartet werden. Bitte lassen Sie die Anwendung im Vordergrund"</string>
<string name="text_d96a21463557171d" formatted="false">"Hintergrund-Keepalive konnte nicht gestartet werden. Bitte lassen Sie die Anwendung im Vordergrund und stellen Sie die Verbindung erneut her."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"Die Hintergrundverbindung wurde beendet. Bitte stellen Sie die Verbindung wieder her."</string>
<string name="text_da904f715e1cff87" formatted="false">"Der Hintergrundverbindungsdienst wurde beendet. Bitte stellen Sie die Verbindung wieder her."</string>
<string name="text_0812359e18082071" formatted="false">"Neue Vorschauen werden bei nachfolgenden Verbindungen weiterhin gespeichert."</string>
<string name="text_b9086662b1df1f20" formatted="false">"Online"</string>
<string name="text_39f2f444a042643e" formatted="false">"Geben Sie den Hostnamen oder die IPv4-Adresse ein, ohne Protokoll und Pfad"</string>
<string name="text_473400815c0f687d" formatted="false">"Ton"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Remote-Text kopieren"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Behalten Sie immer eine höhere Auflösung bei und reduzieren Sie die Bildrate, um die Bildschärfe zu gewährleisten, wenn die Bandbreite nicht ausreicht."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Medienverschlüsselung"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Buchstaben &amp; Zahlen"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Der sichere Speicher oder die Initialisierung des Zertifikats ist fehlgeschlagen. Bitte versuchen Sie es erneut oder löschen Sie die Anwendungsdaten"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Fertig"</string>
<string name="text_678328f6f02881c7" formatted="false">"Sie können nach Abschluss der Datenschutzautorisierung nach Updates suchen"</string>
<string name="text_c4361f9abdba4882" formatted="false">"Registrieren Sie die lokale Identität, nachdem Sie die Datenschutzautorisierung abgeschlossen haben"</string>
<string name="text_c29b888e357d727d" formatted="false">"Das Speichern des Passworts ist fehlgeschlagen. Diese Verbindung kann weiterhin verwendet werden"</string>
<string name="text_3f31300a23afb390" formatted="false">"Passwörter werden verschlüsselt im nativen Android Keystore gespeichert"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"Remote-ID"</string>
<string name="text_0512084e130984bc" formatted="false">"Peer-ID "</string>
<string name="text_c5147d65960196b0" formatted="false">"Peer-ID {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"Alle auf diesem Computer gespeicherten Remote-Vorschauen werden gelöscht und die Verbindungsdatensätze und Passwörter bleiben erhalten."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Bildschirmausrichtung"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Fernbedienungsmenü erweitern"</string>
<string name="text_9343612ee473edd6" formatted="false">"Remote-Verbindung wegen Benachrichtigung getrennt"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Passwort gespeichert"</string>
<string name="text_1cbd391383283924" formatted="false">"Das gespeicherte Passwort kann nicht gelesen werden, bitte geben Sie es erneut ein."</string>
<string name="text_6792b9f341380c68" formatted="false">"Das gespeicherte Passwort kann nicht beibehalten werden, bitte geben Sie es erneut ein."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del gesendet"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"Hintergrund-Keep-Alive ist aktiviert, wechselnde Anwendungen können die Verbindung weiterhin aufrechterhalten"</string>
<string name="text_506409ec08cb2a79" formatted="false">"Hintergrund-Keepalive aktiviert, um Verbindungsbenachrichtigungen zurückzugeben oder zu trennen"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Zugegeben"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Lesen"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Verbunden "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Mit Server verbunden"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Bildrate"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Flüssig"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Balance"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"Die Lizenzdatei in der Anwendung fehlt oder ist beschädigt. Bitte installieren Sie die Anwendung neu."</string>
<string name="text_3f2904435942cc0f" formatted="false">"Die Entwicklungsversion enthält Änderungen, die noch nicht veröffentlicht wurden. Der folgende Link ist der Quellcode der Basisversion."</string>
<string name="text_4338afaad52ea445" formatted="false">"Speichern aktivieren"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Privatsphärenschutz aktivieren"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Open-Source-Komponenten"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Beschreibung der Open-Source-Softwarerechte"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"Dies ist derzeit die neueste Version"</string>
<string name="text_24a52788cf474bf6" formatted="false">"Derzeit unterstützt die Gegenstelle keine direkte Eingabe dieses Textes. Bitte verwenden Sie eine Version des gesteuerten Endes, die die Texteingabe unterstützt, oder senden Sie sie über die Zwischenablage."</string>
<string name="text_33f25b500697a7d9" formatted="false">"Nach erfolgreicher Verbindung wird hier das nächstgelegene Remote-Gerät angezeigt."</string>
<string name="text_70341256d7317e7d" formatted="false">"Ich habe die Datenschutzerklärung gelesen und bin damit einverstanden"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Ausgewählte Datei (kann nicht gelesen werden oder nicht genügend Speicherplatz)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"Die Geräte-ID wurde nicht gefunden"</string>
<string name="text_97b7b18df07b9787" formatted="false">"Das Senden des Befehls ist fehlgeschlagen. Bitte versuchen Sie es erneut"</string>
<string name="text_cb782027ca5986c7" formatted="false">"Der Befehl wurde gesendet und wartet auf die Bestätigung des Computers ..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Autorisierungsstatus"</string>
<string name="text_6e684586884ebee6" formatted="false">"Empfangen"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"erhalten "</string>
<string name="text_d16488b987723161" formatted="false">"Lokaler Decoder"</string>
<string name="text_6b58198955259066" formatted="false">"Widerrufen"</string>
<string name="text_c52e42ee856825db" formatted="false">"Nach dem Abheben wird die Fernverbindung beendet, die Bildschirmvorschau geschlossen und die bestehende Vorschau gelöscht. Der Verbindungsdatensatz und das Passwort bleiben erhalten und vor der erneuten Verbindung ist eine erneute Autorisierung erforderlich."</string>
<string name="text_61f4788032ec596b" formatted="false">"Netzwerkberechtigung entziehen"</string>
<string name="text_e736e419008ac10a" formatted="false">"Netzwerkberechtigung entziehen?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Fernbedienungsmenü einklappen"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Tastatur ausblenden"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Maus ausblenden"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Daten"</string>
<string name="text_94244f41d0a41378" formatted="false">"Das Speichern der Datei ist fehlgeschlagen. Bitte versuchen Sie es erneut"</string>
<string name="text_2553a785fef2a405" formatted="false">"Datei gespeichert"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"Zeitüberschreitung bei der Dateiauswahl und Sitzung beendet"</string>
<string name="text_985a5c380be49f4f" formatted="false">"Der Text ist größer als 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Trennen"</string>
<string name="text_09b1395f50589327" formatted="false">"Kehrt nach dem Trennen der Verbindung zur Startseite zurück"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"Remote-Verbindung trennen?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Trennen"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"Neue Version"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"Neue Version verfügbar: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"Neue Version verfügbar: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"Keine ungelesenen Ankündigungen"</string>
<string name="text_dca2c418ac967366" formatted="false">"Lesestatus kann nicht lokal gespeichert werden. Bitte versuchen Sie es erneut."</string>
<string name="text_5c92da76360056cc" formatted="false">"Diese Ankündigung kann nicht gelöscht werden. Bitte versuchen Sie es erneut."</string>
<string name="text_615709dd3129313c" formatted="false">"Remote-Verbindung konnte nicht initiiert werden"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"Open-Source-Lizenz kann nicht gelesen werden"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Bildschirm"</string>
<string name="text_ffe21a671190323e" formatted="false">"Passwort anzeigen"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Jetzt nicht"</string>
<string name="text_6d9a7758610a2432" formatted="false">"Keine Ankündigungen"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Letzte Verbindungen"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Server"</string>
<string name="text_dfe408e686175095" formatted="false">"Server TLS certificate-Überprüfung fehlgeschlagen"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Serveradresse"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Die Serveranmeldung ist fehlgeschlagen. Bitte überprüfen Sie die Serveradresse und das Netzwerk"</string>
<string name="text_d270868960e68629" formatted="false">"Server-Port"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"Zeitüberschreitung bei der Serververbindung. Bitte überprüfen Sie die Serveradresse und das Netzwerk"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"Nicht aktiviert"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"Es wurde kein Browser zum Öffnen des Links gefunden"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"Nicht autorisiert"</string>
<string name="text_4d0680f9efaef147" formatted="false">"Ungelesen"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Server nicht verbunden · Klicken Sie, um es erneut zu versuchen"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"Diese Anwendung verwendet OpenFEC zur Vorwärtsfehlerkorrektur unterCeCILL-C1.0, was eingeschränkte Garantien und Haftungsbeschränkungen bietet. Einzelheiten finden Sie in der vollständigen Lizenz."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"Lokale ID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"Lokale ID {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Quellcode und Build-Anweisungen für diese Version"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"Die Quellrevision ist für diesen Build nicht verfügbar."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Zum Aktualisieren loslassen"</string>
<string name="text_47077144715b7074" formatted="false">"Bauanleitung"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Erstellen Sie Rezepte und Projekt-Patches"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"Quellcode anzeigen"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"Prüfung fehlgeschlagen. Bitte versuchen Sie es später noch einmal."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Suchen Sie nach Updates"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Sichtschutz wird geschlossen…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Sichere Verbindung wird hergestellt…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"Privatsphärenschutz einschalten..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Suche nach Updates…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Löschen..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Lokale ID erhalten..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Ausgewählte Datei wird gelesen..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Verbinden"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Verbindung zum Signalisierungsserver wird hergestellt..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Verbindung zum Server herstellen..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Verbindung zum Server wird wiederhergestellt..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"Für dieses Gerät ist keine Dateiauswahl verfügbar"</string>
<string name="text_bce2377283c2455a" formatted="false">"Klar"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Vorschaubilder löschen"</string>
<string name="text_ecc809e44637de18" formatted="false">"Vorschaubilder löschen?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Quellcode und Build-Anweisungen"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Quellcode-Version"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Version"</string>
<string name="text_16410ee9663fad10" formatted="false">"Urheberrechts- und Lizenzerklärung"</string>
<string name="text_11de77e1e41e680f" formatted="false">"Computertastatur"</string>
<string name="text_20f57900234debd7" formatted="false">"Qualität"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Videopräferenz"</string>
<string name="text_82a5600c470179fa" formatted="false">"Videolatenz"</string>
<string name="text_33b458faaf967e15" formatted="false">"Videoeinstellungen"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Videoqualität"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Bildrate der Videoaufnahme"</string>
<string name="text_66062971234f422d" formatted="false">"Touchpad"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Relative Maus"</string>
<string name="text_de32e20193ad1042" formatted="false">"Verstanden"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"Okay"</string>
<string name="text_36f33adaf0942634" formatted="false">"Bestätigen"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Verbindung bestätigen"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Offline"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Leertaste"</string>
<string name="text_471a579e8d87faac" formatted="false">"Der Portbereich liegt zwischen 1 und 65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Symbole &amp; Tasten"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Warten Sie, bis der Computer in den Datenschutzstatus zurückkehrt. Ältere Computer unterstützen dies möglicherweise nicht."</string>
<string name="text_5ee880e2524a983a" formatted="false">"Warten auf Remote-Bildschirminformationen"</string>
<string name="text_674c723a85ee7f57" formatted="false">"Warten auf den Remote-Bildschirm ..."</string>
<string name="text_119f0483da4756c4" formatted="false">"Datenschutzzustimmung ausstehend"</string>
<string name="text_7916071c1a8ee458" formatted="false">"Systemtastatur"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Direkte Berührung"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Absolute Maus"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Netzwerk"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Benutzerdefiniert"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Virtuelle Maus"</string>
<string name="text_62c199f37bf95869" formatted="false">"Der Remote-Datenschutzbildschirm wurde über die lokale Verknüpfung entsperrt. Sperren Sie es erneut, um die Privatsphäre wiederherzustellen und das Entsperren lokaler Verknüpfungen zu deaktivieren."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"Die kontrollierte Version ist zu niedrig, bitte aktualisieren Sie auf die neueste Version."</string>
<string name="text_d814476826827b78" formatted="false">"Remote-Encoder"</string>
<string name="text_488402883e27f6f0" formatted="false">"Text, der an die Gegenstelle gesendet werden soll"</string>
<string name="text_c20f7618d330a854" formatted="false">"Video"</string>
<string name="text_310c625425f014d8" formatted="false">"Lizenz"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Lizenzen und Urheberrechtshinweise"</string>
<string name="text_e24134b643a17975" formatted="false">"Die Lizenz- und Urheberrechtshinweise sind offline verfügbar."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"Die Geräteidentität konnte nicht gespeichert werden. Bitte überprüfen Sie den sicheren Speicher"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"Geräteidentität und ausgewählte gespeicherte Zugangskennwörter werden mit Android Keystore verschlüsselt. Verbindungsdatensätze werden nur lokal gespeichert und sind nicht in der Gerätesicherung enthalten. Der Inhalt der Zwischenablage wird gelöscht, wenn die Sitzung beendet wird. Bleiben Sie mit Bild-in-Bild- oder verbundenen Benachrichtigungen in der Sitzung, nachdem Sie die App verlassen haben, und trennen Sie die Verbindung innerhalb der App oder der Benachrichtigungen."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Einstellungen"</string>
<string name="text_4f8771952222a011" formatted="false">"Einstellungen, es gibt eine neue Version"</string>
<string name="text_f4f712d5be91721a" formatted="false">"Zugangspasswort"</string>
<string name="text_dd6c465847af1b60" formatted="false">"Das Zugangspasswort ist falsch, bitte geben Sie es erneut ein."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Zertifikatsüberprüfung fehlgeschlagen · Klicken Sie, um es erneut zu versuchen"</string>
<string name="text_44f3d4d52347095a" formatted="false">"Bitte füllen Sie zunächst die Online-Autorisierung aus"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Bitte aktivieren Sie den CrossDesk-Systemdienst auf der Gegenseite und versuchen Sie es erneut"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"Die Anfrage ist abgelaufen und der Datenschutzstatus des Computers wurde nicht bestätigt."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Bitte warten Sie, bis die aktuelle Datei gesendet wird."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Bitte geben Sie einen Port im Bereich 1–65535 ein"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Geben Sie eine 9-stellige Geräte-ID ein"</string>
<string name="text_285a0714894d2899" formatted="false">"Bitte geben Sie ein gültiges Zugangspasswort ein"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"Einstellungen konnten nicht angewendet werden. Versuchen Sie es erneut."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Softwarelizenz"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Zurück"</string>
<string name="text_7565f94031582c7c" formatted="false">"Zurück zur Steuerleiste"</string>
<string name="text_55113b6925c362fd" formatted="false">"Noch kein Verbindungsdatensatz"</string>
<string name="text_3aa527b2106cc987" formatted="false">"Diese Ankündigung wurde aktualisiert oder gelöscht. Bitte kehren Sie zur Ankündigungsliste zurück, um sie anzuzeigen."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"Remote-Aufgabe ist geschlossen"</string>
<string name="text_c255841f68096d3b" formatted="false">"Remotedesktop"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Remote-Desktop-Touchbereich"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Remote-Desktop: Berühren und klicken, zum Verschieben ziehen, die rechte Taste gedrückt halten, zum Zoomen zusammenziehen, hineinzoomen und den Bildschirm mit zwei Fingern ziehen, mit zwei Fingern nach oben und unten streichen, um im Originalmaßstab zu scrollen"</string>
<string name="text_3535087994d050de" formatted="false">"Remote-Computer"</string>
<string name="text_ed2e03072ce131de" formatted="false">"Das Remote-Fenster wurde geschlossen oder der Hintergrund-Keepalive konnte nicht gestartet werden und die Sitzung wurde beendet."</string>
<string name="text_678b783fb578172b" formatted="false">"Remote-Verbindung"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Falsches Remote-Passwort"</string>
<string name="text_a2ce499295e358ae" formatted="false">"Die Gegenstelle hat die Verbindung geschlossen"</string>
<string name="text_176e117df3ace734" formatted="false">"Die Gegenstelle ist derzeit nicht verfügbar"</string>
<string name="text_d2c0694243f93249" formatted="false">"Das Passwort für den Fernzugriff ist abgelaufen. Bitte verbinden Sie sich erneut."</string>
<string name="text_a5574109f0208e89" formatted="false">"Verbinden"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Verbindung "</string>
<string name="text_89c4766afc85125f" formatted="false">"Verbindung fehlgeschlagen"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Verbindung fehlgeschlagen · Klicken Sie, um es erneut zu versuchen"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Verbindung verloren"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Verbindung beendet"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Verbindungsverzögerung (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Verfügbar bei Verbindung mit einem unterstützten Remote-Gerät."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Querformatbildschirm sperren, nachdem die Verbindung hergestellt wurde"</string>
<string name="text_f741e0536210eafa" formatted="false">"Verbindungsaufforderung"</string>
<string name="text_485a26050ce57431" formatted="false">"Verbindungsmethode"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Mit dem Server verbinden"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Stellen Sie eine Verbindung zum Server her, um die neuesten Ankündigungen zu erhalten."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"Zeitüberschreitung bei der Verbindung. Bitte überprüfen Sie die Geräte-ID, den Server und den Remote-Online-Status"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Mit Remote-Desktop verbinden"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Rücktaste"</string>
<string name="text_e02926784fcedb91" formatted="false">"Mitteilungen"</string>
<string name="text_465a092680d132b3" formatted="false">"Einige Vorschauen konnten nicht gelöscht werden. Bitte versuchen Sie es erneut."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Versuchen Sie es erneut"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Versuchen Sie erneut, den Lesestatus zu speichern"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Sperren"</string>
<string name="text_864200bbb241e067" formatted="false">"Tastatur"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Lesen und autorisieren"</string>
<string name="text_86651d17a401c55b" formatted="false">"Datenschutz"</string>
<string name="text_26c6e011e343708c" formatted="false">"Datenschutz und Autorisierung"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Sichtschutz"</string>
<string name="text_e157fafb76aad954" formatted="false">"Sichtschutz aus"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Sichtschutz an"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Datenschutzbildschirm entsperrt"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Anomalie des Sichtschutzbildschirms"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"Der Datenschutzbildschirm ist vorübergehend nicht verfügbar"</string>
<string name="text_4d59be3c75189032" formatted="false">"Der Status des Datenschutzbildschirms ist abgelaufen und wartet auf Computerbestätigung"</string>
<string name="text_1674532c70ed1db2" formatted="false">"Status des Datenschutzbildschirms muss bestätigt werden"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Datenschutzrichtlinie"</string>
<string name="text_554e957e8e3c00af" formatted="false">"Die Datenschutzerklärung kann vorübergehend nicht gelesen werden. Bitte verweigern Sie die Verbindung zum Internet und installieren Sie die Anwendung neu."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Stumm"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Audio"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Projektwebsite"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"Vorschauen können persönliche oder geschäftliche Informationen vom Remote-Computer enthalten. Nach dem Einschalten speichert jede Verbindung automatisch ein Bild auf dem Gerät und zeigt es in den letzten Verbindungen an. Sie können jederzeit schließen und löschen."</string>
<string name="text_b1c27820fec23edb" formatted="false">"Hoch"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"Standard"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"Standardmäßig deaktiviert. Wenn es eingeschaltet ist, wird der Bildschirm angezeigt und bleibt im Querformat, wenn das Telefon angeschlossen ist. Wenn es ausgeschaltet ist, folgt es der Richtung des Geräts. Tablets und Multi-Windows sind nicht betroffen."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Rechte Maustaste, drücken und ziehen, loslassen, um das Ziehen zu beenden"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Linke Maustaste, drücken und ziehen, loslassen, um das Ziehen zu beenden"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Maussteuerung"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Mausrad, nach oben und unten schieben, um aus dem Tastenbereich zu gleiten"</string>
<string name="text_9aa238fc6121dede" formatted="false">", Paketverlustrate "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", senden "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", Passwort gespeichert"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", empfangen "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", klicken Sie auf Wiederholen"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Completado"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Transferencia fallida"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" dispositivos"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Artículo · "</string>
<string name="text_2c894057fc526576" formatted="false">" elementos no leídos"</string>
<string name="text_d0ac753c6681e578" formatted="false">"Conexión remota CrossDesk"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL versión 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"Aviso OpenFEC"</string>
<string name="text_69ad29820a259526" formatted="false">"Licencia y fuente OpenFEC"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"Conexión directa P2P"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP habilitado"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"Relé de GIRO"</string>
<string name="text_ebede975edd52aee" formatted="false">"Error en la transmisión {0}"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Dispositivos: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} elementos no leídos"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, recepción de {1}, envío de {2}, tasa de pérdida de paquetes {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Conectado"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Tire hacia abajo para actualizar"</string>
<string name="text_37772187ab854191" formatted="false">"Tasa de pérdidas"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Media"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Da prioridad a mantener la imagen fluida y reducirá la resolución de manera más agresiva cuando el ancho de banda o la presión de codificación sean altos."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Baja"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Usando un servidor personalizado"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Guardar contraseña"</string>
<string name="text_10019cef593819ed" formatted="false">"Guardar archivo recibido"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Guardar vistas previas de pantalla remotas"</string>
<string name="text_0f197074373e38fd" formatted="false">"¿Guardar vista previa de pantalla remota?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"La conexión de señalización está interrumpida, vuelva a conectarse."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Error al cargar el anuncio, inténtelo de nuevo."</string>
<string name="text_00554b17c432937e" formatted="false">"Detalles del anuncio"</string>
<string name="text_2428420e407fe2e8" formatted="false">"totales "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Total de elementos {0}· Elementos no leídos {1}"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"Acerca de"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"Acerca de CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"Acerca de, hay una nueva versión."</string>
<string name="text_3fd47edce45b3603" formatted="false">"Cerrar"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Cerrar barra de control"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Desactivar la pantalla de privacidad"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Equilibra la claridad y la suavidad, reduciendo solo gradualmente la resolución bajo presión constante."</string>
<string name="text_b92421822c213c2b" formatted="false">"Resolución"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Eliminar"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Eliminar anuncio"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Eliminar registro"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"¿Eliminar este anuncio?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Actualizar anuncio"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Portapapeles"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Cargando…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Cargar más"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Actualización recomendada"</string>
<string name="text_861521c9e107c648" formatted="false">"Falló la inicialización de la conexión nativa"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Enviar"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Enviar "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"Después de enviar, haga clic en \"Pegar\" en el teclado remoto. El texto remoto recibido se puede copiar manualmente a la máquina local."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Enviar archivo"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Enviar texto"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Cancelar"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Cancelar conexión"</string>
<string name="text_72814b15a6fa152c" formatted="false">"Cada componente está sujeto a la licencia de código abierto enumerada y los derechos otorgados no están sujetos a los términos generales de uso de esta aplicación. Para ver el código fuente se requiere una conexión a Internet."</string>
<string name="text_e009c148d1890dac" formatted="false">"Total"</string>
<string name="text_81c92013219dd115" formatted="false">"Aceptar y continuar"</string>
<string name="text_409696774c9998ce" formatted="false">"Manténgase conectado en segundo plano, haga clic para regresar al escritorio remoto"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"No se pudo iniciar el mantenimiento en segundo plano; mantenga la aplicación en primer plano"</string>
<string name="text_d96a21463557171d" formatted="false">"No se pudo iniciar el mantenimiento en segundo plano. Mantenga la aplicación en primer plano y vuelva a conectarse."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"La conexión en segundo plano ha finalizado; vuelva a conectarse."</string>
<string name="text_da904f715e1cff87" formatted="false">"El servicio de conexión en segundo plano se detuvo; vuelva a conectarse."</string>
<string name="text_0812359e18082071" formatted="false">"Las nuevas vistas previas aún se guardarán en conexiones posteriores."</string>
<string name="text_b9086662b1df1f20" formatted="false">"En línea"</string>
<string name="text_39f2f444a042643e" formatted="false">"Complete el nombre del host o la dirección IPv4, sin protocolo ni ruta"</string>
<string name="text_473400815c0f687d" formatted="false">"Sonido"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Copiar texto remoto"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Mantenga siempre una resolución más alta y reduzca la velocidad de fotogramas para garantizar la claridad de la imagen cuando el ancho de banda sea insuficiente."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Cifrado de medios"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Letras y números"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Error en el almacenamiento seguro o en la inicialización del certificado. Inténtelo de nuevo o borre los datos de la aplicación."</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Hecho"</string>
<string name="text_678328f6f02881c7" formatted="false">"Puede buscar actualizaciones después de completar la autorización de privacidad"</string>
<string name="text_c4361f9abdba4882" formatted="false">"Registre la identidad local después de completar la autorización de privacidad"</string>
<string name="text_c29b888e357d727d" formatted="false">"Error al guardar la contraseña, esta conexión aún se puede utilizar"</string>
<string name="text_3f31300a23afb390" formatted="false">"Las contraseñas se guardan cifradas en el almacén de claves nativo de Android"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"Identificación remota"</string>
<string name="text_0512084e130984bc" formatted="false">"Identificación de pares "</string>
<string name="text_c5147d65960196b0" formatted="false">"ID de igual {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"Se eliminarán todas las vistas previas remotas guardadas en esta máquina y se conservarán los registros de conexión y las contraseñas."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Orientación de la pantalla"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Ampliar el menú del control remoto"</string>
<string name="text_9343612ee473edd6" formatted="false">"Conexión remota desconectada de la notificación"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Contraseña guardada"</string>
<string name="text_1cbd391383283924" formatted="false">"La contraseña guardada no se puede leer; vuelva a ingresarla."</string>
<string name="text_6792b9f341380c68" formatted="false">"La contraseña guardada no se puede conservar; vuelva a ingresarla."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del enviado"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"Mantener vivo en segundo plano está habilitado, cambiar de aplicación puede continuar manteniendo la conexión"</string>
<string name="text_506409ec08cb2a79" formatted="false">"Keepalive en segundo plano habilitado para regresar o desconectarse de las notificaciones de conexión"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Concedido"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Leer"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Conectado "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Conectado al servidor"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Velocidad de fotogramas"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Fluidez"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Equilibrado"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"El archivo de licencia de la aplicación falta o está dañado; reinstale la aplicación."</string>
<string name="text_3f2904435942cc0f" formatted="false">"La versión de desarrollo contiene modificaciones que aún no han sido publicadas. El siguiente enlace es el código fuente de la versión básica."</string>
<string name="text_4338afaad52ea445" formatted="false">"Activar guardado"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Habilitar pantalla de privacidad"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Componentes de código abierto"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Descripción de los derechos del software de código abierto"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"Esta es actualmente la última versión."</string>
<string name="text_24a52788cf474bf6" formatted="false">"Actualmente, el extremo remoto no admite la entrada directa de este texto. Utilice una versión del extremo controlado que admita la entrada de texto o envíela a través del portapapeles."</string>
<string name="text_33f25b500697a7d9" formatted="false">"Tras una conexión exitosa, aquí se mostrará el dispositivo remoto más cercano."</string>
<string name="text_70341256d7317e7d" formatted="false">"He leído y acepto la Política de Privacidad"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Archivo seleccionado (no se puede leer o no hay suficiente espacio)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"No se encontró el ID del dispositivo"</string>
<string name="text_97b7b18df07b9787" formatted="false">"Error al enviar el comando, inténtelo de nuevo"</string>
<string name="text_cb782027ca5986c7" formatted="false">"El comando ha sido enviado, esperando confirmación por computadora..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Estado de autorización"</string>
<string name="text_6e684586884ebee6" formatted="false">"Recibir"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"recibir "</string>
<string name="text_d16488b987723161" formatted="false">"Decodificador local"</string>
<string name="text_6b58198955259066" formatted="false">"Retirar"</string>
<string name="text_c52e42ee856825db" formatted="false">"Después de retirarse, se detendrá la conexión remota, se cerrará la vista previa de la pantalla y se borrará la vista previa existente. El registro de conexión y la contraseña se conservarán y se requerirá autorización nuevamente antes de volver a conectarse."</string>
<string name="text_61f4788032ec596b" formatted="false">"Retirar la autorización de red"</string>
<string name="text_e736e419008ac10a" formatted="false">"¿Retirar la autorización de la red?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Contraer menú de control remoto"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Ocultar teclado"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Ocultar ratón"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Datos"</string>
<string name="text_94244f41d0a41378" formatted="false">"Error al guardar el archivo, inténtelo de nuevo"</string>
<string name="text_2553a785fef2a405" formatted="false">"Archivo guardado"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"Se agotó el tiempo de selección de archivos y la sesión finalizó"</string>
<string name="text_985a5c380be49f4f" formatted="false">"El texto supera los 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Desconectar"</string>
<string name="text_09b1395f50589327" formatted="false">"Volverá a la página de inicio después de desconectarse"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"¿Desconectar la conexión remota?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Desconectar"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"Nueva versión"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"Nueva versión disponible :v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"Nueva versión disponible: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"No hay anuncios no leídos"</string>
<string name="text_dca2c418ac967366" formatted="false">"No se puede guardar el estado de lectura localmente. Por favor inténtalo de nuevo."</string>
<string name="text_5c92da76360056cc" formatted="false">"No se puede eliminar este anuncio. Por favor inténtalo de nuevo."</string>
<string name="text_615709dd3129313c" formatted="false">"No se puede iniciar la conexión remota"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"No se puede leer la licencia de código abierto"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Pantalla"</string>
<string name="text_ffe21a671190323e" formatted="false">"Mostrar contraseña"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Ahora no"</string>
<string name="text_6d9a7758610a2432" formatted="false">"Sin anuncios"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Conexiones recientes"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Servidor"</string>
<string name="text_dfe408e686175095" formatted="false">"Error en la verificación del certificado Server TLS"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Dirección del servidor"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Error al iniciar sesión en el servidor; verifique la dirección y la red del servidor"</string>
<string name="text_d270868960e68629" formatted="false">"Puerto del servidor"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"Se agotó el tiempo de espera de la conexión del servidor; verifique la dirección y la red del servidor."</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"No habilitado"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"No se encontró ningún navegador para abrir el enlace."</string>
<string name="text_94bc3d40defef3e9" formatted="false">"No autorizado"</string>
<string name="text_4d0680f9efaef147" formatted="false">"No leído"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Servidor no conectado · Haga clic para intentar nuevamente"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"Esta aplicación utiliza OpenFEC para la corrección de errores directos bajoCeCILL-C1.0, que proporciona garantías limitadas y limita la responsabilidad. Consulte la licencia completa para obtener más detalles."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"Identificación local "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"ID local {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Código fuente e instrucciones de compilación para esta versión"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"La revisión fuente no está disponible para esta compilación."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Suelte para actualizar"</string>
<string name="text_47077144715b7074" formatted="false">"Instrucciones de construcción"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Crear recetas y parches de proyectos"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"Ver código fuente"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"La verificación falló. Inténtelo de nuevo más tarde."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Buscar actualizaciones"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Cerrando la pantalla de privacidad..."</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Estableciendo una conexión segura..."</string>
<string name="text_0da575e3c92de73a" formatted="false">"Activando la pantalla de privacidad..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Buscando actualizaciones..."</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Borrando..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Obtención de identificación local..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Leyendo el archivo seleccionado..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Conexión"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Conectándose al servidor de señalización..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Conectándose al servidor..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Reconectándose al servidor..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"No hay selector de archivos disponible para este dispositivo"</string>
<string name="text_bce2377283c2455a" formatted="false">"Borrar"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Borrar vistas previas"</string>
<string name="text_ecc809e44637de18" formatted="false">"¿Borrar vistas previas?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Código fuente e instrucciones de compilación"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Versión del código fuente"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Versión"</string>
<string name="text_16410ee9663fad10" formatted="false">"Declaración de derechos de autor y licencia"</string>
<string name="text_11de77e1e41e680f" formatted="false">"Teclado de computadora"</string>
<string name="text_20f57900234debd7" formatted="false">"Calidad"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Preferencia de vídeo"</string>
<string name="text_82a5600c470179fa" formatted="false">"Latencia de vídeo"</string>
<string name="text_33b458faaf967e15" formatted="false">"Configuración de vídeo"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Calidad de vídeo"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Velocidad de fotogramas de captura de vídeo"</string>
<string name="text_66062971234f422d" formatted="false">"Panel táctil"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Ratón relativo"</string>
<string name="text_de32e20193ad1042" formatted="false">"Entendido"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"Aceptar"</string>
<string name="text_36f33adaf0942634" formatted="false">"Confirmar"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Confirmar conexión"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Sin conexión"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Espacio"</string>
<string name="text_471a579e8d87faac" formatted="false">"El rango de puertos es 1–65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Símbolos y teclas"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Espere a que la computadora vuelva al estado de pantalla de privacidad; Es posible que las computadoras más antiguas no lo admitan."</string>
<string name="text_5ee880e2524a983a" formatted="false">"Esperando información de la pantalla remota"</string>
<string name="text_674c723a85ee7f57" formatted="false">"Esperando la pantalla remota..."</string>
<string name="text_119f0483da4756c4" formatted="false">"Esperando autorización de privacidad"</string>
<string name="text_7916071c1a8ee458" formatted="false">"Teclado del sistema"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Toque directo"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Ratón absoluto"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Red"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Personalizado"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Ratón virtual"</string>
<string name="text_62c199f37bf95869" formatted="false">"La pantalla de privacidad remota se desbloqueó usando el acceso directo local. Bloquéelo nuevamente para restaurar la privacidad y deshabilitar el desbloqueo de accesos directos locales."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"La versión controlada es demasiado baja; actualice a la última versión."</string>
<string name="text_d814476826827b78" formatted="false">"Codificador remoto"</string>
<string name="text_488402883e27f6f0" formatted="false">"Texto a enviar al extremo remoto"</string>
<string name="text_c20f7618d330a854" formatted="false">"Vídeo"</string>
<string name="text_310c625425f014d8" formatted="false">"Licencia"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Licencias y avisos de derechos de autor"</string>
<string name="text_e24134b643a17975" formatted="false">"La licencia y el aviso de derechos de autor están disponibles sin conexión."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"No se pudo guardar la identidad del dispositivo; verifique el almacenamiento seguro"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"La identidad del dispositivo y las contraseñas de acceso guardadas seleccionadas se cifran mediante Android Keystore. Los registros de conexión solo se guardan localmente y no se incluyen en la copia de seguridad del dispositivo. El contenido del portapapeles se borra cuando finaliza la sesión. Manténgase en sesión con imagen en imagen o notificaciones conectadas después de salir de la aplicación y desconéctese dentro de la aplicación o las notificaciones."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Ajustes"</string>
<string name="text_4f8771952222a011" formatted="false">"Configuración, hay una nueva versión."</string>
<string name="text_f4f712d5be91721a" formatted="false">"Contraseña de acceso"</string>
<string name="text_dd6c465847af1b60" formatted="false">"La contraseña de acceso es incorrecta, por favor vuelva a ingresarla."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Error en la verificación del certificado · Haga clic para intentar nuevamente"</string>
<string name="text_44f3d4d52347095a" formatted="false">"Primero complete la autorización en línea"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Habilite el servicio del sistema CrossDesk en el extremo remoto y vuelva a intentarlo."</string>
<string name="text_990fd23c62de5ccf" formatted="false">"Se agotó el tiempo de espera de la solicitud y no se ha confirmado el estado de la pantalla de privacidad de la computadora."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Espere a que se envíe el archivo actual."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Ingrese un puerto en el rango 1–65535"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Ingrese una ID de dispositivo de 9 dígitos"</string>
<string name="text_285a0714894d2899" formatted="false">"Por favor ingrese una contraseña de acceso válida"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"No se pudieron aplicar las configuraciones. Intentar otra vez."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Licencia de software"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Volver"</string>
<string name="text_7565f94031582c7c" formatted="false">"Volver a la barra de control"</string>
<string name="text_55113b6925c362fd" formatted="false">"Aún no hay registro de conexión"</string>
<string name="text_3aa527b2106cc987" formatted="false">"Este anuncio ha sido actualizado o eliminado, regrese a la lista de anuncios para verlo."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"La tarea remota está cerrada"</string>
<string name="text_c255841f68096d3b" formatted="false">"Escritorio remoto"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Área táctil del escritorio remoto"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Escritorio remoto: toque y haga clic, arrastre para mover, presione y mantenga presionado el botón derecho, pellizque para hacer zoom, acerque y arrastre la pantalla con dos dedos, deslice hacia arriba y hacia abajo con dos dedos para desplazarse a la escala original"</string>
<string name="text_3535087994d050de" formatted="false">"Computadora remota"</string>
<string name="text_ed2e03072ce131de" formatted="false">"La ventana remota se cerró o el mantenimiento en segundo plano no se pudo iniciar y la sesión finalizó."</string>
<string name="text_678b783fb578172b" formatted="false">"Conexión remota"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Contraseña remota incorrecta"</string>
<string name="text_a2ce499295e358ae" formatted="false">"El extremo remoto ha cerrado la conexión"</string>
<string name="text_176e117df3ace734" formatted="false">"El extremo remoto no está disponible actualmente"</string>
<string name="text_d2c0694243f93249" formatted="false">"La contraseña de acceso remoto ha caducado, vuelva a conectarse."</string>
<string name="text_a5574109f0208e89" formatted="false">"Conectar"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Conexión "</string>
<string name="text_89c4766afc85125f" formatted="false">"Error de conexión"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Error de conexión · Haga clic para intentarlo de nuevo"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Conexión perdida"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Conexión finalizada"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Retardo de conexión (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Disponible cuando se conecta a un dispositivo remoto compatible."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Bloquear la pantalla horizontal después de establecer la conexión"</string>
<string name="text_f741e0536210eafa" formatted="false">"Aviso de conexión"</string>
<string name="text_485a26050ce57431" formatted="false">"Método de conexión"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Conectarse al servidor"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Conéctese al servidor para obtener los últimos anuncios."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"Se agotó el tiempo de conexión; verifique el ID del dispositivo, el servidor y el estado en línea remoto"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Conectarse al escritorio remoto"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Retroceso"</string>
<string name="text_e02926784fcedb91" formatted="false">"Anuncios"</string>
<string name="text_465a092680d132b3" formatted="false">"Algunas vistas previas no se pudieron borrar, inténtalo de nuevo."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Inténtalo de nuevo"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Reintentar guardar el estado de lectura"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Bloquear"</string>
<string name="text_864200bbb241e067" formatted="false">"Teclado"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Leer y autorizar"</string>
<string name="text_86651d17a401c55b" formatted="false">"Privacidad"</string>
<string name="text_26c6e011e343708c" formatted="false">"Privacidad y autorización"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Pantalla de privacidad"</string>
<string name="text_e157fafb76aad954" formatted="false">"Privacidad desactivada"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Privacidad activada"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Pantalla de privacidad desbloqueada"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Anormalidad en la pantalla de privacidad"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"La pantalla de privacidad no está disponible temporalmente"</string>
<string name="text_4d59be3c75189032" formatted="false">"El estado de la pantalla de privacidad ha caducado, esperando confirmación por computadora"</string>
<string name="text_1674532c70ed1db2" formatted="false">"Estado de la pantalla de privacidad por confirmar"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Política de privacidad"</string>
<string name="text_554e957e8e3c00af" formatted="false">"La política de privacidad no se puede leer temporalmente. Rechace conectarse a Internet y reinstale la aplicación."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Silencio"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Audio"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Sitio web del proyecto"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"Las vistas previas pueden contener información personal o laboral de la computadora remota. Después de encenderlo, cada conexión guardará automáticamente una imagen en la máquina y la mostrará en las conexiones recientes. Puedes cerrar y borrar en cualquier momento."</string>
<string name="text_b1c27820fec23edb" formatted="false">"Alta"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"Predeterminado"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"Desactivado de forma predeterminada. Cuando esté encendido, la pantalla entrará y permanecerá en orientación horizontal cuando el teléfono esté conectado; cuando esté apagado, seguirá la dirección del dispositivo. Las tabletas y las ventanas múltiples no se ven afectadas."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Botón derecho del mouse, presione y arrastre, suelte para finalizar el arrastre"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Botón izquierdo del mouse, presione y arrastre, suelte para finalizar el arrastre"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Control del ratón"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Rueda del mouse, deslícela hacia arriba y hacia abajo para salir del rango de teclas"</string>
<string name="text_9aa238fc6121dede" formatted="false">", tasa de pérdida de paquetes "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", enviar "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", contraseña guardada"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", recibir "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", haga clic en Reintentar"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Terminé"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Échec du transfert"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" appareils"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Article · "</string>
<string name="text_2c894057fc526576" formatted="false">" éléments non lus"</string>
<string name="text_d0ac753c6681e578" formatted="false">"Connexion à distance CrossDesk"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL version 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"Avis OpenFEC"</string>
<string name="text_69ad29820a259526" formatted="false">"Licence et source OpenFEC"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"Connexion directe P2P"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP activé"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"Relais TOUR"</string>
<string name="text_ebede975edd52aee" formatted="false">"Échec de la transmission {0}"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Appareils : {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"Éléments non lus {0}"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, réception de {1}, envoi de {2}, taux de perte de paquets {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Connecté"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Déroulez pour actualiser"</string>
<string name="text_37772187ab854191" formatted="false">"Taux de perte"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Moyenne"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Donne la priorité au maintien de la fluidité de l\'image et réduira la résolution de manière plus agressive lorsque la bande passante ou la pression d\'encodage est élevée."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Faible"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Utilisation d\'un serveur personnalisé"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Enregistrer le mot de passe"</string>
<string name="text_10019cef593819ed" formatted="false">"Enregistrer le fichier reçu"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Enregistrer les aperçus de l\'écran distant"</string>
<string name="text_0f197074373e38fd" formatted="false">"Enregistrer l\'aperçu de l\'écran distant ?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"La connexion de signalisation est interrompue, veuillez vous reconnecter."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Le chargement de l\'annonce a échoué, veuillez réessayer."</string>
<string name="text_00554b17c432937e" formatted="false">"Détails de l\'annonce"</string>
<string name="text_2428420e407fe2e8" formatted="false">"Total "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Total des éléments {0}· Éléments non lus {1}"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"À propos"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"À propos de CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"À propos, il existe une nouvelle version"</string>
<string name="text_3fd47edce45b3603" formatted="false">"Fermer"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Fermer la barre de contrôle"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Désactiver l\'écran de confidentialité"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Équilibre la clarté et la douceur, en réduisant seulement progressivement la résolution sous une pression constante."</string>
<string name="text_b92421822c213c2b" formatted="false">"Résolution"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Supprimer"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Supprimer l\'annonce"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Supprimer l\'enregistrement"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"Supprimer cette annonce ?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Actualiser l\'annonce"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Presse-papiers"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Chargement…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Charger plus"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Mise à jour conseillée"</string>
<string name="text_861521c9e107c648" formatted="false">"L\'initialisation de la connexion native a échoué"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Envoi"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Envoyer "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"Après l\'envoi, cliquez sur \"Coller\" sur le clavier distant. Le texte distant reçu peut être copié manuellement sur la machine locale."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Envoyer le fichier"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Envoyer un SMS"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Annuler"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Annuler la connexion"</string>
<string name="text_72814b15a6fa152c" formatted="false">"Chaque composant est soumis à la licence open source répertoriée, et les droits accordés ne sont pas soumis aux conditions générales d\'utilisation de cette application. La visualisation du code source nécessite une connexion Internet."</string>
<string name="text_e009c148d1890dac" formatted="false">"Total"</string>
<string name="text_81c92013219dd115" formatted="false">"Accepter et continuer"</string>
<string name="text_409696774c9998ce" formatted="false">"Restez connecté en arrière-plan, cliquez pour revenir au bureau distant"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"Échec du démarrage de Keepalive en arrière-plan, veuillez conserver l\'application au premier plan"</string>
<string name="text_d96a21463557171d" formatted="false">"L\'application Keepalive en arrière-plan n\'a pas pu démarrer. Veuillez conserver l\'application au premier plan et vous reconnecter."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"La connexion en arrière-plan est terminée, veuillez vous reconnecter."</string>
<string name="text_da904f715e1cff87" formatted="false">"Le service de connexion en arrière-plan s\'est arrêté, veuillez vous reconnecter."</string>
<string name="text_0812359e18082071" formatted="false">"Les nouveaux aperçus seront toujours enregistrés lors des connexions ultérieures."</string>
<string name="text_b9086662b1df1f20" formatted="false">"En ligne"</string>
<string name="text_39f2f444a042643e" formatted="false">"Remplissez le nom d\'hôte ou l\'adresse IPv4, sans protocole ni chemin"</string>
<string name="text_473400815c0f687d" formatted="false">"Son"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Copier le texte distant"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Maintenez toujours une résolution plus élevée et réduisez la fréquence d\'images pour garantir la clarté de l\'image lorsque la bande passante est insuffisante."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Cryptage multimédia"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Lettres et chiffres"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Échec du stockage sécurisé ou de l\'initialisation du certificat. Veuillez réessayer ou effacer les données de l\'application."</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Terminé"</string>
<string name="text_678328f6f02881c7" formatted="false">"Vous pouvez vérifier les mises à jour après avoir terminé l\'autorisation de confidentialité"</string>
<string name="text_c4361f9abdba4882" formatted="false">"Enregistrez l\'identité locale après avoir terminé l\'autorisation de confidentialité"</string>
<string name="text_c29b888e357d727d" formatted="false">"L\'enregistrement du mot de passe a échoué, cette connexion peut toujours être utilisée"</string>
<string name="text_3f31300a23afb390" formatted="false">"Les mots de passe sont enregistrés cryptés dans le Keystore Android natif"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"ID à distance"</string>
<string name="text_0512084e130984bc" formatted="false">"Identification des pairs "</string>
<string name="text_c5147d65960196b0" formatted="false">"ID homologue {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"Tous les aperçus à distance enregistrés sur cette machine seront supprimés et les enregistrements de connexion et les mots de passe seront conservés."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Orientation de l\'écran"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Développer le menu de la télécommande"</string>
<string name="text_9343612ee473edd6" formatted="false">"Connexion à distance déconnectée de la notification"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Mot de passe enregistré"</string>
<string name="text_1cbd391383283924" formatted="false">"Le mot de passe enregistré ne peut pas être lu, veuillez le saisir à nouveau."</string>
<string name="text_6792b9f341380c68" formatted="false">"Le mot de passe enregistré ne peut pas être conservé, veuillez le saisir à nouveau."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del envoyé"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"Le maintien de l\'activité en arrière-plan est activé, les applications de commutation peuvent continuer à maintenir la connexion."</string>
<string name="text_506409ec08cb2a79" formatted="false">"Keepalive en arrière-plan activé pour revenir ou se déconnecter des notifications de connexion"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Accordé"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Lire"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Connecté "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Connecté au serveur"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Fréquence d\'images"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Fluidité"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Équilibré"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"Le fichier de licence dans l\'application est manquant ou endommagé, veuillez réinstaller l\'application."</string>
<string name="text_3f2904435942cc0f" formatted="false">"La version de développement contient des modifications qui n\'ont pas encore été publiées. Le lien suivant est le code source de la version de base."</string>
<string name="text_4338afaad52ea445" formatted="false">"Activer l’enregistrement"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Activer l\'écran de confidentialité"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Composants open source"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Description des droits sur les logiciels open source"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"Il s\'agit actuellement de la dernière version"</string>
<string name="text_24a52788cf474bf6" formatted="false">"Actuellement, l\'extrémité distante ne prend pas en charge la saisie directe de ce texte. Veuillez utiliser une version de l\'extrémité contrôlée qui prend en charge la saisie de texte ou envoyez-la via le presse-papiers."</string>
<string name="text_33f25b500697a7d9" formatted="false">"Une fois la connexion réussie, l\'appareil distant le plus proche sera affiché ici."</string>
<string name="text_70341256d7317e7d" formatted="false">"J\'ai lu et accepté la politique de confidentialité"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Fichier sélectionné (impossible en lecture ou espace insuffisant)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"L\'ID de l\'appareil n\'a pas été trouvé"</string>
<string name="text_97b7b18df07b9787" formatted="false">"L\'envoi de la commande a échoué, veuillez réessayer"</string>
<string name="text_cb782027ca5986c7" formatted="false">"La commande a été envoyée, en attente de confirmation informatique..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Statut d\'autorisation"</string>
<string name="text_6e684586884ebee6" formatted="false">"Réception"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"recevoir "</string>
<string name="text_d16488b987723161" formatted="false">"Décodeur local"</string>
<string name="text_6b58198955259066" formatted="false">"Retirer"</string>
<string name="text_c52e42ee856825db" formatted="false">"Après le retrait, la connexion à distance sera arrêtée, l\'aperçu de l\'écran sera fermé et l\'aperçu existant sera effacé. L\'enregistrement de connexion et le mot de passe seront conservés et une autorisation sera à nouveau requise avant de se reconnecter."</string>
<string name="text_61f4788032ec596b" formatted="false">"Retirer l\'autorisation réseau"</string>
<string name="text_e736e419008ac10a" formatted="false">"Retirer l\'autorisation réseau ?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Réduire le menu de la télécommande"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Masquer le clavier"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Masquer la souris"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Données"</string>
<string name="text_94244f41d0a41378" formatted="false">"L\'enregistrement du fichier a échoué, veuillez réessayer"</string>
<string name="text_2553a785fef2a405" formatted="false">"Fichier enregistré"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"La sélection de fichier a expiré et la session s\'est terminée"</string>
<string name="text_985a5c380be49f4f" formatted="false">"Le texte dépasse 128 Ko"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Déconnecter"</string>
<string name="text_09b1395f50589327" formatted="false">"Reviendra à la page d\'accueil après la déconnexion"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"Déconnecter la connexion à distance ?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Déconnecter"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"Nouvelle version"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"Nouvelle version disponible : v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"Nouvelle version disponible : v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"Aucune annonce non lue"</string>
<string name="text_dca2c418ac967366" formatted="false">"Impossible d\'enregistrer l\'état de lecture localement. Veuillez réessayer."</string>
<string name="text_5c92da76360056cc" formatted="false">"Impossible de supprimer cette annonce. Veuillez réessayer."</string>
<string name="text_615709dd3129313c" formatted="false">"Impossible d\'établir une connexion à distance"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"Impossible de lire la licence open source"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Écran"</string>
<string name="text_ffe21a671190323e" formatted="false">"Afficher le mot de passe"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Pas maintenant"</string>
<string name="text_6d9a7758610a2432" formatted="false">"Aucune annonce"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Connexions récentes"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Serveur"</string>
<string name="text_dfe408e686175095" formatted="false">"La vérification du certificat Server TLS a échoué"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Adresse du serveur"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Échec de la connexion au serveur, veuillez vérifier l\'adresse du serveur et le réseau"</string>
<string name="text_d270868960e68629" formatted="false">"Port du serveur"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"La connexion au serveur a expiré, veuillez vérifier l\'adresse du serveur et le réseau"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"Non activé"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"Aucun navigateur trouvé pour ouvrir le lien"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"Non autorisé"</string>
<string name="text_4d0680f9efaef147" formatted="false">"Non lu"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Serveur non connecté · Cliquez pour réessayer"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"Cette application utilise OpenFEC pour la correction d\'erreur directe sousCeCILL-C1.0, qui offre des garanties limitées et limite la responsabilité. Consultez la licence complète pour plus de détails."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"ID local "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"ID local {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Code source et instructions de construction pour cette version"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"La révision source n\'est pas disponible pour cette version."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Relâchez pour actualiser"</string>
<string name="text_47077144715b7074" formatted="false">"Instructions de construction"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Créer des recettes et des correctifs de projet"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"Afficher le code source"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"La vérification a échoué. Veuillez réessayer plus tard."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Rechercher des mises à jour"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Fermeture de l\'écran de confidentialité…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Établir une connexion sécurisée…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"Activation de l\'écran de confidentialité..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Vérification des mises à jour…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Effacement..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Obtention d\'une pièce d\'identité locale..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Lecture du fichier sélectionné..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Connexion"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Connexion au serveur de signalisation..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Connexion au serveur..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Reconnexion au serveur..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"Aucun sélecteur de fichiers disponible pour cet appareil"</string>
<string name="text_bce2377283c2455a" formatted="false">"Effacer"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Supprimer les aperçus"</string>
<string name="text_ecc809e44637de18" formatted="false">"Supprimer les aperçus ?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Code source et instructions de construction"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Version du code source"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Version"</string>
<string name="text_16410ee9663fad10" formatted="false">"Déclaration de droits d\'auteur et de licence"</string>
<string name="text_11de77e1e41e680f" formatted="false">"Clavier d\'ordinateur"</string>
<string name="text_20f57900234debd7" formatted="false">"Qualité"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Préférence vidéo"</string>
<string name="text_82a5600c470179fa" formatted="false">"Latence vidéo"</string>
<string name="text_33b458faaf967e15" formatted="false">"Paramètres vidéo"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Qualité vidéo"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Fréquence d\'images de capture vidéo"</string>
<string name="text_66062971234f422d" formatted="false">"Pavé tactile"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Souris relative"</string>
<string name="text_de32e20193ad1042" formatted="false">"Compris"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"D\'accord"</string>
<string name="text_36f33adaf0942634" formatted="false">"Confirmer"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Confirmer la connexion"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Hors ligne"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Espace"</string>
<string name="text_471a579e8d87faac" formatted="false">"La plage de ports est comprise entre 1 et 65 535."</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Symboles et touches"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Attendez que l\'ordinateur revienne à l\'état de l\'écran de confidentialité ; les ordinateurs plus anciens peuvent ne pas le prendre en charge."</string>
<string name="text_5ee880e2524a983a" formatted="false">"En attente d\'informations sur l\'écran distant"</string>
<string name="text_674c723a85ee7f57" formatted="false">"En attente de l\'écran distant..."</string>
<string name="text_119f0483da4756c4" formatted="false">"En attente d\'une autorisation de confidentialité"</string>
<string name="text_7916071c1a8ee458" formatted="false">"Clavier système"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Toucher direct"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Souris absolue"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Réseau"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Personnalisé"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Souris virtuelle"</string>
<string name="text_62c199f37bf95869" formatted="false">"L\'écran de confidentialité à distance a été déverrouillé à l\'aide du raccourci local. Verrouillez-le à nouveau pour restaurer la confidentialité et désactiver le déverrouillage des raccourcis locaux."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"La version contrôlée est trop basse, veuillez mettre à niveau vers la dernière version."</string>
<string name="text_d814476826827b78" formatted="false">"Encodeur déporté"</string>
<string name="text_488402883e27f6f0" formatted="false">"Texte à envoyer à l\'extrémité distante"</string>
<string name="text_c20f7618d330a854" formatted="false">"Vidéo"</string>
<string name="text_310c625425f014d8" formatted="false">"Licence"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Licences et mentions de droits d\'auteur"</string>
<string name="text_e24134b643a17975" formatted="false">"La licence et la notice de copyright sont disponibles hors ligne."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"L\'identité de l\'appareil n\'a pas pu être enregistrée. Veuillez vérifier le stockage sécurisé."</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"L\'identité de l\'appareil et les mots de passe d\'accès enregistrés sélectionnés sont cryptés à l\'aide d\'Android Keystore. Les enregistrements de connexion sont uniquement enregistrés localement et ne sont pas inclus dans la sauvegarde de l\'appareil. Le contenu du presse-papiers est effacé à la fin de la session. Restez en session avec des notifications d\'image dans l\'image ou connectées après avoir quitté l\'application, et déconnectez-vous de l\'application ou des notifications."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Réglages"</string>
<string name="text_4f8771952222a011" formatted="false">"Paramètres, il existe une nouvelle version"</string>
<string name="text_f4f712d5be91721a" formatted="false">"Mot de passe d\'accès"</string>
<string name="text_dd6c465847af1b60" formatted="false">"Le mot de passe d\'accès est erroné, veuillez le saisir à nouveau."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Échec de la vérification du certificat · Cliquez pour réessayer"</string>
<string name="text_44f3d4d52347095a" formatted="false">"Veuillez d\'abord remplir l\'autorisation en ligne"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Veuillez activer le service système CrossDesk sur l\'extrémité distante et réessayer."</string>
<string name="text_990fd23c62de5ccf" formatted="false">"Le délai de demande a expiré et l\'état de l\'écran de confidentialité de l\'ordinateur n\'a pas été confirmé."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Veuillez attendre que le fichier actuel soit envoyé."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Veuillez saisir un port compris entre 1 et 65535."</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Saisissez un identifiant d\'appareil à 9 chiffres"</string>
<string name="text_285a0714894d2899" formatted="false">"Veuillez saisir un mot de passe d\'accès valide"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"Impossible d\'appliquer les paramètres. Essayer à nouveau."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Licence du logiciel"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Retour"</string>
<string name="text_7565f94031582c7c" formatted="false">"Retour à la barre de contrôle"</string>
<string name="text_55113b6925c362fd" formatted="false">"Aucun enregistrement de connexion pour l\'instant"</string>
<string name="text_3aa527b2106cc987" formatted="false">"Cette annonce a été mise à jour ou supprimée, veuillez revenir à la liste des annonces pour la consulter."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"La tâche distante est fermée"</string>
<string name="text_c255841f68096d3b" formatted="false">"Bureau à distance"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Zone tactile du bureau à distance"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Bureau à distance : touchez et cliquez, faites glisser pour déplacer, appuyez et maintenez le bouton droit enfoncé, pincez pour zoomer, zoomez et faites glisser l\'écran avec deux doigts, faites glisser de haut en bas avec deux doigts pour faire défiler à l\'échelle d\'origine."</string>
<string name="text_3535087994d050de" formatted="false">"Ordinateur distant"</string>
<string name="text_ed2e03072ce131de" formatted="false">"La fenêtre distante a été fermée ou le keepalive en arrière-plan n\'a pas pu démarrer et la session est terminée."</string>
<string name="text_678b783fb578172b" formatted="false">"Connexion à distance"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Mot de passe distant incorrect"</string>
<string name="text_a2ce499295e358ae" formatted="false">"L\'extrémité distante a fermé la connexion"</string>
<string name="text_176e117df3ace734" formatted="false">"L\'extrémité distante est actuellement indisponible"</string>
<string name="text_d2c0694243f93249" formatted="false">"Le mot de passe d\'accès à distance a expiré, veuillez vous reconnecter."</string>
<string name="text_a5574109f0208e89" formatted="false">"Connecter"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Connexion "</string>
<string name="text_89c4766afc85125f" formatted="false">"Échec de la connexion"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Échec de la connexion · Cliquez pour réessayer"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Connexion perdue"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Connexion terminée"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Délai de connexion (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Disponible lors de la connexion à un appareil distant pris en charge."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Verrouiller l\'écran paysage une fois la connexion établie"</string>
<string name="text_f741e0536210eafa" formatted="false">"Invite de connexion"</string>
<string name="text_485a26050ce57431" formatted="false">"Méthode de connexion"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Connectez-vous au serveur"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Connectez-vous au serveur pour recevoir les dernières annonces."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"La connexion a expiré. Veuillez vérifier l\'ID de l\'appareil, le serveur et l\'état en ligne à distance."</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Connectez-vous au bureau à distance"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Retour arrière"</string>
<string name="text_e02926784fcedb91" formatted="false">"Annonces"</string>
<string name="text_465a092680d132b3" formatted="false">"Certains aperçus n\'ont pas pu être effacés, veuillez réessayer."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Réessayez"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Réessayez d\'enregistrer l\'état de lecture"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Verrouillage"</string>
<string name="text_864200bbb241e067" formatted="false">"Clavier"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Lire et autoriser"</string>
<string name="text_86651d17a401c55b" formatted="false">"Confidentialité"</string>
<string name="text_26c6e011e343708c" formatted="false">"Confidentialité et autorisation"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Écran de confidentialité"</string>
<string name="text_e157fafb76aad954" formatted="false">"Confidentialité inactive"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Confidentialité active"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Écran de confidentialité déverrouillé"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Anomalie de l\'écran de confidentialité"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"L\'écran de confidentialité est temporairement indisponible"</string>
<string name="text_4d59be3c75189032" formatted="false">"L\'état de l\'écran de confidentialité a expiré, en attente de confirmation par l\'ordinateur"</string>
<string name="text_1674532c70ed1db2" formatted="false">"Statut de l\'écran de confidentialité à confirmer"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Politique de confidentialité"</string>
<string name="text_554e957e8e3c00af" formatted="false">"La politique de confidentialité ne peut pas être lue temporairement. Veuillez refuser de vous connecter à Internet et réinstaller l\'application."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Muet"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Audio"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Site Web du projet"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"Les aperçus peuvent contenir des informations personnelles ou professionnelles provenant de l\'ordinateur distant. Après l\'avoir allumé, chaque connexion enregistrera automatiquement une image sur la machine et l\'affichera dans les connexions récentes. Vous pouvez fermer et effacer à tout moment."</string>
<string name="text_b1c27820fec23edb" formatted="false">"Élevée"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"Par défaut"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"Désactivé par défaut. Lorsqu\'il est allumé, l\'écran entre et reste en orientation paysage lorsque le téléphone est connecté ; lorsqu\'il est éteint, il suivra la direction de l\'appareil. Les tablettes et multi-fenêtres ne sont pas concernées."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Bouton droit de la souris, appuyez et faites glisser, relâchez pour terminer le glisser"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Bouton gauche de la souris, appuyez et faites glisser, relâchez pour terminer le glisser"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Contrôle de la souris"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Molette de la souris, faites-la glisser de haut en bas pour sortir de la plage de touches"</string>
<string name="text_9aa238fc6121dede" formatted="false">", taux de perte de paquets "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", envoyer "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", mot de passe enregistré"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", recevoir "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", cliquez sur Réessayer"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" ・完了"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" 転送に失敗しました"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" 台のデバイス"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" 記事 ・ "</string>
<string name="text_2c894057fc526576" formatted="false">" 未読項目"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDeskリモート接続"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL バージョン 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC通知"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFECライセンスとソース"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P直接接続"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRT有効"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"ターンリレー"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0}送信失敗"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"デバイス数: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0}の未読アイテム"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}、受信{1}、送信{2}、パケットロス率{3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● 接続されました"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"プルダウンして更新します"</string>
<string name="text_37772187ab854191" formatted="false">"損失率"</string>
<string name="text_a567bdaa11367f26" formatted="false">"中"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"画像を滑らかに保つことを優先し、帯域幅やエンコード圧力が高い場合には、より積極的に解像度を下げます。"</string>
<string name="text_aa9e366f68d3d097" formatted="false">"低"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"カスタムサーバーの使用"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"パスワードの保存"</string>
<string name="text_10019cef593819ed" formatted="false">"受信したファイルを保存する"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"リモート画面のプレビューを保存する"</string>
<string name="text_0f197074373e38fd" formatted="false">"リモート画面のプレビューを保存しますか?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"信号接続が中断されました。再接続してください。"</string>
<string name="text_789f22a8b1b34797" formatted="false">"お知らせを読み込めませんでした。再試行してください。"</string>
<string name="text_00554b17c432937e" formatted="false">"発表内容"</string>
<string name="text_2428420e407fe2e8" formatted="false">"合計 "</string>
<string name="text_d977c7568cc5201e" formatted="false">"合計{0}個のアイテム ·{1}個の未読アイテム"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"概要"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"CrossDesk について"</string>
<string name="text_390a95b65eb33052" formatted="false">"について、新しいバージョンがあります"</string>
<string name="text_3fd47edce45b3603" formatted="false">"閉じる"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"コントロールバーを閉じる"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"プライバシー画面を無効にする"</string>
<string name="text_7d466b69be42fc43" formatted="false">"明瞭さと滑らかさのバランスを保ち、一定の圧力下で解像度を徐々に低下させるだけです。"</string>
<string name="text_b92421822c213c2b" formatted="false">"解像度"</string>
<string name="text_2f9daa828907b93f" formatted="false">"削除"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"お知らせの削除"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"レコードの削除"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"このお知らせを削除しますか?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"リフレッシュのお知らせ"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"クリップボード"</string>
<string name="text_4927a53bcc886afb" formatted="false">"読み込み中…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"さらに読み込む"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"更新のご案内"</string>
<string name="text_861521c9e107c648" formatted="false">"ネイティブ接続の初期化に失敗しました"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"送信"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"送信 "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"送信後、リモートキーボードの「貼り付け」をクリックしてください。受信したリモート テキストはローカル マシンに手動でコピーできます。"</string>
<string name="text_e7281fbca7af369c" formatted="false">"ファイル送信"</string>
<string name="text_ce6c02d19022e016" formatted="false">"テキストを送信する"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"キャンセル"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"接続をキャンセルする"</string>
<string name="text_72814b15a6fa152c" formatted="false">"各コンポーネントは、リストされているオープン ソース ライセンスの対象となり、付与された権利は、このアプリケーションの一般的な使用条件の対象ではありません。ソース コードを表示するには、インターネット接続が必要です。"</string>
<string name="text_e009c148d1890dac" formatted="false">"合計"</string>
<string name="text_81c92013219dd115" formatted="false">"同意して続行"</string>
<string name="text_409696774c9998ce" formatted="false">"バックグラウンドで接続を維持し、クリックしてリモート デスクトップに戻ります"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"バックグラウンドのキープアライブを開始できませんでした。アプリケーションをフォアグラウンドで維持してください。"</string>
<string name="text_d96a21463557171d" formatted="false">"バックグラウンド キープアライブの開始に失敗しました。アプリケーションをフォアグラウンドに保ち、再接続してください。"</string>
<string name="text_952f7300de0a7bd9" formatted="false">"バックグラウンド接続が終了しました。再接続してください。"</string>
<string name="text_da904f715e1cff87" formatted="false">"バックグラウンド接続サービスが停止しました。再接続してください。"</string>
<string name="text_0812359e18082071" formatted="false">"新しいプレビューはその後の接続でも保存されます。"</string>
<string name="text_b9086662b1df1f20" formatted="false">"オンライン"</string>
<string name="text_39f2f444a042643e" formatted="false">"プロトコルとパスを除いて、ホスト名または IPv4 アドレスを入力します。"</string>
<string name="text_473400815c0f687d" formatted="false">"サウンド"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"リモートテキストをコピーする"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"帯域幅が不十分な場合は、常に高い解像度を維持し、フレーム レートを下げて画像の鮮明さを確保します。"</string>
<string name="text_7ff73de85ec16d15" formatted="false">"メディアの暗号化"</string>
<string name="text_22dc6217af77bc37" formatted="false">"英数字"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"安全なストレージまたは証明書の初期化に失敗しました。再試行するか、アプリケーション データをクリアしてください。"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"完了"</string>
<string name="text_678328f6f02881c7" formatted="false">"プライバシー認証を完了すると、アップデートを確認できます"</string>
<string name="text_c4361f9abdba4882" formatted="false">"プライバシー認証完了後、ローカル ID を登録します"</string>
<string name="text_c29b888e357d727d" formatted="false">"パスワードの保存に失敗しました。この接続はまだ使用できます"</string>
<string name="text_3f31300a23afb390" formatted="false">"パスワードは暗号化されてネイティブ Android キーストアに保存されます"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"リモートID"</string>
<string name="text_0512084e130984bc" formatted="false">"ピアID "</string>
<string name="text_c5147d65960196b0" formatted="false">"ピアID{0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"本機に保存されているリモートプレビューはすべて削除され、接続記録とパスワードは保持されます。"</string>
<string name="text_60cc99cc0013972e" formatted="false">"画面の向き"</string>
<string name="text_9e7294d264afecb7" formatted="false">"リモコンメニューを展開する"</string>
<string name="text_9343612ee473edd6" formatted="false">"通知によりリモート接続が切断されました"</string>
<string name="text_9d32476fe83aef60" formatted="false">"パスワード保存済み"</string>
<string name="text_1cbd391383283924" formatted="false">"保存したパスワードが読み取れないため、再入力してください。"</string>
<string name="text_6792b9f341380c68" formatted="false">"保存したパスワードは保持できませんので、再度入力してください。"</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Delを送信しました"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"バックグラウンドキープアライブが有効になり、アプリケーションを切り替えて接続を維持し続けることができます。"</string>
<string name="text_506409ec08cb2a79" formatted="false">"バックグラウンドキープアライブを有効にして、接続通知を返すか接続を切断することができます"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"認められる"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"読む"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"接続されました "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"サーバーに接続しました"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"フレームレート"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"滑らかさ"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"バランス"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"アプリケーション内のライセンス ファイルが見つからないか破損しています。アプリケーションを再インストールしてください。"</string>
<string name="text_3f2904435942cc0f" formatted="false">"開発バージョンには、まだリリースされていない修正が含まれています。以下のリンクは基本バージョンのソースコードです。"</string>
<string name="text_4338afaad52ea445" formatted="false">"保存を有効にする"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"プライバシー画面を有効にする"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"オープンソースコンポーネント"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"オープンソース ソフトウェアの権利の説明"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"これが現在の最新バージョンです"</string>
<string name="text_24a52788cf474bf6" formatted="false">"現在、リモート エンドはこのテキストの直接入力をサポートしていません。テキスト入力をサポートするバージョンのコントロール エンドを使用するか、クリップボード経由で送信してください。"</string>
<string name="text_33f25b500697a7d9" formatted="false">"接続に成功すると、最も近いリモート デバイスがここに表示されます。"</string>
<string name="text_70341256d7317e7d" formatted="false">"プライバシーポリシーを読んで同意します"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"選択したファイル(読み込み不可、または容量不足)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"デバイスIDが見つかりませんでした"</string>
<string name="text_97b7b18df07b9787" formatted="false">"コマンドの送信に失敗しました。もう一度お試しください。"</string>
<string name="text_cb782027ca5986c7" formatted="false">"コマンドが送信され、コンピューターの確認を待っています..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"認可ステータス"</string>
<string name="text_6e684586884ebee6" formatted="false">"受信"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"受け取る "</string>
<string name="text_d16488b987723161" formatted="false">"ローカルデコーダ"</string>
<string name="text_6b58198955259066" formatted="false">"撤回"</string>
<string name="text_c52e42ee856825db" formatted="false">"退会後はリモート接続が停止され、画面プレビューが閉じられ、既存のプレビューが消去されます。接続記録とパスワードは保持され、再接続する前に再度認証が必要になります。"</string>
<string name="text_61f4788032ec596b" formatted="false">"ネットワーク認証の撤回"</string>
<string name="text_e736e419008ac10a" formatted="false">"ネットワーク認証を取り消しますか?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"リモコンメニューを折りたたむ"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"キーボードを隠す"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"マウスを隠す"</string>
<string name="text_5440f7424f2865bf" formatted="false">"データ"</string>
<string name="text_94244f41d0a41378" formatted="false">"ファイルの保存に失敗しました。もう一度お試しください。"</string>
<string name="text_2553a785fef2a405" formatted="false">"ファイルを保存しました"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"ファイル選択がタイムアウトし、セッションが終了しました"</string>
<string name="text_985a5c380be49f4f" formatted="false">"テキストが128KBを超えています"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"切断"</string>
<string name="text_09b1395f50589327" formatted="false">"切断後ホームページに戻ります"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"リモート接続を切断しますか?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"切断"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"新しいバージョン"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"利用可能な新しいバージョン: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"利用可能な新しいバージョン: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"未読のお知らせはありません"</string>
<string name="text_dca2c418ac967366" formatted="false">"読み取りステータスをローカルに保存できません。もう一度試してください。"</string>
<string name="text_5c92da76360056cc" formatted="false">"このお知らせは削除できません。もう一度試してください。"</string>
<string name="text_615709dd3129313c" formatted="false">"リモート接続を開始できません"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"オープンソースライセンスを読み取れません"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"ディスプレイ"</string>
<string name="text_ffe21a671190323e" formatted="false">"パスワードを表示"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"今は同意しない"</string>
<string name="text_6d9a7758610a2432" formatted="false">"アナウンスはありません"</string>
<string name="text_0b290c6600d92f43" formatted="false">"最近の接続"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"サーバー"</string>
<string name="text_dfe408e686175095" formatted="false">"ServerTLS証明書の検証に失敗しました"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"サーバーアドレス"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"サーバーへのログインに失敗しました。サーバーのアドレスとネットワークを確認してください。"</string>
<string name="text_d270868960e68629" formatted="false">"サーバーポート"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"サーバー接続がタイムアウトしました。サーバーのアドレスとネットワークを確認してください。"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"有効ではありません"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"リンクを開くブラウザが見つかりませんでした"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"無許可"</string>
<string name="text_4d0680f9efaef147" formatted="false">"未読"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"サーバーが接続されていません · クリックして再試行してください"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"このアプリケーションは、限定保証を提供し、責任を制限するCeCILL-C1.0 に基づく前方誤り訂正にOpenFECを使用します。詳細については、完全なライセンスを参照してください。"</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"ローカルID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"ローカルID{0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"このバージョンのソース コードとビルド手順"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"このビルドではソース リビジョンが利用できません。"</string>
<string name="text_f0e274aa130e08a2" formatted="false">"放してリフレッシュ"</string>
<string name="text_47077144715b7074" formatted="false">"組み立て説明書"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"ビルドレシピとプロジェクトパッチ"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"ソースコードを表示する"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"チェックに失敗しました。後でもう一度試してください。"</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"アップデートを確認する"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"プライバシー画面を閉じています…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"安全な接続を確立中…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"プライバシー画面を有効化中…"</string>
<string name="text_91834aebecabcbc3" formatted="false">"アップデートを確認しています…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"クリア中..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"ローカルIDを取得中..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"選択したファイルを読み込み中..."</string>
<string name="text_694186911ae3e4da" formatted="false">"接続する"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"シグナリングサーバーに接続しています..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"サーバーに接続中..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"サーバーに再接続中..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"このデバイスでは使用できるファイル ピッカーがありません"</string>
<string name="text_bce2377283c2455a" formatted="false">"クリア"</string>
<string name="text_ee0e76b8507212de" formatted="false">"プレビューを削除"</string>
<string name="text_ecc809e44637de18" formatted="false">"プレビューを削除しますか?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"ソースコードとビルド手順"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"ソースコードのバージョン"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"バージョン"</string>
<string name="text_16410ee9663fad10" formatted="false">"著作権およびライセンスに関する声明"</string>
<string name="text_11de77e1e41e680f" formatted="false">"コンピューターのキーボード"</string>
<string name="text_20f57900234debd7" formatted="false">"画質"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"ビデオ設定"</string>
<string name="text_82a5600c470179fa" formatted="false">"映像遅延"</string>
<string name="text_33b458faaf967e15" formatted="false">"ビデオ設定"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"ビデオ品質"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"ビデオキャプチャフレームレート"</string>
<string name="text_66062971234f422d" formatted="false">"トラックパッド"</string>
<string name="text_c421dc051bea3f11" formatted="false">"相対マウス"</string>
<string name="text_de32e20193ad1042" formatted="false">"分かりました"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"OK"</string>
<string name="text_36f33adaf0942634" formatted="false">"確認する"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"接続を確認する"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"オフライン"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"スペース"</string>
<string name="text_471a579e8d87faac" formatted="false">"ポート範囲は 1 ~ 65535 です。"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"記号・機能"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"コンピューターがプライバシー画面の状態に戻るまで待ちます。古いコンピューターではサポートされていない可能性があります。"</string>
<string name="text_5ee880e2524a983a" formatted="false">"リモート画面情報待ち"</string>
<string name="text_674c723a85ee7f57" formatted="false">"リモート画面を待っています..."</string>
<string name="text_119f0483da4756c4" formatted="false">"プライバシー認証待ち"</string>
<string name="text_7916071c1a8ee458" formatted="false">"システムキーボード"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"直接タッチ"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"アブソリュートマウス"</string>
<string name="text_872428a7210d5e0c" formatted="false">"ネットワーク"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"カスタム"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"仮想マウス"</string>
<string name="text_62c199f37bf95869" formatted="false">"ローカル ショートカットを使用してリモート プライバシー画面のロックが解除されました。再度ロックするとプライバシーが回復され、ローカル ショートカットのロック解除が無効になります。"</string>
<string name="text_a569ce729c1d6e19" formatted="false">"管理されているバージョンが低すぎるため、最新バージョンにアップグレードしてください。"</string>
<string name="text_d814476826827b78" formatted="false">"リモートエンコーダー"</string>
<string name="text_488402883e27f6f0" formatted="false">"相手側に送信するテキスト"</string>
<string name="text_c20f7618d330a854" formatted="false">"ビデオ"</string>
<string name="text_310c625425f014d8" formatted="false">"ライセンス"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"ライセンスと著作権表示"</string>
<string name="text_e24134b643a17975" formatted="false">"ライセンスと著作権表示はオフラインでも利用できます。"</string>
<string name="text_5e7628f043ce71d0" formatted="false">"デバイス ID を保存できませんでした。安全なストレージを確認してください。"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"デバイス ID と選択された保存されたアクセス パスワードは、Android キーストアを使用して暗号化されます。接続記録はローカルにのみ保存され、デバイスのバックアップには含まれません。クリップボードの内容はセッションが終了するとクリアされます。アプリを終了した後もピクチャーインピクチャーまたは接続された通知でセッションを維持し、アプリまたは通知内で切断します。"</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"設定"</string>
<string name="text_4f8771952222a011" formatted="false">"設定、新しいバージョンがあります"</string>
<string name="text_f4f712d5be91721a" formatted="false">"アクセスパスワード"</string>
<string name="text_dd6c465847af1b60" formatted="false">"アクセスパスワードが間違っていますので、再度入力してください。"</string>
<string name="text_defba1d2989bfab4" formatted="false">"証明書の検証に失敗しました · クリックして再試行してください"</string>
<string name="text_44f3d4d52347095a" formatted="false">"最初にオンライン認証を完了してください"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"リモート エンドでCrossDeskシステム サービスを有効にして、再試行してください。"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"リクエストがタイムアウトし、コンピュータのプライバシー画面の状態が確認できませんでした。"</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"現在のファイルが送信されるまでお待ちください。"</string>
<string name="text_9305d9d72b06110d" formatted="false">"1 ~ 65535 の範囲でポートを入力してください"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"9桁のデバイスIDを入力"</string>
<string name="text_285a0714894d2899" formatted="false">"有効なアクセスパスワードを入力してください"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"設定を適用できませんでした。もう一度やり直してください。"</string>
<string name="text_a762e7e650e285fa" formatted="false">"ソフトウェアライセンス"</string>
<string name="text_572cf45ba43634b3" formatted="false">"戻る"</string>
<string name="text_7565f94031582c7c" formatted="false">"コントロールバーに戻る"</string>
<string name="text_55113b6925c362fd" formatted="false">"まだ接続実績がありません"</string>
<string name="text_3aa527b2106cc987" formatted="false">"このお知らせは更新または削除されました。表示するにはお知らせリストに戻ってください。"</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"リモートタスクが終了しました"</string>
<string name="text_c255841f68096d3b" formatted="false">"リモートデスクトップ"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"リモートデスクトップタッチエリア"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"リモート デスクトップ: タッチしてクリック、ドラッグして移動、右ボタンを押したまま、ピンチしてズーム、ズームインして 2 本の指で画面をドラッグ、2 本の指で上下にスライドして元のスケールでスクロール"</string>
<string name="text_3535087994d050de" formatted="false">"リモートコンピュータ"</string>
<string name="text_ed2e03072ce131de" formatted="false">"リモート ウィンドウが閉じられたか、バックグラウンド キープアライブの開始に失敗し、セッションが終了しました。"</string>
<string name="text_678b783fb578172b" formatted="false">"リモート接続"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"リモートパスワードが正しくありません"</string>
<string name="text_a2ce499295e358ae" formatted="false">"リモートエンドが接続を閉じました"</string>
<string name="text_176e117df3ace734" formatted="false">"リモートエンドは現在利用できません"</string>
<string name="text_d2c0694243f93249" formatted="false">"リモートアクセスパスワードの有効期限が切れています。再接続してください。"</string>
<string name="text_a5574109f0208e89" formatted="false">"接続"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"接続 "</string>
<string name="text_89c4766afc85125f" formatted="false">"接続に失敗しました"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"接続に失敗しました · クリックして再試行してください"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"接続が失われました"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"接続が終了しました"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"接続遅延 (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"サポートされているリモート デバイスに接続されている場合に使用できます。"</string>
<string name="text_2123a9ed718f2c40" formatted="false">"接続完了後、横画面をロックする"</string>
<string name="text_f741e0536210eafa" formatted="false">"接続プロンプト"</string>
<string name="text_485a26050ce57431" formatted="false">"接続方法"</string>
<string name="text_5a048351a77fe88d" formatted="false">"サーバーに接続する"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"サーバーに接続して最新のお知らせを取得します。"</string>
<string name="text_32cd27b5a72515f3" formatted="false">"接続がタイムアウトしました。デバイス ID、サーバー、リモートのオンライン ステータスを確認してください。"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"リモートデスクトップに接続する"</string>
<string name="text_d5407b4f3c666681" formatted="false">"バックスペース"</string>
<string name="text_e02926784fcedb91" formatted="false">"お知らせ"</string>
<string name="text_465a092680d132b3" formatted="false">"一部のプレビューをクリアできませんでした。もう一度お試しください。"</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"もう一度お試しください"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"読み取りステータスの保存をリトライする"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"ロック"</string>
<string name="text_864200bbb241e067" formatted="false">"キーボード"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"読んで承認する"</string>
<string name="text_86651d17a401c55b" formatted="false">"プライバシー"</string>
<string name="text_26c6e011e343708c" formatted="false">"プライバシーと認証"</string>
<string name="text_6cad42fd3da85585" formatted="false">"プライバシー画面"</string>
<string name="text_e157fafb76aad954" formatted="false">"プライバシー画面:オフ"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"プライバシー画面:オン"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"プライバシー画面のロックが解除されました"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"プライバシー画面異常"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"プライバシー画面が一時的に利用できなくなります"</string>
<string name="text_4d59be3c75189032" formatted="false">"プライバシー画面のステータスが期限切れになり、コンピュータの確認を待っています"</string>
<string name="text_1674532c70ed1db2" formatted="false">"プライバシー画面のステータスを確認する"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"プライバシーポリシー"</string>
<string name="text_554e957e8e3c00af" formatted="false">"一時的にプライバシーポリシーが閲覧できなくなります。インターネットへの接続を拒否し、アプリケーションを再インストールしてください。"</string>
<string name="text_7db2e189ecb01065" formatted="false">"ミュート"</string>
<string name="text_296c632ec857a0ba" formatted="false">"オーディオ"</string>
<string name="text_0cadb49096a382e0" formatted="false">"プロジェクトWebサイト"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"プレビューには、リモート コンピューターからの個人情報または仕事情報が含まれる場合があります。オンにすると、接続するたびにマシンに写真が自動的に保存され、最近の接続で表示されます。いつでも閉じてクリアできます。"</string>
<string name="text_b1c27820fec23edb" formatted="false">"高"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"デフォルト"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"デフォルトではオフです。オンにすると、電話が接続されているときに画面が横向きになり、横向きのままになります。オフにすると、デバイスの方向に従います。タブレットやマルチウィンドウは影響を受けません。"</string>
<string name="text_1184a97edd8e1646" formatted="false">"マウスの右ボタンを押してドラッグし、放してドラッグを終了します"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"マウスの左ボタンを押してドラッグし、放してドラッグを終了します"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"マウスコントロール"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"マウスホイール、上下にスライドしてキー範囲外にスライドします"</string>
<string name="text_9aa238fc6121dede" formatted="false">"、パケットロス率 "</string>
<string name="text_0b5d16c21095fe10" formatted="false">"、送信します "</string>
<string name="text_ba9a792d063a8c86" formatted="false">"、パスワードが保存されました"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">"、受け取ります "</string>
<string name="text_42237b5ec70c26f3" formatted="false">"、「再試行」をクリックします"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · 완료"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" 전송 실패"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" 장치"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" 기사 · "</string>
<string name="text_2c894057fc526576" formatted="false">" 읽지 않은 항목"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDesk원격접속"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL 버전 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC공지"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFEC라이선스 및 소스"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P 직접 연결"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP 활성화됨"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"TURN 릴레이"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0}전송 실패"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"기기: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0}읽지 않은 항목"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, 수신{1}, 송신{2}, 패킷 손실률{3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● 연결됨"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"아래로 당겨 새로고침"</string>
<string name="text_37772187ab854191" formatted="false">"손실률"</string>
<string name="text_a567bdaa11367f26" formatted="false">"중간"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"영상을 부드럽게 유지하는 데 우선 순위를 두며 대역폭이나 인코딩 압력이 높을 때 해상도를 더욱 적극적으로 줄입니다."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"낮음"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"커스텀 서버 이용하기"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"비밀번호 저장"</string>
<string name="text_10019cef593819ed" formatted="false">"수신된 파일 저장"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"원격 화면 미리보기 저장"</string>
<string name="text_0f197074373e38fd" formatted="false">"원격 화면 미리보기를 저장하시겠습니까?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"신호 연결이 중단되었습니다. 다시 연결하십시오."</string>
<string name="text_789f22a8b1b34797" formatted="false">"공지사항 로딩에 실패했습니다. 다시 시도해주세요."</string>
<string name="text_00554b17c432937e" formatted="false">"공지사항"</string>
<string name="text_2428420e407fe2e8" formatted="false">"합계 "</string>
<string name="text_d977c7568cc5201e" formatted="false">"총{0}항목 ·{1}읽지 않은 항목"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"소개"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"CrossDesk 정보"</string>
<string name="text_390a95b65eb33052" formatted="false">"새 버전이 있습니다."</string>
<string name="text_3fd47edce45b3603" formatted="false">"닫기"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"컨트롤 바 닫기"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"개인 정보 보호 화면 비활성화"</string>
<string name="text_7d466b69be42fc43" formatted="false">"선명도와 부드러움의 균형을 유지하며 일정한 압력을 가하면 해상도가 점차 감소합니다."</string>
<string name="text_b92421822c213c2b" formatted="false">"해상도"</string>
<string name="text_2f9daa828907b93f" formatted="false">"삭제"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"공지사항 삭제"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"기록 삭제"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"이 공지를 삭제하시겠습니까?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"새로고침 안내"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"클립보드"</string>
<string name="text_4927a53bcc886afb" formatted="false">"로드 중…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"더 로드하기"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"업데이트 안내"</string>
<string name="text_861521c9e107c648" formatted="false">"기본 연결 초기화 실패"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"송신"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"보내기 "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"전송 후 원격 키보드에서 \"붙여넣기\"를 클릭하세요. 수신된 원격 텍스트를 로컬 시스템에 수동으로 복사할 수 있습니다."</string>
<string name="text_e7281fbca7af369c" formatted="false">"파일 보내기"</string>
<string name="text_ce6c02d19022e016" formatted="false">"문자 보내기"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"취소"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"연결 취소"</string>
<string name="text_72814b15a6fa152c" formatted="false">"각 구성 요소에는 나열된 오픈 소스 라이선스가 적용되며 부여된 권리에는 이 애플리케이션의 일반 사용 약관이 적용되지 않습니다. 소스 코드를 보려면 인터넷 연결이 필요합니다."</string>
<string name="text_e009c148d1890dac" formatted="false">"합계"</string>
<string name="text_81c92013219dd115" formatted="false">"동의하고 계속하기"</string>
<string name="text_409696774c9998ce" formatted="false">"백그라운드에서 연결을 유지하고 원격 데스크톱으로 돌아가려면 클릭하세요."</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"백그라운드 연결 유지를 시작하지 못했습니다. 애플리케이션을 포그라운드에 유지하세요."</string>
<string name="text_d96a21463557171d" formatted="false">"백그라운드 연결 유지를 시작하지 못했습니다. 애플리케이션을 포그라운드에 유지하고 다시 연결하세요."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"백그라운드 연결이 종료되었습니다. 다시 연결해 주세요."</string>
<string name="text_da904f715e1cff87" formatted="false">"백그라운드 연결 서비스가 중지되었습니다. 다시 연결해 주세요."</string>
<string name="text_0812359e18082071" formatted="false">"새로운 미리보기는 후속 연결에도 계속 저장됩니다."</string>
<string name="text_b9086662b1df1f20" formatted="false">"온라인"</string>
<string name="text_39f2f444a042643e" formatted="false">"프로토콜 및 경로 없이 호스트 이름 또는 IPv4 주소를 입력하세요."</string>
<string name="text_473400815c0f687d" formatted="false">"소리"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"원격 텍스트 복사"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"대역폭이 부족할 때 영상 선명도를 보장하려면 항상 더 높은 해상도를 유지하고 프레임 속도를 낮추십시오."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"미디어 암호화"</string>
<string name="text_22dc6217af77bc37" formatted="false">"문자·숫자"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"보안 저장 또는 인증서 초기화에 실패했습니다. 다시 시도하거나 애플리케이션 데이터를 삭제하세요."</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"완료"</string>
<string name="text_678328f6f02881c7" formatted="false">"개인정보 동의 완료 후 업데이트 내용을 확인할 수 있습니다."</string>
<string name="text_c4361f9abdba4882" formatted="false">"개인정보 인증 완료 후 로컬 신원 등록"</string>
<string name="text_c29b888e357d727d" formatted="false">"비밀번호 저장에 실패했습니다. 이 연결은 계속 사용할 수 있습니다."</string>
<string name="text_3f31300a23afb390" formatted="false">"비밀번호는 기본 Android 키 저장소에 암호화되어 저장됩니다."</string>
<string name="text_8d3b052c4c67acda" formatted="false">"원격 ID"</string>
<string name="text_0512084e130984bc" formatted="false">"피어 ID "</string>
<string name="text_c5147d65960196b0" formatted="false">"피어 ID{0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"이 기기에 저장된 원격 미리보기는 모두 삭제되며, 연결 기록과 비밀번호는 유지됩니다."</string>
<string name="text_60cc99cc0013972e" formatted="false">"화면 방향"</string>
<string name="text_9e7294d264afecb7" formatted="false">"원격제어 메뉴 확장"</string>
<string name="text_9343612ee473edd6" formatted="false">"알림으로 인해 원격 연결이 끊어졌습니다."</string>
<string name="text_9d32476fe83aef60" formatted="false">"비밀번호 저장됨"</string>
<string name="text_1cbd391383283924" formatted="false">"저장된 비밀번호를 읽을 수 없습니다. 다시 입력해 주세요."</string>
<string name="text_6792b9f341380c68" formatted="false">"저장된 비밀번호는 보관할 수 없으므로 다시 입력해 주세요."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del가 전송되었습니다."</string>
<string name="text_c28d86acbedba3b6" formatted="false">"백그라운드 연결 유지가 활성화되어 전환 애플리케이션이 계속 연결을 유지할 수 있습니다."</string>
<string name="text_506409ec08cb2a79" formatted="false">"연결 알림에서 돌아가거나 연결을 끊기 위해 백그라운드 연결 유지가 활성화되었습니다."</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"승인됨"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"읽기"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"연결됨 "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"서버에 연결됨"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"프레임 속도"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"부드러움"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"균형"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"애플리케이션의 라이센스 파일이 없거나 손상되었습니다. 애플리케이션을 다시 설치하십시오."</string>
<string name="text_3f2904435942cc0f" formatted="false">"개발 버전에는 아직 출시되지 않은 수정 사항이 포함되어 있습니다. 다음 링크는 기본 버전의 소스 코드입니다."</string>
<string name="text_4338afaad52ea445" formatted="false">"저장 사용"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"개인 정보 보호 화면 활성화"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"오픈 소스 구성 요소"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"오픈소스 소프트웨어 권리 설명"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"현재 최신 버전입니다."</string>
<string name="text_24a52788cf474bf6" formatted="false">"현재 원격측에서는 이 텍스트를 직접 입력하는 기능을 지원하지 않습니다. 텍스트 입력을 지원하는 제어된 버전을 사용하거나 클립보드를 통해 보내주세요."</string>
<string name="text_33f25b500697a7d9" formatted="false">"성공적으로 연결되면 가장 가까운 원격 장치가 여기에 표시됩니다."</string>
<string name="text_70341256d7317e7d" formatted="false">"개인정보 보호정책을 읽었으며 이에 동의합니다."</string>
<string name="text_85c4c8fda0461f35" formatted="false">"선택한 파일(읽을 수 없거나 공간이 부족함)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"장치 ID를 찾을 수 없습니다"</string>
<string name="text_97b7b18df07b9787" formatted="false">"명령 전송에 실패했습니다. 다시 시도해 주세요."</string>
<string name="text_cb782027ca5986c7" formatted="false">"명령이 전송되었으며 컴퓨터 확인을 기다리고 있습니다..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"인증상태"</string>
<string name="text_6e684586884ebee6" formatted="false">"수신"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"받다 "</string>
<string name="text_d16488b987723161" formatted="false">"로컬 디코더"</string>
<string name="text_6b58198955259066" formatted="false">"철회"</string>
<string name="text_c52e42ee856825db" formatted="false">"탈퇴 후에는 원격접속이 중단되며 화면 미리보기가 닫히고 기존 미리보기는 지워집니다. 접속 기록과 비밀번호는 유지되며, 재접속 전 다시 인증이 필요합니다."</string>
<string name="text_61f4788032ec596b" formatted="false">"네트워크 승인 철회"</string>
<string name="text_e736e419008ac10a" formatted="false">"네트워크 승인을 철회하시겠습니까?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"리모컨 메뉴 접기"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"키보드 숨기기"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"마우스 숨기기"</string>
<string name="text_5440f7424f2865bf" formatted="false">"데이터"</string>
<string name="text_94244f41d0a41378" formatted="false">"파일 저장에 실패했습니다. 다시 시도해 주세요."</string>
<string name="text_2553a785fef2a405" formatted="false">"파일이 저장되었습니다"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"파일 선택 시간이 초과되어 세션이 종료되었습니다."</string>
<string name="text_985a5c380be49f4f" formatted="false">"텍스트가 128KB를 초과합니다."</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"연결 끊기"</string>
<string name="text_09b1395f50589327" formatted="false">"연결을 끊으면 홈페이지로 돌아갑니다."</string>
<string name="text_a73c0680db0d2e06" formatted="false">"원격 연결을 끊으시겠습니까?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"연결 해제"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"새 버전"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"새 버전 사용 가능: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"새 버전 사용 가능: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"읽지 않은 공지사항 없음"</string>
<string name="text_dca2c418ac967366" formatted="false">"읽기 상태를 로컬에 저장할 수 없습니다. 다시 시도해 주세요."</string>
<string name="text_5c92da76360056cc" formatted="false">"이 공지를 삭제할 수 없습니다. 다시 시도해 주세요."</string>
<string name="text_615709dd3129313c" formatted="false">"원격 연결을 시작할 수 없습니다"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"오픈소스 라이선스를 읽을 수 없습니다."</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"디스플레이"</string>
<string name="text_ffe21a671190323e" formatted="false">"비밀번호 표시"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"나중에"</string>
<string name="text_6d9a7758610a2432" formatted="false">"공지 없음"</string>
<string name="text_0b290c6600d92f43" formatted="false">"최근 연결"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"서버"</string>
<string name="text_dfe408e686175095" formatted="false">"ServerTLS인증서 확인 실패"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"서버 주소"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"서버 로그인에 실패했습니다. 서버 주소와 네트워크를 확인해주세요."</string>
<string name="text_d270868960e68629" formatted="false">"서버 포트"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"서버 연결 시간이 초과되었습니다. 서버 주소와 네트워크를 확인하세요."</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"활성화되지 않음"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"링크를 열 수 있는 브라우저가 없습니다."</string>
<string name="text_94bc3d40defef3e9" formatted="false">"무단"</string>
<string name="text_4d0680f9efaef147" formatted="false">"읽지 않음"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"서버가 연결되지 않았습니다. · 다시 시도하려면 클릭하세요."</string>
<string name="text_7eea7966c0b746c5" formatted="false">"이 애플리케이션은 제한된 보증과 책임 제한을 제공하는CeCILL-C1.0에 따라 순방향 오류 수정을 위해OpenFEC를 사용합니다. 자세한 내용은 전체 라이센스를 참조하세요."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"로컬 ID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"로컬 ID{0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"이 버전의 소스 코드 및 빌드 지침"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"이 빌드에는 소스 개정을 사용할 수 없습니다."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"새로고침하려면 릴리즈하세요"</string>
<string name="text_47077144715b7074" formatted="false">"빌드 지침"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"레시피 및 프로젝트 패치 빌드"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"소스코드 보기"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"확인에 실패했습니다. 나중에 다시 시도해 주세요."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"업데이트 확인"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"개인정보 보호 화면을 닫는 중…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"보안 연결 설정 중…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"개인정보 보호 화면을 켜는 중..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"업데이트 확인 중…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"지우는 중..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"로컬 ID를 얻는 중..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"선택한 파일을 읽는 중..."</string>
<string name="text_694186911ae3e4da" formatted="false">"연결 중"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"시그널링 서버에 연결 중..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"서버에 연결하는 중..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"서버에 재접속하는 중..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"이 장치에 사용할 수 있는 파일 선택기가 없습니다."</string>
<string name="text_bce2377283c2455a" formatted="false">"클리어"</string>
<string name="text_ee0e76b8507212de" formatted="false">"미리보기 삭제"</string>
<string name="text_ecc809e44637de18" formatted="false">"미리보기를 삭제할까요?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"소스 코드 및 빌드 지침"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"소스코드 버전"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"버전"</string>
<string name="text_16410ee9663fad10" formatted="false">"저작권 및 라이선스 정책"</string>
<string name="text_11de77e1e41e680f" formatted="false">"컴퓨터 키보드"</string>
<string name="text_20f57900234debd7" formatted="false">"화질"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"영상 선호도"</string>
<string name="text_82a5600c470179fa" formatted="false">"영상 지연"</string>
<string name="text_33b458faaf967e15" formatted="false">"영상 설정"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"비디오 품질"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"비디오 캡처 프레임 속도"</string>
<string name="text_66062971234f422d" formatted="false">"트랙패드"</string>
<string name="text_c421dc051bea3f11" formatted="false">"상대 마우스"</string>
<string name="text_de32e20193ad1042" formatted="false">"알겠습니다"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"알았어"</string>
<string name="text_36f33adaf0942634" formatted="false">"확인"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"연결 확인"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"오프라인"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"스페이스"</string>
<string name="text_471a579e8d87faac" formatted="false">"포트 범위는 1~65535입니다."</string>
<string name="text_c7d0c77862e32b52" formatted="false">"기호·기능"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"컴퓨터가 개인 정보 보호 화면 상태로 돌아갈 때까지 기다립니다. 오래된 컴퓨터에서는 이를 지원하지 않을 수 있습니다."</string>
<string name="text_5ee880e2524a983a" formatted="false">"원격 화면 정보를 기다리는 중"</string>
<string name="text_674c723a85ee7f57" formatted="false">"원격 화면을 기다리는 중..."</string>
<string name="text_119f0483da4756c4" formatted="false">"개인정보 인증 대기 중"</string>
<string name="text_7916071c1a8ee458" formatted="false">"시스템 키보드"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"직접 터치"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"앱솔루트 마우스"</string>
<string name="text_872428a7210d5e0c" formatted="false">"네트워크"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"커스텀"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"가상마우스"</string>
<string name="text_62c199f37bf95869" formatted="false">"로컬 단축키를 사용하여 원격 개인정보 보호 화면을 잠금 해제했습니다. 개인정보 보호를 복원하고 로컬 바로가기 잠금 해제를 비활성화하려면 다시 잠그세요."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"제어 버전이 너무 낮습니다. 최신 버전으로 업그레이드하세요."</string>
<string name="text_d814476826827b78" formatted="false">"리모트 엔코더"</string>
<string name="text_488402883e27f6f0" formatted="false">"원격지로 보낼 텍스트"</string>
<string name="text_c20f7618d330a854" formatted="false">"영상"</string>
<string name="text_310c625425f014d8" formatted="false">"라이센스"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"라이센스 및 저작권 고지"</string>
<string name="text_e24134b643a17975" formatted="false">"라이센스 및 저작권 고지는 오프라인에서 확인할 수 있습니다."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"기기 ID를 저장하지 못했습니다. 보안 저장소를 확인하세요."</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"기기 ID와 선택된 저장된 액세스 비밀번호는 Android Keystore를 사용하여 암호화됩니다. 연결 기록은 로컬에만 저장되며 장치 백업에는 포함되지 않습니다. 세션이 종료되면 클립보드 내용이 지워집니다. 앱을 종료한 후에도 PIP(picture-in-picture) 또는 연결된 알림으로 세션을 유지하고 앱 또는 알림 내에서 연결을 끊습니다."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"설정"</string>
<string name="text_4f8771952222a011" formatted="false">"설정, 새 버전이 있습니다"</string>
<string name="text_f4f712d5be91721a" formatted="false">"접속 비밀번호"</string>
<string name="text_dd6c465847af1b60" formatted="false">"접속비밀번호가 잘못되었습니다. 다시 입력해 주세요."</string>
<string name="text_defba1d2989bfab4" formatted="false">"인증서 확인 실패 · 다시 시도하려면 클릭하세요."</string>
<string name="text_44f3d4d52347095a" formatted="false">"먼저 온라인 인증을 완료해주세요."</string>
<string name="text_2a07ca002bc5093d" formatted="false">"원격 측에서CrossDesk시스템 서비스를 활성화하고 다시 시도하십시오."</string>
<string name="text_990fd23c62de5ccf" formatted="false">"요청 시간이 초과되어 컴퓨터의 개인 정보 보호 화면 상태가 확인되지 않았습니다."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"현재 파일이 전송될 때까지 기다려 주십시오."</string>
<string name="text_9305d9d72b06110d" formatted="false">"1~65535 범위의 포트를 입력하세요."</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"기기 ID 9자리를 입력하세요."</string>
<string name="text_285a0714894d2899" formatted="false">"유효한 접속 비밀번호를 입력해주세요"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"설정을 적용할 수 없습니다. 다시 시도해 보세요."</string>
<string name="text_a762e7e650e285fa" formatted="false">"소프트웨어 라이센스"</string>
<string name="text_572cf45ba43634b3" formatted="false">"뒤로"</string>
<string name="text_7565f94031582c7c" formatted="false">"컨트롤 바로 돌아가기"</string>
<string name="text_55113b6925c362fd" formatted="false">"아직 접속기록이 없습니다"</string>
<string name="text_3aa527b2106cc987" formatted="false">"본 공지사항은 업데이트 또는 삭제되었습니다. 공지사항 목록으로 돌아가서 확인하시기 바랍니다."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"원격 작업이 종료되었습니다."</string>
<string name="text_c255841f68096d3b" formatted="false">"원격 데스크톱"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"원격 데스크톱 터치 영역"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"원격 데스크톱: 터치하고 클릭하고, 드래그하여 이동하고, 오른쪽 버튼을 길게 누르고, 핀치하여 확대/축소하고, 두 손가락으로 화면을 확대하고 드래그하고, 두 손가락으로 위아래로 밀어 원래 크기로 스크롤합니다."</string>
<string name="text_3535087994d050de" formatted="false">"원격 컴퓨터"</string>
<string name="text_ed2e03072ce131de" formatted="false">"원격 창이 닫혔거나 백그라운드 Keepalive 시작에 실패하여 세션이 종료되었습니다."</string>
<string name="text_678b783fb578172b" formatted="false">"원격 접속"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"원격 비밀번호가 올바르지 않습니다."</string>
<string name="text_a2ce499295e358ae" formatted="false">"원격 측에서 연결을 종료했습니다."</string>
<string name="text_176e117df3ace734" formatted="false">"현재 원격 끝을 사용할 수 없습니다."</string>
<string name="text_d2c0694243f93249" formatted="false">"원격 접속 비밀번호가 만료되었습니다. 다시 접속해 주세요."</string>
<string name="text_a5574109f0208e89" formatted="false">"연결"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"연결 "</string>
<string name="text_89c4766afc85125f" formatted="false">"연결 실패"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"연결 실패 · 다시 시도하려면 클릭하세요."</string>
<string name="text_2ab7d89557e779ab" formatted="false">"연결 끊김"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"연결이 종료되었습니다"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"연결 지연(RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"지원되는 원격 장치에 연결된 경우 사용할 수 있습니다."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"연결 완료 후 가로 화면 잠금"</string>
<string name="text_f741e0536210eafa" formatted="false">"연결 프롬프트"</string>
<string name="text_485a26050ce57431" formatted="false">"연결 방법"</string>
<string name="text_5a048351a77fe88d" formatted="false">"서버에 접속"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"서버에 접속하여 최신 공지사항을 받아보세요."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"연결 시간이 초과되었습니다. 장치 ID, 서버 및 원격 온라인 상태를 확인하십시오."</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"원격 데스크톱에 연결"</string>
<string name="text_d5407b4f3c666681" formatted="false">"백스페이스"</string>
<string name="text_e02926784fcedb91" formatted="false">"공지사항"</string>
<string name="text_465a092680d132b3" formatted="false">"일부 미리보기를 삭제할 수 없습니다. 다시 시도해 주세요."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"다시 시도해 보세요"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"읽은 상태 저장 다시 시도"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"잠금"</string>
<string name="text_864200bbb241e067" formatted="false">"키보드"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"읽기 및 승인"</string>
<string name="text_86651d17a401c55b" formatted="false">"개인 정보 보호"</string>
<string name="text_26c6e011e343708c" formatted="false">"개인 정보 보호 및 승인"</string>
<string name="text_6cad42fd3da85585" formatted="false">"개인정보 보호 화면"</string>
<string name="text_e157fafb76aad954" formatted="false">"화면 보호 꺼짐"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"화면 보호 켜짐"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"개인정보 보호 화면 잠금 해제됨"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"개인정보 화면 이상"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"개인 정보 보호 화면을 일시적으로 사용할 수 없습니다."</string>
<string name="text_4d59be3c75189032" formatted="false">"개인 정보 보호 화면 상태가 만료되어 컴퓨터 확인을 기다리는 중입니다."</string>
<string name="text_1674532c70ed1db2" formatted="false">"개인정보 보호 화면 상태 확인 예정"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"개인정보 보호정책"</string>
<string name="text_554e957e8e3c00af" formatted="false">"개인정보취급방침은 일시적으로 열람할 수 없습니다. 인터넷 연결을 거부하고 애플리케이션을 다시 설치하십시오."</string>
<string name="text_7db2e189ecb01065" formatted="false">"음소거"</string>
<string name="text_296c632ec857a0ba" formatted="false">"오디오"</string>
<string name="text_0cadb49096a382e0" formatted="false">"프로젝트 홈페이지"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"미리보기에는 원격 컴퓨터의 개인 정보나 업무 정보가 포함될 수 있습니다. 전원을 켜면 각 연결이 자동으로 기기에 사진을 저장하고 최근 연결에 표시됩니다. 언제든지 닫고 지울 수 있습니다."</string>
<string name="text_b1c27820fec23edb" formatted="false">"높음"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"기본값"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"기본적으로 꺼져 있습니다. 전원을 켜면 휴대폰이 연결되면 화면이 가로 방향으로 전환되고 유지됩니다. 꺼지면 장치의 방향을 따릅니다. 태블릿과 멀티 윈도우는 영향을 받지 않습니다."</string>
<string name="text_1184a97edd8e1646" formatted="false">"마우스 오른쪽 버튼을 누르고 드래그한 후 떼면 드래그가 종료됩니다."</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"마우스 왼쪽 버튼을 누르고 드래그한 후 떼면 드래그가 종료됩니다."</string>
<string name="text_269f39a07b4a4e01" formatted="false">"마우스 제어"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"마우스 휠, 위아래로 밀어 키 범위 밖으로 슬라이드"</string>
<string name="text_9aa238fc6121dede" formatted="false">", 패킷 손실률 "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", 보내다 "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", 비밀번호가 저장되었습니다"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", 수신 "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", 재시도를 클릭하세요."</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Concluído"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Falha na transferência"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" dispositivos"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Artigo · "</string>
<string name="text_2c894057fc526576" formatted="false">" itens não lidos"</string>
<string name="text_d0ac753c6681e578" formatted="false">"Conexão remota CrossDesk"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL versão 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"Aviso OpenFEC"</string>
<string name="text_69ad29820a259526" formatted="false">"Licença e fonte OpenFEC"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"Conexão direta P2P"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRT habilitado"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"GIRAR relé"</string>
<string name="text_ebede975edd52aee" formatted="false">"Falha na transmissão {0}"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Dispositivos: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"Itens não lidos {0}"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, recebendo {1}, enviando {2}, taxa de perda de pacotes {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Conectado"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Puxe para baixo para atualizar"</string>
<string name="text_37772187ab854191" formatted="false">"Taxa de perda"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Média"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Prioriza manter a imagem suave e reduzirá a resolução de forma mais agressiva quando a largura de banda ou a pressão de codificação forem altas."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Baixa"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Usando um servidor personalizado"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Salvar senha"</string>
<string name="text_10019cef593819ed" formatted="false">"Salvar arquivo recebido"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Salvar visualizações de tela remotas"</string>
<string name="text_0f197074373e38fd" formatted="false">"Salvar visualização remota da tela?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"A conexão de sinalização foi interrompida, reconecte."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Falha ao carregar o anúncio. Tente novamente."</string>
<string name="text_00554b17c432937e" formatted="false">"Detalhes do anúncio"</string>
<string name="text_2428420e407fe2e8" formatted="false">"Total "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Total de itens {0}· Itens não lidos {1}"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"Sobre"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"Sobre o CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"Sobre, há uma nova versão"</string>
<string name="text_3fd47edce45b3603" formatted="false">"Fechar"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Fechar barra de controle"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Desativar tela de privacidade"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Equilibra clareza e suavidade, reduzindo apenas gradualmente a resolução sob pressão constante."</string>
<string name="text_b92421822c213c2b" formatted="false">"Resolução"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Excluir"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Excluir anúncio"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Excluir registro"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"Excluir este anúncio?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Anúncio de atualização"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Área de transferência"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Carregando…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Carregue mais"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Atualização recomendada"</string>
<string name="text_861521c9e107c648" formatted="false">"Falha na inicialização da conexão nativa"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Enviar"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Enviar "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"Após o envio, clique em “Colar” no teclado remoto. O texto remoto recebido pode ser copiado manualmente para a máquina local."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Enviar arquivo"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Enviar texto"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Cancelar"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Cancelar conexão"</string>
<string name="text_72814b15a6fa152c" formatted="false">"Cada componente está sujeito à licença de código aberto listada e os direitos concedidos não estão sujeitos aos termos gerais de uso deste aplicativo. A visualização do código-fonte requer uma conexão com a Internet."</string>
<string name="text_e009c148d1890dac" formatted="false">"Total"</string>
<string name="text_81c92013219dd115" formatted="false">"Concorde e continue"</string>
<string name="text_409696774c9998ce" formatted="false">"Mantenha-se conectado em segundo plano, clique para retornar à área de trabalho remota"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"Falha ao iniciar a manutenção de atividade em segundo plano, mantenha o aplicativo em primeiro plano"</string>
<string name="text_d96a21463557171d" formatted="false">"Falha ao iniciar a manutenção de atividade em segundo plano. Mantenha o aplicativo em primeiro plano e reconecte."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"A conexão em segundo plano foi encerrada. Reconecte."</string>
<string name="text_da904f715e1cff87" formatted="false">"O serviço de conexão em segundo plano foi interrompido, reconecte."</string>
<string name="text_0812359e18082071" formatted="false">"Novas visualizações ainda serão salvas nas conexões subsequentes."</string>
<string name="text_b9086662b1df1f20" formatted="false">"On-line"</string>
<string name="text_39f2f444a042643e" formatted="false">"Preencha o nome do host ou endereço IPv4, sem protocolo e caminho"</string>
<string name="text_473400815c0f687d" formatted="false">"Som"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Copiar texto remoto"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Sempre mantenha uma resolução mais alta e reduza a taxa de quadros para garantir a clareza da imagem quando a largura de banda for insuficiente."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Criptografia de mídia"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Letras e números"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Falha no armazenamento seguro ou na inicialização do certificado. Tente novamente ou limpe os dados do aplicativo"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Feito"</string>
<string name="text_678328f6f02881c7" formatted="false">"Você pode verificar se há atualizações após concluir a autorização de privacidade"</string>
<string name="text_c4361f9abdba4882" formatted="false">"Registre a identidade local após concluir a autorização de privacidade"</string>
<string name="text_c29b888e357d727d" formatted="false">"Falha ao salvar a senha, esta conexão ainda pode ser usada"</string>
<string name="text_3f31300a23afb390" formatted="false">"As senhas são salvas criptografadas no Android Keystore nativo"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"ID remoto"</string>
<string name="text_0512084e130984bc" formatted="false">"ID do par "</string>
<string name="text_c5147d65960196b0" formatted="false">"ID de par {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"Todas as visualizações remotas salvas nesta máquina serão excluídas e os registros de conexão e senhas serão mantidos."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Orientação da tela"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Expandir menu de controle remoto"</string>
<string name="text_9343612ee473edd6" formatted="false">"Conexão remota desconectada da notificação"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Senha salva"</string>
<string name="text_1cbd391383283924" formatted="false">"A senha salva não pode ser lida, digite-a novamente."</string>
<string name="text_6792b9f341380c68" formatted="false">"A senha salva não pode ser mantida, digite-a novamente."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del enviado"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"O keep-alive em segundo plano está habilitado, a troca de aplicativos pode continuar a manter a conexão"</string>
<string name="text_506409ec08cb2a79" formatted="false">"Manutenção de atividade em segundo plano habilitada para retornar ou desconectar das notificações de conexão"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Concedido"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Leia"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Conectado "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Conectado ao servidor"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Taxa de quadros"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Fluidez"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Equilibrado"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"O arquivo de licença do aplicativo está ausente ou danificado. Reinstale o aplicativo."</string>
<string name="text_3f2904435942cc0f" formatted="false">"A versão de desenvolvimento contém modificações que ainda não foram lançadas. O link a seguir é o código fonte da versão básica."</string>
<string name="text_4338afaad52ea445" formatted="false">"Ativar salvamento"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Habilitar tela de privacidade"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Componentes de código aberto"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Descrição dos direitos de software de código aberto"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"Esta é atualmente a versão mais recente"</string>
<string name="text_24a52788cf474bf6" formatted="false">"Atualmente, a extremidade remota não suporta a entrada direta deste texto. Use uma versão do final controlado que suporte entrada de texto ou envie-o através da área de transferência."</string>
<string name="text_33f25b500697a7d9" formatted="false">"Após a conexão bem-sucedida, o dispositivo remoto mais próximo será exibido aqui."</string>
<string name="text_70341256d7317e7d" formatted="false">"Li e concordo com a Política de Privacidade"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Arquivo selecionado (não pode ser lido ou espaço insuficiente)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"O ID do dispositivo não foi encontrado"</string>
<string name="text_97b7b18df07b9787" formatted="false">"Falha no envio do comando, tente novamente"</string>
<string name="text_cb782027ca5986c7" formatted="false">"O comando foi enviado, aguardando confirmação do computador..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Status de autorização"</string>
<string name="text_6e684586884ebee6" formatted="false">"Receber"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"receber "</string>
<string name="text_d16488b987723161" formatted="false">"Decodificador local"</string>
<string name="text_6b58198955259066" formatted="false">"Revogar"</string>
<string name="text_c52e42ee856825db" formatted="false">"Após a retirada, a conexão remota será interrompida, a visualização da tela será fechada e a visualização existente será apagada. O registro de conexão e a senha serão mantidos e será necessária autorização novamente antes de reconectar."</string>
<string name="text_61f4788032ec596b" formatted="false">"Retirar autorização de rede"</string>
<string name="text_e736e419008ac10a" formatted="false">"Retirar a autorização da rede?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Recolher menu de controle remoto"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Ocultar teclado"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Ocultar mouse"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Dados"</string>
<string name="text_94244f41d0a41378" formatted="false">"Falha ao salvar o arquivo, tente novamente"</string>
<string name="text_2553a785fef2a405" formatted="false">"Arquivo salvo"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"A seleção do arquivo expirou e a sessão foi encerrada"</string>
<string name="text_985a5c380be49f4f" formatted="false">"O texto excede 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Desconectar"</string>
<string name="text_09b1395f50589327" formatted="false">"Retornará à página inicial após desconectar"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"Desconectar a conexão remota?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Desconectar"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"Nova versão"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"Nova versão disponível: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"Nova versão disponível: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"Nenhum anúncio não lido"</string>
<string name="text_dca2c418ac967366" formatted="false">"Não é possível salvar o status de leitura localmente. Por favor, tente novamente."</string>
<string name="text_5c92da76360056cc" formatted="false">"Não é possível excluir este anúncio. Por favor, tente novamente."</string>
<string name="text_615709dd3129313c" formatted="false">"Não é possível iniciar a conexão remota"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"Não é possível ler a licença de código aberto"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Tela"</string>
<string name="text_ffe21a671190323e" formatted="false">"Mostrar senha"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Agora não"</string>
<string name="text_6d9a7758610a2432" formatted="false">"Sem anúncios"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Conexões recentes"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Servidor"</string>
<string name="text_dfe408e686175095" formatted="false">"Falha na verificação do certificado Server TLS"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Endereço do servidor"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Falha no login do servidor, verifique o endereço do servidor e a rede"</string>
<string name="text_d270868960e68629" formatted="false">"Porta do servidor"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"A conexão do servidor expirou, verifique o endereço do servidor e a rede"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"Não habilitado"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"Nenhum navegador encontrado para abrir o link"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"Não autorizado"</string>
<string name="text_4d0680f9efaef147" formatted="false">"Não lido"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Servidor não conectado · Clique para tentar novamente"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"Este aplicativo usa OpenFEC para correção direta de erros noCeCILL-C1.0, que fornece garantias limitadas e limita a responsabilidade. Veja a licença completa para detalhes."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"ID local "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"ID local {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Código-fonte e instruções de construção para esta versão"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"A revisão fonte não está disponível para esta compilação."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Solte para atualizar"</string>
<string name="text_47077144715b7074" formatted="false">"Instruções de construção"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Crie receitas e patches de projeto"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"Ver código-fonte"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"Falha na verificação. Por favor, tente novamente mais tarde."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Verifique se há atualizações"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Fechando a tela de privacidade…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Estabelecendo conexão segura…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"Ativando a tela de privacidade..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Verificando atualizações…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Limpando..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Obtendo ID local..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Lendo arquivo selecionado..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Conectando"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Conectando ao servidor de sinalização..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Conectando ao servidor..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Reconectando ao servidor..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"Nenhum seletor de arquivos disponível para este dispositivo"</string>
<string name="text_bce2377283c2455a" formatted="false">"Limpar"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Limpar prévias"</string>
<string name="text_ecc809e44637de18" formatted="false">"Limpar prévias?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Código-fonte e instruções de construção"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Versão do código-fonte"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Versão"</string>
<string name="text_16410ee9663fad10" formatted="false">"Declaração de direitos autorais e licença"</string>
<string name="text_11de77e1e41e680f" formatted="false">"Teclado de computador"</string>
<string name="text_20f57900234debd7" formatted="false">"Qualidade"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Preferência de vídeo"</string>
<string name="text_82a5600c470179fa" formatted="false">"Latência do vídeo"</string>
<string name="text_33b458faaf967e15" formatted="false">"Configurações de vídeo"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Qualidade de vídeo"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Taxa de quadros de captura de vídeo"</string>
<string name="text_66062971234f422d" formatted="false">"Touchpad"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Rato relativo"</string>
<string name="text_de32e20193ad1042" formatted="false">"Entendi"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"OK"</string>
<string name="text_36f33adaf0942634" formatted="false">"Confirmar"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Confirme a conexão"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Off-line"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Espaço"</string>
<string name="text_471a579e8d87faac" formatted="false">"O intervalo de portas é 1–65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Símbolos e teclas"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Aguarde o computador retornar ao estado da tela de privacidade; computadores mais antigos podem não suportá-lo."</string>
<string name="text_5ee880e2524a983a" formatted="false">"Aguardando informações da tela remota"</string>
<string name="text_674c723a85ee7f57" formatted="false">"Aguardando a tela remota..."</string>
<string name="text_119f0483da4756c4" formatted="false">"Aguardando autorização de privacidade"</string>
<string name="text_7916071c1a8ee458" formatted="false">"Teclado do sistema"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Toque direto"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Rato Absoluto"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Rede"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Personalizado"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Rato virtual"</string>
<string name="text_62c199f37bf95869" formatted="false">"A tela de privacidade remota foi desbloqueada usando o atalho local. Bloqueie-o novamente para restaurar a privacidade e desativar o desbloqueio de atalhos locais."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"A versão controlada é muito baixa, atualize para a versão mais recente."</string>
<string name="text_d814476826827b78" formatted="false">"Codificador remoto"</string>
<string name="text_488402883e27f6f0" formatted="false">"Texto a ser enviado para o terminal remoto"</string>
<string name="text_c20f7618d330a854" formatted="false">"Vídeo"</string>
<string name="text_310c625425f014d8" formatted="false">"Licença"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Licenças e avisos de direitos autorais"</string>
<string name="text_e24134b643a17975" formatted="false">"A licença e o aviso de direitos autorais estão disponíveis offline."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"Falha ao salvar a identidade do dispositivo. Verifique o armazenamento seguro"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"A identidade do dispositivo e as senhas de acesso salvas selecionadas são criptografadas usando o Android Keystore. Os registros de conexão são salvos apenas localmente e não são incluídos no backup do dispositivo. O conteúdo da área de transferência é apagado quando a sessão é encerrada. Mantenha a sessão com notificações picture-in-picture ou conectadas depois de sair do aplicativo e desconecte-se do aplicativo ou das notificações."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Configurações"</string>
<string name="text_4f8771952222a011" formatted="false">"Configurações, há uma nova versão"</string>
<string name="text_f4f712d5be91721a" formatted="false">"Senha de acesso"</string>
<string name="text_dd6c465847af1b60" formatted="false">"A senha de acesso está errada, digite-a novamente."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Falha na verificação do certificado · Clique para tentar novamente"</string>
<string name="text_44f3d4d52347095a" formatted="false">"Por favor, preencha primeiro a autorização online"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Ative o serviço do sistema CrossDesk na extremidade remota e tente novamente"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"A solicitação expirou e o status da tela de privacidade do computador não foi confirmado."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Aguarde o envio do arquivo atual."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Insira uma porta no intervalo 1–65535"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Insira um ID de dispositivo de 9 dígitos"</string>
<string name="text_285a0714894d2899" formatted="false">"Por favor, insira uma senha de acesso válida"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"Não foi possível aplicar as configurações. Tente novamente."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Licença de software"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Voltar"</string>
<string name="text_7565f94031582c7c" formatted="false">"Retornar à barra de controle"</string>
<string name="text_55113b6925c362fd" formatted="false">"Ainda não há registro de conexão"</string>
<string name="text_3aa527b2106cc987" formatted="false">"Este anúncio foi atualizado ou excluído. Volte à lista de anúncios para visualizá-lo."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"A tarefa remota está fechada"</string>
<string name="text_c255841f68096d3b" formatted="false">"Área de Trabalho Remota"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Área de toque da área de trabalho remota"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Área de trabalho remota: toque e clique, arraste para mover, pressione e segure o botão direito, aperte para ampliar, amplie e arraste a tela com dois dedos, deslize para cima e para baixo com dois dedos para rolar na escala original"</string>
<string name="text_3535087994d050de" formatted="false">"Computador remoto"</string>
<string name="text_ed2e03072ce131de" formatted="false">"A janela remota foi fechada ou a manutenção de atividade em segundo plano falhou ao iniciar e a sessão foi encerrada."</string>
<string name="text_678b783fb578172b" formatted="false">"Conexão remota"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Senha remota incorreta"</string>
<string name="text_a2ce499295e358ae" formatted="false">"A extremidade remota fechou a conexão"</string>
<string name="text_176e117df3ace734" formatted="false">"A extremidade remota está indisponível no momento"</string>
<string name="text_d2c0694243f93249" formatted="false">"A senha de acesso remoto expirou, reconecte."</string>
<string name="text_a5574109f0208e89" formatted="false">"Conectar"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Conexão "</string>
<string name="text_89c4766afc85125f" formatted="false">"Falha na conexão"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Falha na conexão · Clique para tentar novamente"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Conexão perdida"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Conexão encerrada"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Atraso de conexão (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Disponível quando conectado a um dispositivo remoto compatível."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Bloqueie a tela paisagem após a conexão ser estabelecida"</string>
<string name="text_f741e0536210eafa" formatted="false">"Solicitação de conexão"</string>
<string name="text_485a26050ce57431" formatted="false">"Método de conexão"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Conecte-se ao servidor"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Conecte-se ao servidor para obter os anúncios mais recentes."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"A conexão expirou, verifique o ID do dispositivo, o servidor e o status on-line remoto"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Conecte-se à área de trabalho remota"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Retrocesso"</string>
<string name="text_e02926784fcedb91" formatted="false">"Avisos"</string>
<string name="text_465a092680d132b3" formatted="false">"Não foi possível limpar algumas visualizações. Tente novamente."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Tente novamente"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Tente salvar novamente o status de leitura"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Bloqueio"</string>
<string name="text_864200bbb241e067" formatted="false">"Teclado"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Ler e autorizar"</string>
<string name="text_86651d17a401c55b" formatted="false">"Privacidade"</string>
<string name="text_26c6e011e343708c" formatted="false">"Privacidade e Autorização"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Tela de privacidade"</string>
<string name="text_e157fafb76aad954" formatted="false">"Privacidade desativada"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Privacidade ativada"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Tela de privacidade desbloqueada"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Anormalidade na tela de privacidade"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"A tela de privacidade está temporariamente indisponível"</string>
<string name="text_4d59be3c75189032" formatted="false">"O status da tela de privacidade expirou, aguardando confirmação do computador"</string>
<string name="text_1674532c70ed1db2" formatted="false">"Status da tela de privacidade a ser confirmado"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Política de Privacidade"</string>
<string name="text_554e957e8e3c00af" formatted="false">"A política de privacidade não pode ser lida temporariamente. Recuse-se a se conectar à Internet e reinstale o aplicativo."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Mudo"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Áudio"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Site do projeto"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"As visualizações podem conter informações pessoais ou de trabalho do computador remoto. Após ligá-lo, cada conexão salvará automaticamente uma imagem na máquina e a exibirá nas conexões recentes. Você pode fechar e limpar a qualquer momento."</string>
<string name="text_b1c27820fec23edb" formatted="false">"Alta"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"Padrão"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"Desligado por padrão. Quando ligado, a tela entrará e permanecerá na orientação paisagem quando o telefone estiver conectado; quando desligado, seguirá a direção do dispositivo. Tablets e janelas múltiplas não são afetados."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Botão direito do mouse, pressione e arraste, solte para finalizar o arrasto"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Botão esquerdo do mouse, pressione e arraste, solte para finalizar o arrasto"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Controle do mouse"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Roda do mouse, deslize para cima e para baixo para sair do intervalo de teclas"</string>
<string name="text_9aa238fc6121dede" formatted="false">", taxa de perda de pacotes "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", enviar "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", senha salva"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", receba "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", clique em Tentar novamente"</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Завершено"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Передача не удалась"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" устройства"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Статья · "</string>
<string name="text_2c894057fc526576" formatted="false">" непрочитанные элементы"</string>
<string name="text_d0ac753c6681e578" formatted="false">"Удаленное подключение CrossDesk"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL версия 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"Уведомление OpenFEC"</string>
<string name="text_69ad29820a259526" formatted="false">"Лицензия и исходный код OpenFEC"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"Прямое соединение P2P"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP включен"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"Реле ПОВОРОТА"</string>
<string name="text_ebede975edd52aee" formatted="false">"Передача {0} не удалась."</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Устройства: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} непрочитанные элементы"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, прием {1}, отправка {2}, скорость потери пакетов {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Подключено"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Потяните вниз, чтобы обновить"</string>
<string name="text_37772187ab854191" formatted="false">"Потери пакетов"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Среднее"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Отдает приоритет сохранению плавности изображения и более агрессивно снижает разрешение при высокой пропускной способности или нагрузке на кодирование."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Низкое"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Использование пользовательского сервера"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Сохранить пароль"</string>
<string name="text_10019cef593819ed" formatted="false">"Сохранить полученный файл"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Сохранять превью удалённого экрана"</string>
<string name="text_0f197074373e38fd" formatted="false">"Сохранить предварительный просмотр удаленного экрана?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"Сигнальное соединение прервано, пожалуйста, переподключитесь."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Не удалось загрузить объявление. Повторите попытку."</string>
<string name="text_00554b17c432937e" formatted="false">"Подробности объявления"</string>
<string name="text_2428420e407fe2e8" formatted="false">"Итого "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Всего элементов {0}· Непрочитанные элементы {1}"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"О программе"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"О CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"О, вышла новая версия"</string>
<string name="text_3fd47edce45b3603" formatted="false">"Закрыть"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Закрыть панель управления"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Выключить приватный экран"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Балансирует четкость и плавность изображения, постепенно снижая разрешение под постоянным давлением."</string>
<string name="text_b92421822c213c2b" formatted="false">"Разрешение"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Удалить"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Удалить объявление"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Удалить запись"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"Удалить это объявление?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Обновить объявление"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Буфер обмена"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Загрузка…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Загрузить еще"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Рекомендуется обновление"</string>
<string name="text_861521c9e107c648" formatted="false">"Не удалось инициализировать собственное соединение."</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Отправка"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Отправить "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"После отправки нажмите «Вставить» на удаленной клавиатуре. Полученный удаленный текст можно вручную скопировать на локальный компьютер."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Отправить файл"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Отправить текст"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Отмена"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Отмена соединения"</string>
<string name="text_72814b15a6fa152c" formatted="false">"На каждый компонент распространяется действие указанной лицензии с открытым исходным кодом, а предоставленные права не подпадают под действие общих условий использования этого приложения. Для просмотра исходного кода требуется подключение к Интернету."</string>
<string name="text_e009c148d1890dac" formatted="false">"Итого"</string>
<string name="text_81c92013219dd115" formatted="false">"Согласитесь и продолжайте."</string>
<string name="text_409696774c9998ce" formatted="false">"Оставайтесь на связи в фоновом режиме, нажмите, чтобы вернуться к удаленному рабочему столу."</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"Не удалось запустить фоновую поддержку активности, оставьте приложение на переднем плане."</string>
<string name="text_d96a21463557171d" formatted="false">"Не удалось запустить фоновую поддержку активности. Оставьте приложение в активном режиме и переподключитесь."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"Фоновое соединение прервано, подключитесь заново."</string>
<string name="text_da904f715e1cff87" formatted="false">"Служба фонового подключения остановлена, подключитесь повторно."</string>
<string name="text_0812359e18082071" formatted="false">"Новые превью по-прежнему будут сохраняться при последующих подключениях."</string>
<string name="text_b9086662b1df1f20" formatted="false">"В сети"</string>
<string name="text_39f2f444a042643e" formatted="false">"Введите имя хоста или IPv4-адрес без протокола и пути."</string>
<string name="text_473400815c0f687d" formatted="false">"Звук"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Копирование удаленного текста"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Всегда поддерживайте более высокое разрешение и уменьшайте частоту кадров, чтобы обеспечить четкость изображения при недостаточной полосе пропускания."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Шифрование мультимедиа"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Буквы и цифры"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Не удалось инициализировать безопасное хранилище или сертификат. Повторите попытку или очистите данные приложения."</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Готово"</string>
<string name="text_678328f6f02881c7" formatted="false">"Вы можете проверить наличие обновлений после завершения авторизации конфиденциальности."</string>
<string name="text_c4361f9abdba4882" formatted="false">"Зарегистрируйте локальную личность после завершения авторизации конфиденциальности."</string>
<string name="text_c29b888e357d727d" formatted="false">"Не удалось сохранить пароль, это соединение все еще можно использовать."</string>
<string name="text_3f31300a23afb390" formatted="false">"Пароли сохраняются в зашифрованном виде в собственном хранилище ключей Android."</string>
<string name="text_8d3b052c4c67acda" formatted="false">"Удаленный ID"</string>
<string name="text_0512084e130984bc" formatted="false">"Идентификатор узла "</string>
<string name="text_c5147d65960196b0" formatted="false">"Идентификатор узла {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"Все удаленные просмотры, сохраненные на этом компьютере, будут удалены, а записи подключений и пароли сохранятся."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Ориентация экрана"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Развернуть меню пульта дистанционного управления"</string>
<string name="text_9343612ee473edd6" formatted="false">"Удаленное соединение отключено из-за уведомления"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Пароль сохранён"</string>
<string name="text_1cbd391383283924" formatted="false">"Сохраненный пароль невозможно прочитать, введите его еще раз."</string>
<string name="text_6792b9f341380c68" formatted="false">"Сохраненный пароль невозможно сохранить, введите его еще раз."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del отправлено"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"Фоновая поддержка активности включена, переключающиеся приложения могут продолжать поддерживать соединение."</string>
<string name="text_506409ec08cb2a79" formatted="false">"Фоновая поддержка активности включена для возврата или отключения от уведомлений о соединении."</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Разрешено"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Прочитано"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Подключено "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Подключено к серверу"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Частота кадров"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Плавность"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Баланс"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"Файл лицензии в приложении отсутствует или поврежден, переустановите приложение."</string>
<string name="text_3f2904435942cc0f" formatted="false">"Версия для разработки содержит модификации, которые еще не были выпущены. По следующей ссылке приведен исходный код базовой версии."</string>
<string name="text_4338afaad52ea445" formatted="false">"Включить сохранение"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Включить приватный экран"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Компоненты с открытым кодом"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Описание прав на программное обеспечение с открытым исходным кодом."</string>
<string name="text_6e86570183c3cdd0" formatted="false">"На данный момент это последняя версия."</string>
<string name="text_24a52788cf474bf6" formatted="false">"В настоящее время удаленный конец не поддерживает прямой ввод этого текста. Пожалуйста, используйте версию управляемого конца, поддерживающую ввод текста, или отправьте его через буфер обмена."</string>
<string name="text_33f25b500697a7d9" formatted="false">"При успешном подключении здесь будет отображено ближайшее удаленное устройство."</string>
<string name="text_70341256d7317e7d" formatted="false">"Я прочитал и согласен с Политикой конфиденциальности."</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Выбранный файл (невозможно прочитать или недостаточно места)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"Идентификатор устройства не найден."</string>
<string name="text_97b7b18df07b9787" formatted="false">"Не удалось отправить команду. Повторите попытку."</string>
<string name="text_cb782027ca5986c7" formatted="false">"Команда отправлена, ждём подтверждения от компьютера..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Статус авторизации"</string>
<string name="text_6e684586884ebee6" formatted="false">"Приём"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"получить "</string>
<string name="text_d16488b987723161" formatted="false">"Локальный декодер"</string>
<string name="text_6b58198955259066" formatted="false">"Отозвать"</string>
<string name="text_c52e42ee856825db" formatted="false">"После выхода удаленное соединение будет остановлено, предварительный просмотр экрана закроется, а существующий предварительный просмотр будет очищен. Запись о подключении и пароль будут сохранены, и перед повторным подключением потребуется повторная авторизация."</string>
<string name="text_61f4788032ec596b" formatted="false">"Отозвать сетевую авторизацию"</string>
<string name="text_e736e419008ac10a" formatted="false">"Отозвать сетевую авторизацию?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Свернуть меню пульта дистанционного управления"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Скрыть клавиатуру"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Скрыть мышь"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Данные"</string>
<string name="text_94244f41d0a41378" formatted="false">"Не удалось сохранить файл. Повторите попытку."</string>
<string name="text_2553a785fef2a405" formatted="false">"Файл сохранен."</string>
<string name="text_d573b3f9a810ea04" formatted="false">"Тайм-аут выбора файла истек, сеанс завершен."</string>
<string name="text_985a5c380be49f4f" formatted="false">"Размер текста превышает 128 КБ."</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Отключить"</string>
<string name="text_09b1395f50589327" formatted="false">"Вернется на домашнюю страницу после отключения."</string>
<string name="text_a73c0680db0d2e06" formatted="false">"Отключить удаленное соединение?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Отключить"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"Новая версия"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"Доступна новая версия: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"Доступна новая версия: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"Нет непрочитанных объявлений"</string>
<string name="text_dca2c418ac967366" formatted="false">"Не удалось сохранить статус чтения локально. Повторите попытку."</string>
<string name="text_5c92da76360056cc" formatted="false">"Не удалось удалить объявление. Повторите попытку."</string>
<string name="text_615709dd3129313c" formatted="false">"Невозможно инициировать удаленное соединение."</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"Невозможно прочитать лицензию с открытым исходным кодом."</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Экран"</string>
<string name="text_ffe21a671190323e" formatted="false">"Показать пароль"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Не сейчас"</string>
<string name="text_6d9a7758610a2432" formatted="false">"Нет объявлений"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Недавние связи"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Сервер"</string>
<string name="text_dfe408e686175095" formatted="false">"Проверка Server TLS certificate не удалась."</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Адрес сервера"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Не удалось войти на сервер, проверьте адрес сервера и сеть."</string>
<string name="text_d270868960e68629" formatted="false">"Порт сервера"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"Тайм-аут подключения к серверу истек, проверьте адрес сервера и сеть."</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"Не включено"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"Не найден браузер, в котором можно было бы открыть ссылку."</string>
<string name="text_94bc3d40defef3e9" formatted="false">"Несанкционированное"</string>
<string name="text_4d0680f9efaef147" formatted="false">"Не прочитано"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Сервер не подключен · Нажмите, чтобы повторить попытку."</string>
<string name="text_7eea7966c0b746c5" formatted="false">"Приложение использует OpenFEC для прямого исправления ошибок по лицензии CeCILL-C 1.0 с ограниченной гарантией и ответственностью. Подробности — в тексте лицензии."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"Местный идентификатор "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"Локальный идентификатор {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Исходный код и инструкции по сборке этой версии."</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"Ревизия исходного кода этой сборки недоступна."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Отпустите, чтобы обновить"</string>
<string name="text_47077144715b7074" formatted="false">"Инструкции по сборке"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Рецепты сборки и исправления проектов."</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"Открыть исходный код"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"Не удалось проверить обновления. Повторите позже."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Проверить обновления"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Закрытие экрана конфиденциальности…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Установление безопасного соединения…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"Включение экрана конфиденциальности..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Проверка обновлений…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Очистка..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Получение локального идентификатора..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Чтение выбранного файла..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Подключение"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Подключение к серверу сигнализации..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Подключение к серверу..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Повторное подключение к серверу..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"Для этого устройства средство выбора файлов недоступно."</string>
<string name="text_bce2377283c2455a" formatted="false">"Очистить"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Удалить превью"</string>
<string name="text_ecc809e44637de18" formatted="false">"Удалить превью?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Исходный код и сборка"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Версия исходного кода"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Версия"</string>
<string name="text_16410ee9663fad10" formatted="false">"Положение об авторских правах и лицензии."</string>
<string name="text_11de77e1e41e680f" formatted="false">"Компьютерная клавиатура"</string>
<string name="text_20f57900234debd7" formatted="false">"Качество"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Режим видео"</string>
<string name="text_82a5600c470179fa" formatted="false">"Задержка видео"</string>
<string name="text_33b458faaf967e15" formatted="false">"Настройки видео"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Качество видео"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Частота захвата видео"</string>
<string name="text_66062971234f422d" formatted="false">"Тачпад"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Родственная мышь"</string>
<string name="text_de32e20193ad1042" formatted="false">"Понятно"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"ОК"</string>
<string name="text_36f33adaf0942634" formatted="false">"Подтвердить"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Подтвердите соединение"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Не в сети"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Пробел"</string>
<string name="text_471a579e8d87faac" formatted="false">"Диапазон портов: 1–65535."</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Символы и клавиши"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Подождите, пока компьютер вернется в состояние экрана конфиденциальности; старые компьютеры могут его не поддерживать."</string>
<string name="text_5ee880e2524a983a" formatted="false">"Ожидание информации об удаленном экране."</string>
<string name="text_674c723a85ee7f57" formatted="false">"Ожидание удаленного экрана..."</string>
<string name="text_119f0483da4756c4" formatted="false">"Ожидание разрешения на конфиденциальность."</string>
<string name="text_7916071c1a8ee458" formatted="false">"Системная клавиатура"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Касание"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Абсолютная мышь"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Сеть"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Пользовательский"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Виртуальная мышь"</string>
<string name="text_62c199f37bf95869" formatted="false">"Приватный экран отключён локальным сочетанием клавиш. Заблокируйте его снова, чтобы восстановить защиту и запретить локальную разблокировку."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"Управляемая версия слишком низкая, обновите ее до последней версии."</string>
<string name="text_d814476826827b78" formatted="false">"Кодер удалённой стороны"</string>
<string name="text_488402883e27f6f0" formatted="false">"Текст для отправки на удаленный конец"</string>
<string name="text_c20f7618d330a854" formatted="false">"Видео"</string>
<string name="text_310c625425f014d8" formatted="false">"Лицензия"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Лицензии и авторские права"</string>
<string name="text_e24134b643a17975" formatted="false">"Лицензия и уведомление об авторских правах доступны в автономном режиме."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"Не удалось сохранить идентификационные данные устройства. Проверьте безопасное хранилище."</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"Идентификатор устройства и выбранные сохраненные пароли доступа шифруются с помощью хранилища ключей Android. Записи подключений сохраняются только локально и не включаются в резервную копию устройства. Содержимое буфера обмена очищается после завершения сеанса. Оставайтесь в сеансе с помощью «картинка в картинке» или подключенных уведомлений после выхода из приложения и отключайтесь внутри приложения или уведомлений."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Настройки"</string>
<string name="text_4f8771952222a011" formatted="false">"Настройки, есть новая версия"</string>
<string name="text_f4f712d5be91721a" formatted="false">"Пароль доступа"</string>
<string name="text_dd6c465847af1b60" formatted="false">"Неверный пароль доступа, пожалуйста, введите его еще раз."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Не удалось проверить сертификат · Нажмите, чтобы повторить попытку."</string>
<string name="text_44f3d4d52347095a" formatted="false">"Сначала пройдите онлайн-авторизацию."</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Пожалуйста, включите системную службу CrossDesk на удаленном конце и повторите попытку."</string>
<string name="text_990fd23c62de5ccf" formatted="false">"Время ожидания запроса истекло, а состояние экрана конфиденциальности компьютера не подтверждено."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Пожалуйста, дождитесь отправки текущего файла."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Введите порт в диапазоне 1–65535."</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Введите ID из 9 цифр"</string>
<string name="text_285a0714894d2899" formatted="false">"Пожалуйста, введите действительный пароль доступа."</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"Не удалось применить настройки. Повторите попытку."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Лицензия программы"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Назад"</string>
<string name="text_7565f94031582c7c" formatted="false">"Возврат к панели управления."</string>
<string name="text_55113b6925c362fd" formatted="false">"Пока нет записей о подключении"</string>
<string name="text_3aa527b2106cc987" formatted="false">"Это объявление было обновлено или удалено. Чтобы просмотреть его, вернитесь к списку объявлений."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"Удаленная задача закрыта."</string>
<string name="text_c255841f68096d3b" formatted="false">"Удаленный рабочий стол"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Сенсорная область удаленного рабочего стола"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Удаленный рабочий стол: коснитесь и щелкните, перетащите для перемещения, нажмите и удерживайте правую кнопку, сведите пальцы для масштабирования, увеличьте масштаб и перетащите экран двумя пальцами, проведите двумя пальцами вверх и вниз для прокрутки в исходном масштабе."</string>
<string name="text_3535087994d050de" formatted="false">"Удаленный компьютер"</string>
<string name="text_ed2e03072ce131de" formatted="false">"Удаленное окно было закрыто, или не удалось запустить фоновую проверку активности, и сеанс завершился."</string>
<string name="text_678b783fb578172b" formatted="false">"Удаленное подключение"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Неверный удаленный пароль."</string>
<string name="text_a2ce499295e358ae" formatted="false">"Удаленная сторона закрыла соединение."</string>
<string name="text_176e117df3ace734" formatted="false">"Удаленный конец в настоящее время недоступен."</string>
<string name="text_d2c0694243f93249" formatted="false">"Срок действия пароля удаленного доступа истек, подключитесь повторно."</string>
<string name="text_a5574109f0208e89" formatted="false">"Подключиться"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Подключение "</string>
<string name="text_89c4766afc85125f" formatted="false">"Соединение не удалось"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Не удалось подключиться · Нажмите, чтобы повторить попытку."</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Соединение потеряно"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Соединение завершено"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Задержка соединения (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Доступно при подключении к поддерживаемому устройству."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Блокировка горизонтального экрана после установления соединения."</string>
<string name="text_f741e0536210eafa" formatted="false">"Запрос на подключение"</string>
<string name="text_485a26050ce57431" formatted="false">"Способ подключения"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Подключиться к серверу"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Подключитесь к серверу, чтобы получать последние объявления."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"Тайм-аут соединения истек, проверьте идентификатор устройства, сервер и удаленный онлайн-статус."</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Подключение к удаленному рабочему столу"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Возврат"</string>
<string name="text_e02926784fcedb91" formatted="false">"Объявления"</string>
<string name="text_465a092680d132b3" formatted="false">"Некоторые предварительные просмотры не удалось удалить. Повторите попытку."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Попробуйте еще раз"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Повторить попытку сохранения статуса чтения."</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Заблокировать"</string>
<string name="text_864200bbb241e067" formatted="false">"Клавиатура"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Прочитать и авторизоваться"</string>
<string name="text_86651d17a401c55b" formatted="false">"Конфиденциальность"</string>
<string name="text_26c6e011e343708c" formatted="false">"Конфиденциальность и авторизация"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Приватный экран"</string>
<string name="text_e157fafb76aad954" formatted="false">"Приватный экран выкл."</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Приватный экран вкл."</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Приватный экран разблокирован"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Неисправность экрана конфиденциальности."</string>
<string name="text_598a2747dbd21dd5" formatted="false">"Экран конфиденциальности временно недоступен."</string>
<string name="text_4d59be3c75189032" formatted="false">"Срок действия статуса экрана конфиденциальности истек, ожидание подтверждения от компьютера."</string>
<string name="text_1674532c70ed1db2" formatted="false">"Статус экрана конфиденциальности подлежит подтверждению."</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Политика конфиденциальности"</string>
<string name="text_554e957e8e3c00af" formatted="false">"Политику конфиденциальности временно невозможно прочитать. Пожалуйста, откажитесь от подключения к Интернету и переустановите приложение."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Без звука"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Аудио"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Сайт проекта"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"Предварительный просмотр может содержать личную или рабочую информацию с удаленного компьютера. После его включения каждое подключение будет автоматически сохранять изображение на устройстве и отображать его при последних подключениях. Вы можете закрыть и очистить в любое время."</string>
<string name="text_b1c27820fec23edb" formatted="false">"Высокое"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"По умолчанию"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"По умолчанию выключено. При включении экран войдет и останется в альбомной ориентации при подключении телефона; в выключенном состоянии он будет следовать направлению устройства. Планшеты и мультиокна не затронуты."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Правая кнопка мыши, нажмите и перетащите, отпустите, чтобы завершить перетаскивание."</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Левую кнопку мыши нажмите и перетащите, отпустите, чтобы завершить перетаскивание."</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Управление мышью"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Колесо мыши: проведите вверх и вниз, чтобы выйти за пределы диапазона клавиш."</string>
<string name="text_9aa238fc6121dede" formatted="false">", коэффициент потери пакетов "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", отправить "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", пароль сохранен"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", получить "</string>
<string name="text_42237b5ec70c26f3" formatted="false">"нажмите «Повторить»."</string>
</resources>
@@ -0,0 +1,308 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by localization/generate.py. -->
<resources>
<string name="text_58b6df1059088699" formatted="false">" · Completed"</string>
<string name="text_53af552fb3af7f6e" formatted="false">" Transfer failed"</string>
<string name="text_23ebae95d26dfe5e" formatted="false">" devices"</string>
<string name="text_a7f4dfd234a43b16" formatted="false">" Article · "</string>
<string name="text_2c894057fc526576" formatted="false">" unread items"</string>
<string name="text_d0ac753c6681e578" formatted="false">"CrossDesk remote connection"</string>
<string name="text_0991c6fc1de4ffd6" formatted="false">"GNU GPL version 3"</string>
<string name="text_5ae7dc4079223967" formatted="false">"OpenFEC notice"</string>
<string name="text_69ad29820a259526" formatted="false">"OpenFEC license and source"</string>
<string name="text_63ae4a0c7a448d21" formatted="false">"P2P direct connection"</string>
<string name="text_70cae3c6aad8e152" formatted="false">"SRTP enabled"</string>
<string name="text_ef813fec7d6d8a3b" formatted="false">"TURN relay"</string>
<string name="text_ebede975edd52aee" formatted="false">"{0} transmission failed"</string>
<string name="text_b3ffb6e982ecf5df" formatted="false">"Devices: {0}"</string>
<string name="text_fc66a78b9e27fecf" formatted="false">"{0} unread items"</string>
<string name="text_dcb59cdcf66150a1" formatted="false">"{0}, receiving {1}, sending {2}, packet loss rate {3}"</string>
<string name="text_01965b3249458d01" formatted="false">"● Connected"</string>
<string name="text_eb4236fe6a926a5a" formatted="false">"Pull down to refresh"</string>
<string name="text_37772187ab854191" formatted="false">"Loss Rate"</string>
<string name="text_a567bdaa11367f26" formatted="false">"Medium"</string>
<string name="text_e6ff95a9fe6acc7f" formatted="false">"Prioritizes keeping the picture smooth, and will reduce resolution more aggressively when bandwidth or encoding pressure is high."</string>
<string name="text_aa9e366f68d3d097" formatted="false">"Low"</string>
<string name="text_f03d03eb35e3330c" formatted="false">"Using a custom server"</string>
<string name="text_2e9f4819e064ce12" formatted="false">"Save password"</string>
<string name="text_10019cef593819ed" formatted="false">"Save received file"</string>
<string name="text_3bcdcd330bb3044c" formatted="false">"Save remote screen previews"</string>
<string name="text_0f197074373e38fd" formatted="false">"Save remote screen preview?"</string>
<string name="text_71ca53f95a8484da" formatted="false">"The signaling connection is interrupted, please reconnect."</string>
<string name="text_789f22a8b1b34797" formatted="false">"Announcement loading failed, please try again."</string>
<string name="text_00554b17c432937e" formatted="false">"Announcement details"</string>
<string name="text_2428420e407fe2e8" formatted="false">"Total "</string>
<string name="text_d977c7568cc5201e" formatted="false">"Total {0} items ·{1} unread items"</string>
<string name="text_52d25a9e30ba94f1" formatted="false">"About"</string>
<string name="text_459c1e980c1c8ce2" formatted="false">"About CrossDesk"</string>
<string name="text_390a95b65eb33052" formatted="false">"About, there is a new version"</string>
<string name="text_3fd47edce45b3603" formatted="false">"Close"</string>
<string name="text_56a18c93e12fdc4e" formatted="false">"Close control bar"</string>
<string name="text_4eb0565cc4658d7f" formatted="false">"Disable privacy screen"</string>
<string name="text_7d466b69be42fc43" formatted="false">"Balances clarity and smoothness, only gradually reducing resolution under constant pressure."</string>
<string name="text_b92421822c213c2b" formatted="false">"Resolution"</string>
<string name="text_2f9daa828907b93f" formatted="false">"Delete"</string>
<string name="text_0e46cdb3442edfba" formatted="false">"Delete announcement"</string>
<string name="text_f2cf9101bb2d8816" formatted="false">"Delete record"</string>
<string name="text_d087671ca7df7f0f" formatted="false">"Delete this announcement?"</string>
<string name="text_33e7584cd5c252d7" formatted="false">"Refresh announcement"</string>
<string name="text_8c10e2c1728ddf8e" formatted="false">"Clipboard"</string>
<string name="text_4927a53bcc886afb" formatted="false">"Loading…"</string>
<string name="text_af90a08fec8ee28d" formatted="false">"Load more"</string>
<string name="text_a739f24a9fd8a5d9" formatted="false">"Update recommended"</string>
<string name="text_861521c9e107c648" formatted="false">"Native connection initialization failed"</string>
<string name="text_edecf0ae6e5144f9" formatted="false">"Send"</string>
<string name="text_43b7b60867ca51e7" formatted="false">"Send "</string>
<string name="text_da7df5a5543c4ba1" formatted="false">"After sending, click \"Paste\" on the remote keyboard. The received remote text can be manually copied to the local machine."</string>
<string name="text_e7281fbca7af369c" formatted="false">"Send file"</string>
<string name="text_ce6c02d19022e016" formatted="false">"Send text"</string>
<string name="text_2cd0f3be8738a86c" formatted="false">"Cancel"</string>
<string name="text_3687f953cfe85fb8" formatted="false">"Cancel connection"</string>
<string name="text_72814b15a6fa152c" formatted="false">"Each component is subject to the listed open source license, and the rights granted are not subject to the general terms of use of this application. Viewing source code requires an internet connection."</string>
<string name="text_e009c148d1890dac" formatted="false">"Total"</string>
<string name="text_81c92013219dd115" formatted="false">"Agree and continue"</string>
<string name="text_409696774c9998ce" formatted="false">"Keep connected in the background, click to return to the remote desktop"</string>
<string name="text_dcb8d4e2e6973912" formatted="false">"Background keepalive failed to start, please keep the application in the foreground"</string>
<string name="text_d96a21463557171d" formatted="false">"Background keepalive failed to start, please keep the application in the foreground and reconnect."</string>
<string name="text_952f7300de0a7bd9" formatted="false">"The background connection has ended, please reconnect."</string>
<string name="text_da904f715e1cff87" formatted="false">"The background connection service has stopped, please reconnect."</string>
<string name="text_0812359e18082071" formatted="false">"New previews will still be saved on subsequent connections."</string>
<string name="text_b9086662b1df1f20" formatted="false">"Online"</string>
<string name="text_39f2f444a042643e" formatted="false">"Fill in the host name or IPv4 address, without protocol and path"</string>
<string name="text_473400815c0f687d" formatted="false">"Sound"</string>
<string name="text_ab5ca1fab212f4ca" formatted="false">"Copy remote text"</string>
<string name="text_c92269b2039e7aa3" formatted="false">"Always maintain a higher resolution and reduce the frame rate to ensure picture clarity when bandwidth is insufficient."</string>
<string name="text_7ff73de85ec16d15" formatted="false">"Media encryption"</string>
<string name="text_22dc6217af77bc37" formatted="false">"Letters &amp; numbers"</string>
<string name="text_9ca1cdaf831182e3" formatted="false">"Secure storage or certificate initialization failed, please try again or clear application data"</string>
<string name="text_c0b3fbff51ccc40b" formatted="false">"Done"</string>
<string name="text_678328f6f02881c7" formatted="false">"You can check for updates after completing privacy authorization"</string>
<string name="text_c4361f9abdba4882" formatted="false">"Register the local identity after completing privacy authorization"</string>
<string name="text_c29b888e357d727d" formatted="false">"Password saving failed, this connection can still be used"</string>
<string name="text_3f31300a23afb390" formatted="false">"Passwords are saved encrypted in the native Android Keystore"</string>
<string name="text_8d3b052c4c67acda" formatted="false">"Remote ID"</string>
<string name="text_0512084e130984bc" formatted="false">"Peer ID "</string>
<string name="text_c5147d65960196b0" formatted="false">"Peer ID {0}"</string>
<string name="text_882b705d31ac7e2a" formatted="false">"All remote previews saved on this machine will be deleted, and the connection records and passwords will be retained."</string>
<string name="text_60cc99cc0013972e" formatted="false">"Screen orientation"</string>
<string name="text_9e7294d264afecb7" formatted="false">"Expand remote control menu"</string>
<string name="text_9343612ee473edd6" formatted="false">"Remote connection disconnected from notification"</string>
<string name="text_9d32476fe83aef60" formatted="false">"Password saved"</string>
<string name="text_1cbd391383283924" formatted="false">"The saved password cannot be read, please re-enter it."</string>
<string name="text_6792b9f341380c68" formatted="false">"The saved password cannot be retained, please re-enter it."</string>
<string name="text_74d3f0927d465047" formatted="false">"Ctrl+Alt+Del sent"</string>
<string name="text_c28d86acbedba3b6" formatted="false">"Background keep-alive is enabled, switching applications can continue to maintain the connection"</string>
<string name="text_506409ec08cb2a79" formatted="false">"Background keepalive enabled to return or disconnect from connection notifications"</string>
<string name="text_521e65ffc7d0ff2d" formatted="false">"Granted"</string>
<string name="text_efdd1a47e4beb2f9" formatted="false">"Read"</string>
<string name="text_1f672b9b18d18e87" formatted="false">"Connected "</string>
<string name="text_9c05d9d7fa1b0abd" formatted="false">"Connected to server"</string>
<string name="text_f9404bf335bc14cf" formatted="false">"Frame rate"</string>
<string name="text_9ae0c69e26db1d6a" formatted="false">"Smooth"</string>
<string name="text_df544e46aa4d5c17" formatted="false">"Balanced"</string>
<string name="text_3362e8fa7f65cc2f" formatted="false">"The license file in the application is missing or damaged, please reinstall the application."</string>
<string name="text_3f2904435942cc0f" formatted="false">"The development version contains modifications that have not yet been released. The following link is the source code of the basic version."</string>
<string name="text_4338afaad52ea445" formatted="false">"Enable saving"</string>
<string name="text_c8bb8255390df3b6" formatted="false">"Enable privacy screen"</string>
<string name="text_c6ecaca362ae1c80" formatted="false">"Open-source components"</string>
<string name="text_6099db946bdb5ee4" formatted="false">"Open source software rights description"</string>
<string name="text_6e86570183c3cdd0" formatted="false">"This is currently the latest version"</string>
<string name="text_24a52788cf474bf6" formatted="false">"Currently, the remote end does not support direct input of this text. Please use a version of the controlled end that supports text input, or send it through the clipboard."</string>
<string name="text_33f25b500697a7d9" formatted="false">"Upon successful connection, the nearest remote device will be displayed here."</string>
<string name="text_70341256d7317e7d" formatted="false">"I have read and agreed to the Privacy Policy"</string>
<string name="text_85c4c8fda0461f35" formatted="false">"Selected file (cannot be read or insufficient space)"</string>
<string name="text_9b58c9ff5f6a856f" formatted="false">"The device ID was not found"</string>
<string name="text_97b7b18df07b9787" formatted="false">"Command sending failed, please try again"</string>
<string name="text_cb782027ca5986c7" formatted="false">"The command has been sent, waiting for computer confirmation..."</string>
<string name="text_3ca40a586103f6b7" formatted="false">"Authorization status"</string>
<string name="text_6e684586884ebee6" formatted="false">"Receive"</string>
<string name="text_84ae92e54e3078aa" formatted="false">"receive "</string>
<string name="text_d16488b987723161" formatted="false">"Local decoder"</string>
<string name="text_6b58198955259066" formatted="false">"Withdraw"</string>
<string name="text_c52e42ee856825db" formatted="false">"After withdrawing, the remote connection will be stopped, the screen preview will be closed and the existing preview will be cleared. The connection record and password will be retained, and authorization is required again before reconnecting."</string>
<string name="text_61f4788032ec596b" formatted="false">"Withdraw network authorization"</string>
<string name="text_e736e419008ac10a" formatted="false">"Withdraw network authorization?"</string>
<string name="text_73431dcf0afb7a65" formatted="false">"Collapse remote control menu"</string>
<string name="text_d826c4ece4dd4031" formatted="false">"Hide keyboard"</string>
<string name="text_6e9a457c0c3c34ed" formatted="false">"Hide mouse"</string>
<string name="text_5440f7424f2865bf" formatted="false">"Data"</string>
<string name="text_94244f41d0a41378" formatted="false">"File saving failed, please try again"</string>
<string name="text_2553a785fef2a405" formatted="false">"File saved"</string>
<string name="text_d573b3f9a810ea04" formatted="false">"File selection timed out and session ended"</string>
<string name="text_985a5c380be49f4f" formatted="false">"Text exceeds 128 KB"</string>
<string name="text_f33ac04eece6a6a6" formatted="false">"Disconnect"</string>
<string name="text_09b1395f50589327" formatted="false">"Will return to the homepage after disconnecting"</string>
<string name="text_a73c0680db0d2e06" formatted="false">"Disconnect remote connection?"</string>
<string name="text_a33f3804768e68ee" formatted="false">"Disconnect"</string>
<string name="text_47ceaf502c5a05d6" formatted="false">"New version"</string>
<string name="text_78823e6cf1a8afe5" formatted="false">"New version available: v"</string>
<string name="text_b7e58b67fa8f7e78" formatted="false">"New version available: v{0}"</string>
<string name="text_ef23787ec32c5588" formatted="false">"No unread announcements"</string>
<string name="text_dca2c418ac967366" formatted="false">"Unable to save reading status locally. Please try again."</string>
<string name="text_5c92da76360056cc" formatted="false">"Unable to delete this announcement. Please try again."</string>
<string name="text_615709dd3129313c" formatted="false">"Unable to initiate remote connection"</string>
<string name="text_a5c4f4ad79a640ae" formatted="false">"Unable to read open source license"</string>
<string name="text_1a13ea5910a0c5e6" formatted="false">"Display"</string>
<string name="text_ffe21a671190323e" formatted="false">"Show password"</string>
<string name="text_a94460e8a04f04c5" formatted="false">"Not now"</string>
<string name="text_6d9a7758610a2432" formatted="false">"No announcements"</string>
<string name="text_0b290c6600d92f43" formatted="false">"Recent connections"</string>
<string name="text_2388cbc6a486af9f" formatted="false">"Server"</string>
<string name="text_dfe408e686175095" formatted="false">"Server TLS certificate verification failed"</string>
<string name="text_d3716cc5a2f5a810" formatted="false">"Server Address"</string>
<string name="text_2a9608d4465d0f70" formatted="false">"Server login failed, please check the server address and network"</string>
<string name="text_d270868960e68629" formatted="false">"Server port"</string>
<string name="text_7a4e4a6a1b479a52" formatted="false">"Server connection timed out, please check the server address and network"</string>
<string name="text_f95ea7f4c063eea7" formatted="false">"Not enabled"</string>
<string name="text_c8f7ed6f2186fb98" formatted="false">"No browser found to open the link"</string>
<string name="text_94bc3d40defef3e9" formatted="false">"Unauthorized"</string>
<string name="text_4d0680f9efaef147" formatted="false">"Unread"</string>
<string name="text_d1fcb66a323a16f5" formatted="false">"Server not connected · Click to try again"</string>
<string name="text_7eea7966c0b746c5" formatted="false">"This application uses OpenFEC for forward error correction under CeCILL-C 1.0, which provides limited warranties and limits liability. See the full license for details."</string>
<string name="text_c611b4e50b0dbc08" formatted="false">"Local ID "</string>
<string name="text_97d10220f08dd7fc" formatted="false">"Local ID {0}"</string>
<string name="text_91f15d505770d7a0" formatted="false">"Source code and build instructions for this version"</string>
<string name="text_5b2c8ca2c5a87241" formatted="false">"Source revision is unavailable for this build."</string>
<string name="text_f0e274aa130e08a2" formatted="false">"Release to refresh"</string>
<string name="text_47077144715b7074" formatted="false">"Build instructions"</string>
<string name="text_837ab2d0b2f4cb98" formatted="false">"Build recipes and project patches"</string>
<string name="text_921c3b62f1f7d6f4" formatted="false">"View source code"</string>
<string name="text_fdd1059f10c4b979" formatted="false">"Check failed. Please try again later."</string>
<string name="text_7f68ebad19ba6bcd" formatted="false">"Check for updates"</string>
<string name="text_4f4d1e704e38c161" formatted="false">"Closing privacy screen…"</string>
<string name="text_2a05f6fbd3f4436e" formatted="false">"Establishing secure connection…"</string>
<string name="text_0da575e3c92de73a" formatted="false">"Turning on privacy screen..."</string>
<string name="text_91834aebecabcbc3" formatted="false">"Checking for updates…"</string>
<string name="text_0571a5f5f8ec4c83" formatted="false">"Clearing..."</string>
<string name="text_0e6336c8fe918730" formatted="false">"Obtaining local ID..."</string>
<string name="text_faebd20c2801fd0f" formatted="false">"Reading selected file..."</string>
<string name="text_694186911ae3e4da" formatted="false">"Connecting"</string>
<string name="text_c3ccc7f7d37533e3" formatted="false">"Connecting to signaling server..."</string>
<string name="text_4024d3c0f467a4f6" formatted="false">"Connecting to server..."</string>
<string name="text_46fe1ef52ec3a10f" formatted="false">"Reconnecting to server..."</string>
<string name="text_4e4fce649fc00238" formatted="false">"No file picker available for this device"</string>
<string name="text_bce2377283c2455a" formatted="false">"Clear"</string>
<string name="text_ee0e76b8507212de" formatted="false">"Clear previews"</string>
<string name="text_ecc809e44637de18" formatted="false">"Clear previews?"</string>
<string name="text_2782e4485ad23d34" formatted="false">"Source code and build instructions"</string>
<string name="text_1f566fe8f8b718f3" formatted="false">"Source code version"</string>
<string name="text_5f76b2bf82dd2c5d" formatted="false">"Version"</string>
<string name="text_16410ee9663fad10" formatted="false">"Copyright and License Statement"</string>
<string name="text_11de77e1e41e680f" formatted="false">"Computer keyboard"</string>
<string name="text_20f57900234debd7" formatted="false">"Quality"</string>
<string name="text_43b04aba53a54cf5" formatted="false">"Video Preference"</string>
<string name="text_82a5600c470179fa" formatted="false">"Video latency"</string>
<string name="text_33b458faaf967e15" formatted="false">"Video settings"</string>
<string name="text_afb5acdf8f07fadb" formatted="false">"Video Quality"</string>
<string name="text_12b41b7a3fbcf8ca" formatted="false">"Video Capture Frame Rate"</string>
<string name="text_66062971234f422d" formatted="false">"Trackpad"</string>
<string name="text_c421dc051bea3f11" formatted="false">"Relative mouse"</string>
<string name="text_de32e20193ad1042" formatted="false">"Got it"</string>
<string name="text_fac2a67ad87807c4" formatted="false">"OK"</string>
<string name="text_36f33adaf0942634" formatted="false">"Confirm"</string>
<string name="text_a5b7fba4dbd8f622" formatted="false">"Confirm connection"</string>
<string name="text_be1b4f3c6c1cda1e" formatted="false">"Offline"</string>
<string name="text_bfa4a6e0fe42ac28" formatted="false">"Space"</string>
<string name="text_471a579e8d87faac" formatted="false">"Port range is 1–65535"</string>
<string name="text_c7d0c77862e32b52" formatted="false">"Symbols &amp; keys"</string>
<string name="text_a562f07bd38c1a49" formatted="false">"Wait for the computer to return to the privacy screen state; older computers may not support it."</string>
<string name="text_5ee880e2524a983a" formatted="false">"Waiting for remote screen information"</string>
<string name="text_674c723a85ee7f57" formatted="false">"Waiting for the remote screen..."</string>
<string name="text_119f0483da4756c4" formatted="false">"Waiting for privacy authorization"</string>
<string name="text_7916071c1a8ee458" formatted="false">"System keyboard"</string>
<string name="text_02e9ba6fda719e2e" formatted="false">"Direct touch"</string>
<string name="text_8527c8c6d1ccc1ca" formatted="false">"Absolute Mouse"</string>
<string name="text_872428a7210d5e0c" formatted="false">"Network"</string>
<string name="text_4eafa9e925b30bcd" formatted="false">"Custom"</string>
<string name="text_dffd0c12b8f27247" formatted="false">"Virtual mouse"</string>
<string name="text_62c199f37bf95869" formatted="false">"The remote privacy screen was unlocked using the local shortcut. Lock it again to restore privacy and disable local shortcut unlocking."</string>
<string name="text_a569ce729c1d6e19" formatted="false">"The controlled version is too low, please upgrade to the latest version."</string>
<string name="text_d814476826827b78" formatted="false">"Remote encoder"</string>
<string name="text_488402883e27f6f0" formatted="false">"Text to be sent to the remote end"</string>
<string name="text_c20f7618d330a854" formatted="false">"Video"</string>
<string name="text_310c625425f014d8" formatted="false">"License"</string>
<string name="text_5c34f7d0e2124dca" formatted="false">"Licenses and copyright notices"</string>
<string name="text_e24134b643a17975" formatted="false">"The license and copyright notice are available offline."</string>
<string name="text_5e7628f043ce71d0" formatted="false">"Device identity failed to save, please check secure storage"</string>
<string name="text_5d35ee0d7945fcbc" formatted="false">"Device identity and selected saved access passwords are encrypted using Android Keystore. Connection records are only saved locally and are not included in device backup. The clipboard contents are cleared when the session is ended. Stay in session with picture-in-picture or connected notifications after leaving the app, and disconnect within the app or notifications."</string>
<string name="text_df3d58c7d84b85f2" formatted="false">"Settings"</string>
<string name="text_4f8771952222a011" formatted="false">"Settings, there is a new version"</string>
<string name="text_f4f712d5be91721a" formatted="false">"Access password"</string>
<string name="text_dd6c465847af1b60" formatted="false">"The access password is wrong, please re-enter it."</string>
<string name="text_defba1d2989bfab4" formatted="false">"Certificate verification failed · Click to try again"</string>
<string name="text_44f3d4d52347095a" formatted="false">"Please complete the online authorization first"</string>
<string name="text_2a07ca002bc5093d" formatted="false">"Please enable the CrossDesk system service on the remote end and try again"</string>
<string name="text_990fd23c62de5ccf" formatted="false">"The request timed out and the computer\'s privacy screen status has not been confirmed."</string>
<string name="text_e5c8fa6dcf7b2a8b" formatted="false">"Please wait for the current file to be sent."</string>
<string name="text_9305d9d72b06110d" formatted="false">"Please enter a port in the range 1–65535"</string>
<string name="text_3c7223dfb8dde5cf" formatted="false">"Enter a 9-digit device ID"</string>
<string name="text_285a0714894d2899" formatted="false">"Please enter a valid access password"</string>
<string name="text_c808bb77ffd2b4cd" formatted="false">"Could not apply settings. Try again."</string>
<string name="text_a762e7e650e285fa" formatted="false">"Software license"</string>
<string name="text_572cf45ba43634b3" formatted="false">"Back"</string>
<string name="text_7565f94031582c7c" formatted="false">"Return to control bar"</string>
<string name="text_55113b6925c362fd" formatted="false">"No connection record yet"</string>
<string name="text_3aa527b2106cc987" formatted="false">"This announcement has been updated or deleted, please return to the announcement list to view it."</string>
<string name="text_e357aa9a0fb28c16" formatted="false">"Remote task is closed"</string>
<string name="text_c255841f68096d3b" formatted="false">"Remote Desktop"</string>
<string name="text_133f4c7dabd88b97" formatted="false">"Remote desktop touch area"</string>
<string name="text_5931aeb3778f28f1" formatted="false">"Remote desktop: Touch and click, drag to move, press and hold the right button, pinch to zoom, zoom in and drag the screen with two fingers, slide up and down with two fingers to scroll at the original scale"</string>
<string name="text_3535087994d050de" formatted="false">"Remote computer"</string>
<string name="text_ed2e03072ce131de" formatted="false">"The remote window has been closed, or the background keepalive failed to start, and the session has ended."</string>
<string name="text_678b783fb578172b" formatted="false">"Remote connection"</string>
<string name="text_deccbd3eb9ba45f2" formatted="false">"Incorrect remote password"</string>
<string name="text_a2ce499295e358ae" formatted="false">"The remote end has closed the connection"</string>
<string name="text_176e117df3ace734" formatted="false">"The remote end is currently unavailable"</string>
<string name="text_d2c0694243f93249" formatted="false">"The remote access password has expired, please reconnect."</string>
<string name="text_a5574109f0208e89" formatted="false">"Connect"</string>
<string name="text_1d54bfbae5f8ac08" formatted="false">"Connection "</string>
<string name="text_89c4766afc85125f" formatted="false">"Connection failed"</string>
<string name="text_04bf1a61ba1369f6" formatted="false">"Connection failed · Click to try again"</string>
<string name="text_2ab7d89557e779ab" formatted="false">"Connection lost"</string>
<string name="text_c1d20bf4c3e471c1" formatted="false">"Connection ended"</string>
<string name="text_e57d2cbc0e9908e6" formatted="false">"Connection delay (RTT)"</string>
<string name="text_db4d4f0fab78498a" formatted="false">"Available when connected to a supported remote device."</string>
<string name="text_2123a9ed718f2c40" formatted="false">"Lock landscape screen after connection is established"</string>
<string name="text_f741e0536210eafa" formatted="false">"Connection prompt"</string>
<string name="text_485a26050ce57431" formatted="false">"Connection method"</string>
<string name="text_5a048351a77fe88d" formatted="false">"Connect to server"</string>
<string name="text_5854c4add7ab0f7a" formatted="false">"Connect to the server to get the latest announcements."</string>
<string name="text_32cd27b5a72515f3" formatted="false">"Connection timed out, please check the device ID, server and remote online status"</string>
<string name="text_6edc4b54cc1a9838" formatted="false">"Connect to remote desktop"</string>
<string name="text_d5407b4f3c666681" formatted="false">"Backspace"</string>
<string name="text_e02926784fcedb91" formatted="false">"Announcements"</string>
<string name="text_465a092680d132b3" formatted="false">"Some previews could not be cleared, please try again."</string>
<string name="text_b8784c8dd5636ff2" formatted="false">"Try again"</string>
<string name="text_a504fb6f162bdb20" formatted="false">"Retry saving reading status"</string>
<string name="text_ff9872a023e3a8aa" formatted="false">"Lock"</string>
<string name="text_864200bbb241e067" formatted="false">"Keyboard"</string>
<string name="text_1f0f014d4177a0cc" formatted="false">"Read and authorize"</string>
<string name="text_86651d17a401c55b" formatted="false">"Privacy"</string>
<string name="text_26c6e011e343708c" formatted="false">"Privacy and Authorization"</string>
<string name="text_6cad42fd3da85585" formatted="false">"Privacy screen"</string>
<string name="text_e157fafb76aad954" formatted="false">"Privacy screen off"</string>
<string name="text_bbef0eac0c04f214" formatted="false">"Privacy screen on"</string>
<string name="text_0c7535f7c5e43b6f" formatted="false">"Privacy screen unlocked"</string>
<string name="text_b62ac3dfb606c00b" formatted="false">"Privacy screen abnormality"</string>
<string name="text_598a2747dbd21dd5" formatted="false">"Privacy screen is temporarily unavailable"</string>
<string name="text_4d59be3c75189032" formatted="false">"Privacy screen status has expired, waiting for computer confirmation"</string>
<string name="text_1674532c70ed1db2" formatted="false">"Privacy screen status to be confirmed"</string>
<string name="text_e464b41fd5c45ba5" formatted="false">"Privacy Policy"</string>
<string name="text_554e957e8e3c00af" formatted="false">"The privacy policy cannot be read temporarily. Please refuse to connect to the Internet and reinstall the application."</string>
<string name="text_7db2e189ecb01065" formatted="false">"Mute"</string>
<string name="text_296c632ec857a0ba" formatted="false">"Audio"</string>
<string name="text_0cadb49096a382e0" formatted="false">"Project website"</string>
<string name="text_3beca60bd0c654f7" formatted="false">"Previews may contain personal or work information from the remote computer. After turning it on, each connection will automatically save a picture on the machine and display it in recent connections. You can close and clear at any time."</string>
<string name="text_b1c27820fec23edb" formatted="false">"High"</string>
<string name="text_844b8cc8dff7c1d8" formatted="false">"Default"</string>
<string name="text_4db8737054f3b0cc" formatted="false">"Off by default. When turned on, the screen will enter and remain in landscape orientation when the phone is connected; when turned off, it will follow the direction of the device. Tablets and multi-windows are not affected."</string>
<string name="text_1184a97edd8e1646" formatted="false">"Right mouse button, press and drag, release to end dragging"</string>
<string name="text_926eaa3af0b5e48e" formatted="false">"Left mouse button, press and drag, release to end dragging"</string>
<string name="text_269f39a07b4a4e01" formatted="false">"Mouse control"</string>
<string name="text_cba62aeeb4a63a23" formatted="false">"Mouse wheel, slide up and down to slide out of the key range"</string>
<string name="text_9aa238fc6121dede" formatted="false">", packet loss rate "</string>
<string name="text_0b5d16c21095fe10" formatted="false">", send "</string>
<string name="text_ba9a792d063a8c86" formatted="false">", password saved"</string>
<string name="text_f4c1f9d7896760a7" formatted="false">", receive "</string>
<string name="text_42237b5ec70c26f3" formatted="false">", click Retry"</string>
</resources>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="zh-Hans" />
<locale android:name="zh-Hant" />
<locale android:name="ja" />
<locale android:name="ko" />
<locale android:name="de" />
<locale android:name="fr" />
<locale android:name="es" />
<locale android:name="pt-BR" />
<locale android:name="ru" />
</locale-config>
+1 -1
View File
@@ -427,7 +427,7 @@ int main(int argc, char* argv[]) {
CSimpleIniA config;
config.SetUnicode(true);
config.LoadFile((paths.GetCachePath() / "config.ini").string().c_str());
console.SetLanguage(static_cast<int>(config.GetLongValue("Settings", "language", 0)));
console.SetLanguage(static_cast<int>(config.GetLongValue("Settings", "language", 10)));
std::cout << console.Text("console_cli_help");
return 0;
}
@@ -62,7 +62,7 @@ int ConfigCenter::Load() {
const long language_value =
ini_->GetLongValue(section_, "language", static_cast<long>(language_));
if (language_value < static_cast<long>(LANGUAGE::CHINESE) ||
language_value > static_cast<long>(LANGUAGE::RUSSIAN)) {
language_value > static_cast<long>(LANGUAGE::SYSTEM)) {
language_ = LANGUAGE::ENGLISH;
} else {
language_ = static_cast<LANGUAGE>(language_value);
@@ -261,7 +261,7 @@ int ConfigCenter::Save() {
int ConfigCenter::SetLanguage(LANGUAGE language) {
const int value = static_cast<int>(language);
if (value < 0 || value > 2 || StoreValues({{"language", std::to_string(value)}}) != 0) return -1;
if (value < 0 || value > static_cast<int>(LANGUAGE::SYSTEM) || StoreValues({{"language", std::to_string(value)}}) != 0) return -1;
language_ = language;
return 0;
}
@@ -20,7 +20,11 @@ namespace crossdesk {
class ConfigCenter {
public:
enum class LANGUAGE { CHINESE = 0, ENGLISH = 1, RUSSIAN = 2 };
enum class LANGUAGE {
CHINESE = 0, ENGLISH = 1, RUSSIAN = 2, TRADITIONAL_CHINESE = 3,
JAPANESE = 4, KOREAN = 5, GERMAN = 6, FRENCH = 7, SPANISH = 8,
BRAZILIAN_PORTUGUESE = 9, SYSTEM = 10
};
enum class CONNECTION_MODE { DEVICE_ID = 0, DIRECT_IP = 1 };
enum class VIDEO_ENCODE_FORMAT { H264 = 0, AV1 = 1 };
enum class TURN_MODE {
@@ -93,7 +97,7 @@ class ConfigCenter {
std::unique_ptr<IniStorage> ini_;
const char* section_ = "Settings";
LANGUAGE language_ = LANGUAGE::CHINESE;
LANGUAGE language_ = LANGUAGE::SYSTEM;
CONNECTION_MODE connection_mode_ = CONNECTION_MODE::DEVICE_ID;
bool enable_ip_direct_ = true;
ScreenCaptureMethod screen_capture_method_ = ScreenCaptureMethod::Auto;
@@ -1201,9 +1201,8 @@ void GuiApplication::InitializeSettings() {
settings_.LoadRecentConnectionAliases();
localization_language_index_ =
localization::detail::ClampLanguageIndex(language_button_value_);
language_button_value_ = localization_language_index_;
localization_language_ =
static_cast<ConfigCenter::LANGUAGE>(localization_language_index_);
static_cast<ConfigCenter::LANGUAGE>(language_button_value_);
privacy_.SetText({
localization::privacy_screen_unlock_hint[localization_language_index_]});
}
@@ -1563,11 +1562,7 @@ void GuiApplication::BindMainCallbacks(bool gallery_window) {
if (config_center_->SetConnectionMode(mode) != 0) {
LOG_WARN("Could not save connection mode preference");
main->set_offline_warning(UiText(
localization_language_index_ == 0
? "无法保存连接模式,下次启动可能不会恢复当前模式。"
: localization_language_index_ == 2
? "Не удалось сохранить режим подключения. При следующем запуске он может не восстановиться."
: "Could not save the connection mode. It may not be restored on the next launch."));
localization::Literal("无法保存连接模式,下次启动可能不会恢复当前模式。", localization_language_index_)));
}
});
main->on_toggle_gallery_fullscreen(
@@ -3078,13 +3073,9 @@ void GuiApplication::UpdateLocalization() {
return;
}
#if _WIN32
// All views refer to static literals; selecting the text needs no allocation
// and only runs when the language changes. Tick still checks connection state.
ui_->shutdown_reason = language == 0
? L"远程控制连接中,保留连接以确认或取消关机。"
: language == 2
? L"Удалённый сеанс активен. Соединение сохранено для подтверждения или отмены выключения."
: L"Remote session active. Keeping the connection available to confirm or cancel shutdown.";
static std::vector<std::wstring> shutdown_reasons(localization::GetSupportedLanguages().size());
ui_->shutdown_reason = localization::GetWindowsLocalizedLabel(
language, "shutdown_reason", L"Remote session active.", shutdown_reasons);
#endif
ui_localization::ApplyMainWindowStrings(ui_->main, language);
ui_localization::ApplyStreamWindowStrings(ui_->main, language);
@@ -3247,13 +3238,12 @@ void GuiApplication::SyncMainWindow() {
// Keep this filter separate from DeskHub's current group, keyed by name so
// adding/removing groups does not silently select a different one by index.
const auto tr_group = [this](const char* zh, const char* en, const char* ru) {
return UiText(localization_language_index_ == 0 ? zh
: localization_language_index_ == 2 ? ru : en);
const auto tr_group = [this](const char* zh) {
return UiText(localization::Literal(zh, localization_language_index_).c_str());
};
std::vector<slint::SharedString> recent_groups{
tr_group("全部设备", "All devices", "Все устройства"),
tr_group("未分组", "Ungrouped", "Без группы")};
tr_group("全部设备"),
tr_group("未分组")};
const bool lan_mode = ui_->main->get_lan_mode();
int recent_group_index = 0;
if (!lan_mode && ui_->recent_group_filter) {
@@ -4565,9 +4555,7 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
// common save succeeds. Restore the previous wall file if it fails.
if (!wall_settings || (wall_changed && !updated_wall.Save(ui_->gallery_path))) {
main->set_gallery_settings_error(UiText(
localization_language_index_ == 0 ? "保存失败,请重试"
: localization_language_index_ == 2 ? "Не удалось сохранить. Повторите попытку"
: "Could not save; try again"));
localization::Literal("保存失败,请重试", localization_language_index_)));
main->set_settings_tab(1);
return false;
}
@@ -4586,9 +4574,7 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
if (wall_changed && !ui_->gallery.Save(ui_->gallery_path))
LOG_ERROR("Failed to restore DeskHub settings after IP direct save failure");
main->set_settings_save_error(UiText(
localization_language_index_ == 0 ? "无法保存 IP 连接设置,请重试。"
: localization_language_index_ == 2 ? "Не удалось сохранить настройки подключения по IP. Повторите попытку."
: "Could not save IP connection settings. Try again."));
localization::Literal("无法保存 IP 连接设置,请重试。", localization_language_index_)));
return false;
}
#if defined(_WIN32) || defined(__APPLE__) || (defined(__linux__) && !defined(__OHOS__))
@@ -4596,22 +4582,18 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
if (wall_changed && !ui_->gallery.Save(ui_->gallery_path))
LOG_ERROR("Failed to restore DeskHub settings after lock preference save failure");
main->set_settings_save_error(UiText(
localization_language_index_ == 0 ? "无法保存断开后锁屏设置,请重试。"
: localization_language_index_ == 2 ? "Не удалось сохранить настройку блокировки при отключении. Повторите попытку."
: "Could not save lock-on-disconnect settings. Try again."));
localization::Literal("无法保存断开后锁屏设置,请重试。", localization_language_index_)));
return false;
}
#endif
if (config_center_->SetPrivacyLocalUnlock(main->get_privacy_local_unlock_allowed()) != 0) {
if (wall_changed) ui_->gallery.Save(ui_->gallery_path);
main->set_settings_save_error(UiText(
localization_language_index_ == 0 ? "无法保存隐私屏解锁设置,请重试。"
: localization_language_index_ == 2 ? "Не удалось сохранить настройку разблокировки. Повторите попытку."
: "Could not save privacy unlock settings. Try again."));
localization::Literal("无法保存隐私屏解锁设置,请重试。", localization_language_index_)));
return false;
}
language_button_value_ =
localization::detail::ClampLanguageIndex(main->get_language_index());
std::clamp(main->get_language_index(), 0, localization::kSystemLanguage);
video_encode_format_button_value_ = std::clamp(main->get_codec_index(), 0, 1);
enable_self_hosted_ = main->get_self_hosted_enabled();
#if !defined(__OHOS__)
@@ -4621,7 +4603,7 @@ bool GuiApplication::SaveSettingsFromUi(bool gallery_window) {
localization_language_ =
static_cast<ConfigCenter::LANGUAGE>(language_button_value_);
localization_language_index_ = language_button_value_;
localization_language_index_ = localization::detail::ClampLanguageIndex(language_button_value_);
config_center_->SetLanguage(localization_language_);
privacy_.SetText(
{localization::privacy_screen_unlock_hint[localization_language_index_],
@@ -4773,10 +4755,7 @@ void GuiApplication::ConnectFromUi(const std::string& input) {
const std::string remote_id = CompactPeerId(input);
if (!gallery::Workspace::ValidId(remote_id)) {
ui_->main->set_connection_input_error(
UiText(localization_language_index_ == 0 ? "请输入有效的 9 位设备 ID"
: localization_language_index_ == 2
? "Введите корректный 9-значный ID устройства"
: "Enter a valid 9-digit device ID"));
UiText(localization::Literal("请输入有效的 9 位设备 ID", localization_language_index_)));
return;
}
ui_->main->set_connection_input_error("");
@@ -5330,41 +5309,31 @@ bool GuiApplication::AddGalleryDevice(const std::string& input,
const std::string& password,
const std::string& group,
bool remember_password) {
const auto error = [this](const char* zh, const char* en, const char* ru) {
const auto error = [this](const char* zh) {
ui_->wall->set_gallery_notice(UiText(
localization_language_index_ == 0 ? zh
: localization_language_index_ == 2 ? ru : en));
localization::Literal(zh, localization_language_index_).c_str()));
return false;
};
const auto id = gallery::Workspace::NormalizeId(input);
if (!gallery::Workspace::ValidId(id))
return error("请输入有效的 9 位设备 ID",
"Enter a valid 9-digit device ID",
"Введите корректный 9-значный ID устройства");
return error("请输入有效的 9 位设备 ID");
if (password.empty() || password.size() > 6 ||
password.find_first_not_of(
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") !=
std::string::npos)
return error("请输入 1–6 位英文字母或数字密码",
"Use 1–6 letters (A–Z, a–z) or digits (0–9)",
"Пароль: 1–6 латинских букв или цифр");
return error("请输入 1–6 位英文字母或数字密码");
if (!signal_connected_)
return error("尚未连接服务器,请稍后重试",
"Not connected to the server; try again shortly",
"Нет соединения с сервером. Повторите позже");
return error("尚未连接服务器,请稍后重试");
using State = SlintUi::BatchOperation::State;
for (const auto& operation : ui_->batch_operations)
if (operation.id == id && operation.action == "connect" &&
(operation.state == State::Queued || operation.state == State::Running))
return error("该设备正在连接,请稍候",
"This device is already connecting",
"Устройство уже подключается");
return error("该设备正在连接,请稍候");
auto updated = ui_->gallery;
updated.Add(id, Trim(group));
if (!updated.Save(ui_->gallery_path))
return error("保存失败,请重试", "Could not save; try again",
"Не удалось сохранить. Повторите попытку");
return error("保存失败,请重试");
ui_->gallery = std::move(updated);
ui_->gallery_hidden_active_ids.erase(id);
if (auto props = FindRemoteSession(id)) {
@@ -5406,9 +5375,7 @@ bool GuiApplication::ImportGalleryRecent(const std::string& input_group) {
updated.AddMany(ids, group);
if (!updated.Save(ui_->gallery_path)) {
ui_->wall->set_gallery_notice(UiText(
localization_language_index_ == 0 ? "保存失败,请重试"
: localization_language_index_ == 2 ? "Не удалось сохранить. Повторите попытку"
: "Could not save; try again"));
localization::Literal("保存失败,请重试", localization_language_index_)));
return false;
}
ui_->gallery = std::move(updated);
@@ -5488,9 +5455,7 @@ bool GuiApplication::SaveDeviceGroups(const std::string& new_group,
auto updated = ui_->gallery;
if (!updated.SetGroups(id, names) || !updated.Save(ui_->gallery_path)) {
main->set_device_group_error(UiText(
localization_language_index_ == 0 ? "保存失败,请重试"
: localization_language_index_ == 2 ? "Не удалось сохранить. Повторите попытку"
: "Could not save; try again"));
localization::Literal("保存失败,请重试", localization_language_index_)));
return false;
}
// Removing an empty group shifts indices. Retain the filter by name instead
@@ -5528,10 +5493,8 @@ void GuiApplication::GalleryAction(const std::string& action,
ui_->gallery_network_synced = {};
return;
}
const auto tr = [this](const char* zh, const char* en, const char* ru) {
return UiText(localization_language_index_ == 0 ? zh
: localization_language_index_ == 2 ? ru
: en);
const auto tr = [this](const char* zh) {
return UiText(localization::Literal(zh, localization_language_index_).c_str());
};
if (action == "recent-open") {
ReleaseControl();
@@ -5749,7 +5712,7 @@ void GuiApplication::GalleryAction(const std::string& action,
SyncGallery();
};
if (action == "file") {
OpenFileDialog(std::string(tr("选择分发文件", "Select a file to send", "Выберите файл для отправки")),
OpenFileDialog(std::string(tr("选择分发文件")),
[enqueue](std::string file) {
if (!file.empty()) enqueue(file);
});
@@ -6026,11 +5989,8 @@ void GuiApplication::SyncGallery() {
ui_->wall->set_gallery_recent_available(available);
ui_->wall->set_gallery_recent_selected(selected);
}
const auto tr = [this](const char* zh, const char* en,
const char* ru) -> std::string {
return localization_language_index_ == 0 ? zh
: localization_language_index_ == 2 ? ru
: en;
const auto tr = [this](const char* zh) -> std::string {
return localization::Literal(zh, localization_language_index_).c_str();
};
const auto name = [this](const std::string& id) {
for (const auto& [_, recent] : recent_connections_)
@@ -6070,10 +6030,10 @@ void GuiApplication::SyncGallery() {
device.id = UiText(FormatPeerId(id.c_str()));
const auto mode = props->traversal_mode_.load();
device.mode = UiText(mode == TraversalMode::P2P
? tr("直连", "Direct", "Прямой")
? tr("直连")
: mode == TraversalMode::UnknownMode
? tr("等待统计", "Awaiting stats", "Ожидание статистики")
: tr("中继", "Relay", "Ретрансляция"));
? tr("等待统计")
: tr("中继"));
device.inbound = device.outbound = device.loss = "—";
device.video_latency = "—";
// Prefer the wall's own frame path. An independently displayed stream
@@ -6241,7 +6201,7 @@ void GuiApplication::SyncGallery() {
label += member.group;
}
std::vector<slint::SharedString> groups{
UiText(tr("全部设备", "All devices", "Все устройства"))};
UiText(tr("全部设备"))};
for (const auto& group : wall.groups) groups.push_back(UiText(group));
const auto old_groups = ui_->wall->get_gallery_groups();
bool changed_groups = !old_groups || old_groups->row_count() != groups.size();
@@ -6325,8 +6285,8 @@ void GuiApplication::SyncGallery() {
tile.online = tile.connected || online.value_or(false);
tile.presence_known = tile.connected || online.has_value();
tile.status = UiText(
tile.connected ? tr("等待画面", "Waiting for video", "Ожидание видео")
: tr("未连接", "Not connected", "Нет соединения"));
tile.connected ? tr("等待画面")
: tr("未连接"));
const bool queued = !tile.connected && std::any_of(
ui_->batch_operations.begin(), ui_->batch_operations.end(),
[&](const auto& operation) {
@@ -6335,9 +6295,9 @@ void GuiApplication::SyncGallery() {
});
// A queued retry takes precedence over errors from the previous session.
if (queued)
tile.status = UiText(tr("队列中", "Queued", "В очереди"));
tile.status = UiText(tr("队列中"));
else if (props && props->connection_auth_.Get().password_required)
tile.status = UiText(tr("需要密码", "Password required", "Нужен пароль"));
tile.status = UiText(tr("需要密码"));
else if (props && !tile.connected)
tile.status = UiText(ConnectionStatusText(
props->connection_status_.load(), localization_language_index_));
@@ -6365,8 +6325,8 @@ void GuiApplication::SyncGallery() {
tile.has_frame = true;
tile.stale = now - props->gallery_frame_at_ > 5s;
tile.status =
UiText(tile.stale ? tr("画面已停留", "Frame stale", "Кадр устарел")
: tr("只读预览", "View only", "Просмотр"));
UiText(tile.stale ? tr("画面已停留")
: tr("只读预览"));
}
}
return tile;
@@ -1,116 +1,91 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LOCALIZATION_H_
#define _LOCALIZATION_H_
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LOCALIZATION_H_
#define _LOCALIZATION_H_
#include <cstddef>
#include <string>
#include <unordered_map>
#include <vector>
#include "localization_data.h"
#include "system_language.h"
#if _WIN32
#include <Windows.h>
#endif
namespace crossdesk {
namespace localization {
namespace crossdesk {
namespace localization {
struct LanguageOption {
std::string code;
std::string display_name;
};
class LocalizedString {
public:
constexpr explicit LocalizedString(const char* key) : key_(key) {}
const std::string& operator[](int language_index) const;
private:
const char* key_;
};
inline const std::vector<LanguageOption>& GetSupportedLanguages() {
static const std::vector<LanguageOption> kSupportedLanguages = {
{"zh-CN", reinterpret_cast<const char*>(u8"中文")},
{"en-US", "English"},
{"ru-RU", reinterpret_cast<const char*>(u8"Русский")}};
return kSupportedLanguages;
}
public:
constexpr explicit LocalizedString(const char* key) : key_(key) {}
const std::string& operator[](int language_index) const;
private:
const char* key_;
};
inline const std::vector<LanguageOption>& GetSupportedLanguages() {
static const std::vector<LanguageOption> kSupportedLanguages = {
{"zh-CN", "简体中文"}, {"en-US", "English"}, {"ru-RU", "Русский"},
{"zh-Hant", "繁體中文"}, {"ja", "日本語"}, {"ko", "한국어"},
{"de", "Deutsch"}, {"fr", "Français"}, {"es", "Español"},
{"pt-BR", "Português (Brasil)"}};
return kSupportedLanguages;
}
namespace detail {
inline int ClampLanguageIndex(int language_index) {
if (language_index >= 0 &&
language_index < static_cast<int>(GetSupportedLanguages().size())) {
return language_index;
}
return 0;
if (language_index == kSystemLanguage) return SystemLanguage();
return language_index >= 0 && language_index < kSystemLanguage
? language_index : kEnglishLanguage;
}
using TranslationTable =
std::unordered_map<std::string,
std::unordered_map<std::string, std::string>>;
inline std::unordered_map<std::string, std::string> MakeLocalizedValues(
const TranslationRow& row) {
return {{"zh-CN", reinterpret_cast<const char*>(row.zh)},
{"en-US", reinterpret_cast<const char*>(row.en)},
{"ru-RU", reinterpret_cast<const char*>(row.ru)}};
}
inline TranslationTable BuildTranslationTable() {
TranslationTable table;
for (const auto& row : kTranslationRows) {
table[row.key] = MakeLocalizedValues(row);
}
using TranslationTable = std::unordered_map<std::string, std::vector<std::string>>;
inline const TranslationTable& LiteralTable() {
static const TranslationTable table = [] {
TranslationTable result;
for (const auto& row : kTranslationRows)
result.emplace(row.key, std::vector<std::string>(std::begin(row.text), std::end(row.text)));
return result;
}();
return table;
}
inline const TranslationTable& GetTranslationTable() {
static const TranslationTable table = BuildTranslationTable();
return table;
}
inline const std::string& GetTranslatedText(const std::string& key,
int language_index) {
static const std::string kEmptyText = "";
const auto& table = GetTranslationTable();
const auto key_it = table.find(key);
if (key_it == table.end()) {
return kEmptyText;
}
const auto& localized_values = key_it->second;
const std::string& language_code =
GetSupportedLanguages()[ClampLanguageIndex(language_index)].code;
const auto exact_it = localized_values.find(language_code);
if (exact_it != localized_values.end()) {
return exact_it->second;
}
const auto english_it = localized_values.find("en-US");
if (english_it != localized_values.end()) {
return english_it->second;
}
const auto chinese_it = localized_values.find("zh-CN");
if (chinese_it != localized_values.end()) {
return chinese_it->second;
}
return kEmptyText;
}
} // namespace detail
inline const std::string& GetLiteralText(const std::string& key, int language) {
static const std::string empty;
const auto it = LiteralTable().find(key);
if (it == LiteralTable().end()) return empty;
const auto& values = it->second;
return values[ClampLanguageIndex(language)];
}
inline const std::string& GetTranslatedText(const std::string& key, int language) {
static const std::unordered_map<std::string, std::string> names = [] {
std::unordered_map<std::string, std::string> result;
for (const auto& row : kNamedTranslations) result.emplace(row.name, row.literal);
return result;
}();
static const std::string empty;
const auto it = names.find(key);
return it == names.end() ? empty : GetLiteralText(it->second, language);
}
} // namespace detail
inline const std::string& Literal(const std::string& key, int language) {
return detail::GetLiteralText(key, language);
}
inline const std::string& LocalizedString::operator[](
int language_index) const {
return detail::GetTranslatedText(key_, language_index);
@@ -130,11 +105,11 @@ inline std::string FormatDisplayLabel(size_t display_index,
return label;
}
#define CROSSDESK_DECLARE_LOCALIZED_STRING(name, zh, en, ru) \
inline const LocalizedString name(#name);
CROSSDESK_LOCALIZATION_ALL(CROSSDESK_DECLARE_LOCALIZED_STRING)
#undef CROSSDESK_DECLARE_LOCALIZED_STRING
#define CROSSDESK_DECLARE_LOCALIZED_STRING(name) \
inline const LocalizedString name(#name);
CROSSDESK_LOCALIZATION_ALL(CROSSDESK_DECLARE_LOCALIZED_STRING)
#undef CROSSDESK_DECLARE_LOCALIZED_STRING
#if _WIN32
inline const wchar_t* GetWindowsLocalizedLabel(
int language_index, const char* key, const wchar_t* fallback,
@@ -187,8 +162,8 @@ inline const wchar_t* GetExitProgramLabel(int language_index) {
cache);
}
#endif
} // namespace localization
} // namespace crossdesk
#endif
} // namespace localization
} // namespace crossdesk
#endif
File diff suppressed because one or more lines are too long
@@ -0,0 +1,111 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-10-27
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _SYSTEM_LANGUAGE_H_
#define _SYSTEM_LANGUAGE_H_
#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>
#if defined(_WIN32)
#include <Windows.h>
#elif defined(__APPLE__)
#include <CoreFoundation/CoreFoundation.h>
#endif
namespace crossdesk {
namespace localization {
inline constexpr int kSystemLanguage = 10;
inline constexpr int kEnglishLanguage = 1;
// Keep 0/1/2 compatible with existing Chinese/English/Russian config files.
inline int MatchLanguage(std::string tag) {
for (char& c : tag) {
if (c == '_') c = '-';
else if (c >= 'A' && c <= 'Z') c += 'a' - 'A';
}
tag = tag.substr(0, tag.find_first_of(".@"));
const auto base = tag.substr(0, tag.find('-'));
if (base == "zh") {
const auto parts = "-" + tag + "-";
if (parts.find("-hans-") != std::string::npos) return 0;
return parts.find("-hant-") != std::string::npos ||
parts.find("-tw-") != std::string::npos ||
parts.find("-hk-") != std::string::npos ||
parts.find("-mo-") != std::string::npos ? 3 : 0;
}
const char* codes[] = {"zh", "en", "ru", "zh", "ja", "ko", "de", "fr", "es", "pt"};
for (int i = 1; i < kSystemLanguage; ++i)
if (i != 3 && base == codes[i]) return i;
if (base == "c" || base == "posix") return kEnglishLanguage;
return -1;
}
inline int MatchPreferredLanguages(const std::vector<std::string>& tags) {
for (const auto& tag : tags) {
const int match = MatchLanguage(tag);
if (match >= 0) return match;
}
return kEnglishLanguage;
}
inline int DetectSystemLanguage() {
std::vector<std::string> tags;
#if defined(_WIN32)
ULONG count = 0, length = 0;
GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, nullptr, &length);
std::vector<wchar_t> names(length);
if (length && GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &count, names.data(), &length)) {
for (const wchar_t* name = names.data(); *name; name += wcslen(name) + 1) {
std::string tag;
for (const wchar_t* c = name; *c; ++c) tag += static_cast<char>(*c);
tags.push_back(tag);
}
}
#elif defined(__APPLE__)
if (CFArrayRef names = CFLocaleCopyPreferredLanguages()) {
for (CFIndex i = 0; i < CFArrayGetCount(names); ++i) {
char tag[128] = {};
auto name = static_cast<CFStringRef>(CFArrayGetValueAtIndex(names, i));
if (CFStringGetCString(name, tag, sizeof(tag), kCFStringEncodingUTF8)) tags.emplace_back(tag);
}
CFRelease(names);
}
#else
// The Harmony desktop host supplies LANGUAGE from the system i18n API.
const char* locale = std::getenv("LC_ALL");
if (!locale || !*locale) locale = std::getenv("LC_MESSAGES");
if (!locale || !*locale) locale = std::getenv("LANG");
if (locale && (std::string(locale) == "C" || std::string(locale) == "POSIX")) return kEnglishLanguage;
if (const char* preferred = std::getenv("LANGUAGE")) {
std::string list(preferred);
size_t start = 0;
do {
const auto end = list.find(':', start);
tags.push_back(list.substr(start, end - start));
if (end == std::string::npos) break;
start = end + 1;
} while (start < list.size());
}
if (locale) tags.emplace_back(locale);
#endif
return MatchPreferredLanguages(tags);
}
inline int SystemLanguage() {
// Language changes take effect on the next application launch; explicit
// application preferences always win over this process-wide default.
static const int language = DetectSystemLanguage();
return language;
}
} // namespace localization
} // namespace crossdesk
#endif
@@ -461,8 +461,7 @@ int SettingsManager::Load() {
std::make_shared<Thumbnail>(owner_.cache_path_ + "/thumbnails/",
owner_.aes128_key_, owner_.aes128_iv_);
owner_.language_button_value_ = localization::detail::ClampLanguageIndex(
static_cast<int>(owner_.config_center_->GetLanguage()));
owner_.language_button_value_ = static_cast<int>(owner_.config_center_->GetLanguage());
owner_.video_encode_format_button_value_ =
static_cast<int>(owner_.config_center_->GetVideoEncodeFormat());
owner_.enable_self_hosted_ = owner_.config_center_->IsSelfHosted();
+15 -15
View File
@@ -19,12 +19,12 @@ export struct AboutComponent {
}
export global AboutStrings {
out property <string> software-license: GalleryStrings.tr("软件许可", "Software license", "Лицензия программы");
out property <string> components: GalleryStrings.tr("开源组件", "Open-source components", "Компоненты с открытым кодом");
out property <string> source: GalleryStrings.tr("源码与构建说明", "Source code and build instructions", "Исходный код и сборка");
out property <string> notices: GalleryStrings.tr("许可与版权声明", "Licenses and copyright notices", "Лицензии и авторские права");
out property <string> view-source: GalleryStrings.tr("查看源码", "View source code", "Открыть исходный код");
out property <string> back: GalleryStrings.tr("返回", "Back", "Назад");
out property <string> software-license: GalleryStrings.texts[130];
out property <string> components: GalleryStrings.texts[131];
out property <string> source: GalleryStrings.texts[132];
out property <string> notices: GalleryStrings.texts[133];
out property <string> view-source: GalleryStrings.texts[134];
out property <string> back: GalleryStrings.texts[135];
}
export component AboutNavigationGroup inherits FlatSurface {
@@ -137,7 +137,7 @@ export component AboutPanel inherits SettingsDialog {
if root.page == AboutPage.components: VerticalLayout {
padding: 0px; spacing: 10px;
Text {
text: GalleryStrings.tr("以下列出本版本的主要开源组件。各组件遵循各自的开源许可。", "Main open-source components used in this build. Each component is covered by its own license.", "Основные открытые компоненты этой сборки. Каждый распространяется по своей лицензии.");
text: GalleryStrings.texts[136];
color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap;
}
Rectangle {
@@ -146,9 +146,9 @@ export component AboutPanel inherits SettingsDialog {
notice := VerticalLayout {
x: 16px; y: 16px; width: parent.width - 32px;
height: self.preferred-height; spacing: 12px;
Text { text: GalleryStrings.tr("OpenFEC 告知", "OpenFEC notice", "Уведомление OpenFEC"); color: WorkbenchStyle.text; font-size: 14px; font-weight: 600; }
Text { text: GalleryStrings.texts[137]; color: WorkbenchStyle.text; font-size: 14px; font-weight: 600; }
Text {
text: GalleryStrings.tr("本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。", "This application uses OpenFEC for forward error correction under CeCILL-C 1.0, which provides limited warranties and limits liability. See the full license for details.", "Приложение использует OpenFEC для прямого исправления ошибок по лицензии CeCILL-C 1.0 с ограниченной гарантией и ответственностью. Подробности — в тексте лицензии.");
text: GalleryStrings.texts[138];
color: WorkbenchStyle.muted; font-size: 13px; wrap: word-wrap;
font-family: "CrossDesk Message Spacing";
}
@@ -158,7 +158,7 @@ export component AboutPanel inherits SettingsDialog {
font-family: "CrossDesk Message Spacing";
}
if root.openfec-index >= 0: AboutNavigationRow {
title: GalleryStrings.tr("OpenFEC 许可与源码", "OpenFEC license and source", "Лицензия и исходный код OpenFEC");
title: GalleryStrings.texts[139];
clicked => { root.component-index = root.openfec-index; root.page = AboutPage.component; }
}
}
@@ -185,24 +185,24 @@ export component AboutPanel inherits SettingsDialog {
clicked => { root.document-index = index; root.page = AboutPage.document; }
}
if root.selected.documents.length == 0: Text {
text: GalleryStrings.tr("本组件未内置许可文本,请通过“查看源码”查阅许可与版权声明。", "License text is not bundled for this component. Use View source code to read its license and copyright notices.", "Текст лицензии не включён. Откройте исходный код для просмотра лицензии и авторских прав.");
text: GalleryStrings.texts[140];
color: WorkbenchStyle.muted; font-size: 12px; wrap: word-wrap;
}
}
if root.page == AboutPage.source: VerticalLayout {
padding: 0px; spacing: 12px;
Text {
text: root.revision != "" ? GalleryStrings.tr("源码版本:", "Source revision: ", "Ревизия исходного кода: ") + root.revision
: GalleryStrings.tr("本版本源码信息暂不可用。", "Source revision is unavailable for this build.", "Ревизия исходного кода этой сборки недоступна.");
text: root.revision != "" ? GalleryStrings.texts[141] + root.revision
: GalleryStrings.texts[142];
color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap;
}
AboutNavigationRow { title: AboutStrings.view-source; external: true; clicked => { root.open-link(root.source-url); } }
AboutNavigationRow {
title: GalleryStrings.tr("构建说明", "Build instructions", "Инструкции по сборке"); external: true;
title: GalleryStrings.texts[143]; external: true;
clicked => { root.open-link(root.build-url); }
}
AboutNavigationRow {
title: GalleryStrings.tr("项目主页", "Project website", "Сайт проекта"); external: true;
title: GalleryStrings.texts[144]; external: true;
clicked => { root.open-link("https://crossdesk.cn"); }
}
}
@@ -35,12 +35,12 @@ export component DeviceCardActions inherits Rectangle {
spacing: 0px;
CardAction {
icon: FontAwesomeIcons.pen;
tooltip: GalleryStrings.tr("修改名称", "Rename", "Переименовать");
tooltip: GalleryStrings.texts[35];
clicked => { root.edit-alias(); }
}
CardAction {
icon: FontAwesomeIcons.tags;
tooltip: GalleryStrings.tr("修改分组", "Edit groups", "Изменить группы");
tooltip: GalleryStrings.texts[30];
clicked => { root.edit-groups(); }
}
CardAction {
@@ -51,7 +51,7 @@ export component DeviceCardActions inherits Rectangle {
CardAction {
icon: root.connected ? FontAwesomeIcons.xmark : FontAwesomeIcons.arrow-right-long;
icon-size: 13px;
tooltip: root.connected ? GalleryStrings.tr("断开", "Disconnect", "Отключить") : GalleryStrings.tr("连接", "Connect", "Подключить");
tooltip: root.connected ? GalleryStrings.texts[36] : GalleryStrings.texts[37];
clicked => {
if root.connected { root.disconnect-device(); }
else { root.connect-device(); }
@@ -31,7 +31,7 @@ export component DeviceGroupDialog inherits Rectangle {
TouchArea {}
Text {
x: 18px; y: 16px; height: 24px;
text: GalleryStrings.tr("修改分组", "Edit groups", "Изменить группы");
text: GalleryStrings.texts[30];
color: #24272d; font-size: 17px; font-weight: 600;
}
Text {
@@ -64,7 +64,7 @@ export component DeviceGroupDialog inherits Rectangle {
}
Text {
x: 39px; width: parent.width - 48px; height: parent.height;
text: option.name == "" ? GalleryStrings.tr("未分组", "Ungrouped", "Без группы") : option.name;
text: option.name == "" ? GalleryStrings.texts[31] : option.name;
color: parent.selected ? ControlStyle.selected-text : #30343b;
font-weight: parent.selected ? 600 : 400;
font-size: 12px; overflow: elide; vertical-alignment: center;
@@ -91,7 +91,7 @@ export component DeviceGroupDialog inherits Rectangle {
x: 18px; y: group-list.y + group-list.height + 12px;
width: parent.width - 36px; height: 30px;
text <=> root.new-group;
placeholder-text: GalleryStrings.tr("新建分组(可选)", "New group (optional)", "Новая группа (необязательно)");
placeholder-text: GalleryStrings.texts[32];
}
if root.error != "": Text {
x: 18px; y: group-list.y + group-list.height + 48px;
@@ -102,8 +102,8 @@ export component DeviceGroupDialog inherits Rectangle {
x: 18px; y: parent.height - 46px; width: parent.width - 36px; height: 28px;
spacing: 10px;
Rectangle { horizontal-stretch: 1; }
CompactButton { width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.cancel(); } }
CompactButton { width: 88px; height: 28px; primary: true; text: GalleryStrings.tr("保存", "Save", "Сохранить"); clicked => { root.save(root.new-group); } }
CompactButton { width: 72px; height: 28px; text: GalleryStrings.texts[33]; clicked => { root.cancel(); } }
CompactButton { width: 88px; height: 28px; primary: true; text: GalleryStrings.texts[34]; clicked => { root.save(root.new-group); } }
}
}
}
@@ -1,10 +1,24 @@
export global GalleryStrings {
in-out property <int> language: 0;
in-out property <[string]> texts;
in-out property <string> retry-prefix;
in-out property <string> retry-suffix;
public pure function retry-after(seconds: int) -> string {
return self.retry-prefix + seconds + self.retry-suffix;
}
// Compatibility for embedding callers with their own three-language text.
public pure function tr(zh: string, en: string, ru: string) -> string {
return self.language == 0 ? zh : self.language == 2 ? ru : en;
}
public pure function device-count(count: int) -> string {
if self.language == 0 { return count + " 台设备"; }
if self.language == 3 { return count + " 臺裝置"; }
if self.language == 4 { return count + " 台"; }
if self.language == 5 { return count + "개 기기"; }
if self.language == 6 { return count + (count == 1 ? " Gerät" : " Geräte"); }
if self.language == 7 { return count + (count == 1 ? " appareil" : " appareils"); }
if self.language == 8 { return count + (count == 1 ? " dispositivo" : " dispositivos"); }
if self.language == 9 { return count + (count == 1 ? " dispositivo" : " dispositivos"); }
if self.language == 2 {
let last-two = mod(count, 100);
let last = mod(count, 10);
@@ -65,10 +65,10 @@ component NetworkRow inherits Rectangle {
encryption-text := Text {
x: 6px; width: parent.width - 12px; height: parent.height;
text: !root.device.encryption-known
? GalleryStrings.tr("待确认", "Unknown", "Неизвестно")
? GalleryStrings.texts[38]
: root.device.encryption-enabled
? GalleryStrings.tr("已加密", "Encrypted", "Зашифровано")
: GalleryStrings.tr("未加密", "Unencrypted", "Без шифр.");
? GalleryStrings.texts[39]
: GalleryStrings.texts[40];
color: !root.device.encryption-known ? #7a8490
: root.device.encryption-enabled ? #18794e : #8a6428;
font-size: 10px; horizontal-alignment: center;
@@ -83,7 +83,7 @@ component NetworkRow inherits Rectangle {
Text {
y: root.heading ? 0px : 8px; height: root.heading ? parent.height : 20px;
width: parent.width;
text: root.heading ? GalleryStrings.tr("画面", "Video", "Видео") : root.device.resolution;
text: root.heading ? GalleryStrings.texts[41] : root.device.resolution;
font-size: root.heading ? 11px : 12px; font-weight: root.heading ? 600 : 400;
color: root.heading ? #6b7785 : #293746;
vertical-alignment: center; overflow: elide;
@@ -98,7 +98,7 @@ component NetworkRow inherits Rectangle {
width: max(100px, root.width * 0.18);
if root.heading: Text {
x: 0px; width: parent.width;
text: GalleryStrings.tr("收发速率", "Transfer rate", "Скорость");
text: GalleryStrings.texts[42];
font-size: 11px; font-weight: 600; color: #6b7785;
vertical-alignment: center; overflow: elide;
}
@@ -126,14 +126,14 @@ component NetworkRow inherits Rectangle {
Text {
y: root.heading ? 0px : 8px; height: root.heading ? parent.height : 20px;
width: parent.width;
text: root.heading ? GalleryStrings.tr("视频延时", "Video latency", "Задержка видео") : root.device.video-latency;
text: root.heading ? GalleryStrings.texts[43] : root.device.video-latency;
font-size: root.heading ? 11px : 12px; font-weight: root.heading ? 600 : 400;
color: root.heading ? #6b7785 : #293746;
vertical-alignment: center; overflow: elide;
}
if !root.heading: Text {
y: 30px; height: 17px; width: parent.width;
text: GalleryStrings.tr("丢包 ", "Loss ", "Потери ") + root.device.loss;
text: GalleryStrings.texts[44] + root.device.loss;
font-size: 11px; color: #7a8490;
vertical-alignment: center; overflow: elide;
}
@@ -155,7 +155,7 @@ component NetworkRow inherits Rectangle {
width: (parent.width - 12px) / 2;
encoder-label := Text {
height: parent.height; width: self.preferred-width;
text: GalleryStrings.tr("被控端编码器 ", "Remote encoder ", "Удалённый кодер ");
text: GalleryStrings.texts[45];
font-size: 10px; color: #586f87; vertical-alignment: center;
}
ScrollingText {
@@ -167,7 +167,7 @@ component NetworkRow inherits Rectangle {
width: (parent.width - 12px) / 2;
decoder-label := Text {
height: parent.height; width: self.preferred-width;
text: GalleryStrings.tr("控制端解码器 ", "Local decoder ", "Локальный декодер ");
text: GalleryStrings.texts[46];
font-size: 10px; color: #586f87; vertical-alignment: center;
}
ScrollingText {
@@ -182,11 +182,11 @@ component NetworkEmptyState inherits Rectangle {
VerticalLayout {
alignment: center; spacing: 8px;
Text {
text: GalleryStrings.tr("暂无已连接设备", "No connected devices", "Нет подключённых устройств");
text: GalleryStrings.texts[47];
font-size: 13px; color: #596878; horizontal-alignment: center;
}
Text {
text: GalleryStrings.tr("建立连接后,网络和画面信息将在此显示", "Network and video details appear here once connected", "После подключения здесь появятся данные сети и видео");
text: GalleryStrings.texts[48];
font-size: 11px; color: #8a95a1; horizontal-alignment: center;
}
}
@@ -216,25 +216,25 @@ export component GalleryNetworkDialog inherits Rectangle {
}
Text {
x: 20px; y: 17px; width: parent.width - 120px; height: 24px;
text: GalleryStrings.tr("设备网络状态", "Device network status", "Состояние сети устройств");
text: GalleryStrings.texts[49];
font-size: 17px; font-weight: 600; color: #293746;
}
CompactButton {
border-radius: UiRadius.control;
x: parent.width - 80px; y: 17px; width: 60px; height: 26px;
text: GalleryStrings.tr("关闭", "Close", "Закрыть"); clicked => { root.close(); }
text: GalleryStrings.texts[50]; clicked => { root.close(); }
}
Text {
x: 20px; y: 48px; width: parent.width - 40px; height: 18px;
text: GalleryStrings.tr("已连接设备 ", "Connected devices ", "Подключённые устройства ") + root.devices.length
+ GalleryStrings.tr(" 台", "", "");
text: GalleryStrings.texts[51] + root.devices.length
+ GalleryStrings.texts[52];
font-size: 11px; color: #7a8490; overflow: elide;
}
NetworkRow {
x: 12px; y: 78px; width: parent.width - 38px; height: 30px;
heading: true; background: #f3f5f8; border-radius: UiRadius.control;
device: {
name: GalleryStrings.tr("设备 / ID", "Device / ID", "Устройство / ID"),
name: GalleryStrings.texts[53],
};
}
list := ScrollView {
@@ -9,31 +9,31 @@ export component GallerySettings inherits Rectangle {
in property <string> error;
SettingsGroup {
y: 0px; width: parent.width;
title: GalleryStrings.tr("画面传输", "Video transmission", "Передача видео"); rows: 3;
title: GalleryStrings.texts[145]; rows: 3;
SettingsRow {
label: GalleryStrings.tr("分辨率上限", "Resolution limit", "Макс. разрешение");
label: GalleryStrings.texts[146];
label-reserve: self.width - 112px;
SettingsSelect {
border-radius: UiRadius.control;
x: 136px; y: 4px; width: parent.width - 150px;
current-index <=> root.resolution-index;
model: [GalleryStrings.tr("自动", "Auto", "Авто"),
model: [GalleryStrings.texts[147],
"320 × 180", "640 × 360", "1280 × 720", "1920 × 1080", "2560 × 1440", "3840 × 2160",
GalleryStrings.tr("原始分辨率", "Original resolution", "Исходное разрешение")];
GalleryStrings.texts[148]];
}
}
SettingsRow {
label: GalleryStrings.tr("帧率", "Frame rate", "Частота кадров");
label: GalleryStrings.texts[149];
label-reserve: self.width - 112px;
SettingsSelect {
border-radius: UiRadius.control;
x: 136px; y: 4px; width: parent.width - 150px;
current-index <=> root.frame-rate-index;
model: [GalleryStrings.tr("跟随设备设置", "Follow device settings", "Настройки устройства"), "15 FPS", "30 FPS", "60 FPS"];
model: [GalleryStrings.texts[150], "15 FPS", "30 FPS", "60 FPS"];
}
}
SettingsRow {
label: GalleryStrings.tr("视频总带宽", "Total bandwidth", "Общий битрейт");
label: GalleryStrings.texts[151];
label-reserve: self.width - 112px; divider: false;
SettingsSelect {
border-radius: UiRadius.control;
+69 -69
View File
@@ -156,7 +156,7 @@ export component GalleryView inherits Rectangle {
in property <int> count: 0;
in property <int> connected-count: 0;
private property <string> connected-summary: root.connected-count + " / " + root.count
+ GalleryStrings.tr(" 台已连接", " connected", " подключено");
+ GalleryStrings.texts[152];
callback network-open;
in property <int> selected-count: 0;
in property <bool> selected-audio-available: false;
@@ -297,10 +297,10 @@ export component GalleryView inherits Rectangle {
add-menu := GalleryMenuButton {
horizontal-padding: root.toolbar-button-padding;
width: self.preferred-button-width;
text: GalleryStrings.tr("添加", "Add", "Добавить"); menu-width: 232px;
text: GalleryStrings.texts[153]; menu-width: 232px;
items: [
{label: GalleryStrings.tr("通过设备 ID 添加", "Add by device ID", "Добавить по ID устройства"), enabled: true},
{label: GalleryStrings.tr("从近期连接添加", "Add from recent", "Добавить из недавних"), enabled: true},
{label: GalleryStrings.texts[154], enabled: true},
{label: GalleryStrings.texts[155], enabled: true},
];
opened => { root.device-tooltip-id = ""; root.action("release", ""); }
selected(index) => {
@@ -316,12 +316,12 @@ export component GalleryView inherits Rectangle {
horizontal-stretch: 1; min-width: 0px;
Text {
x: 0px; y: 0px; width: parent.width; height: root.hidden-selected > 0 ? 13px : parent.height;
text: GalleryStrings.tr("已选 ", "Selected ", "Выбрано ") + root.selected-count + GalleryStrings.tr(" 台", "", "");
text: GalleryStrings.texts[156] + root.selected-count + GalleryStrings.texts[52];
color: #62666d; vertical-alignment: center; font-size: 11px; overflow: elide;
}
if root.hidden-selected > 0: Text {
y: 14px; width: parent.width; height: 10px;
text: GalleryStrings.tr("跨页 ", "Other pages ", "Вне страницы ") + root.hidden-selected;
text: GalleryStrings.texts[157] + root.hidden-selected;
color: #80858d; font-size: 10px; overflow: elide;
}
}
@@ -329,27 +329,27 @@ export component GalleryView inherits Rectangle {
horizontal-padding: root.toolbar-button-padding;
enabled: root.count > 0;
text: root.count > 0 && root.selected-count == root.count
? GalleryStrings.tr("取消全选", "Deselect all", "Снять выбор")
: GalleryStrings.tr("全选", "Select all", "Выбрать все");
? GalleryStrings.texts[158]
: GalleryStrings.texts[159];
alternate-text: root.count > 0 && root.selected-count == root.count
? GalleryStrings.tr("全选", "Select all", "Выбрать все")
: GalleryStrings.tr("取消全选", "Deselect all", "Снять выбор");
? GalleryStrings.texts[159]
: GalleryStrings.texts[158];
clicked => { root.action("all-select", ""); }
}
GalleryToolbarButton { horizontal-padding: root.toolbar-button-padding; primary: true; enabled: root.selected-count > 0; text: GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action("connect", ""); } }
GalleryToolbarButton { horizontal-padding: root.toolbar-button-padding; enabled: root.selected-count > 0; text: GalleryStrings.tr("断开", "Disconnect", "Отключить"); clicked => { root.confirm-action("disconnect", self.text); } }
GalleryToolbarButton { horizontal-padding: root.toolbar-button-padding; primary: true; enabled: root.selected-count > 0; text: GalleryStrings.texts[37]; clicked => { root.action("connect", ""); } }
GalleryToolbarButton { horizontal-padding: root.toolbar-button-padding; enabled: root.selected-count > 0; text: GalleryStrings.texts[36]; clicked => { root.confirm-action("disconnect", self.text); } }
batch-menu := GalleryMenuButton {
horizontal-padding: root.toolbar-button-padding;
width: self.preferred-button-width; enabled: root.selected-count > 0;
text: GalleryStrings.tr("更多", "More", "Ещё");
text: GalleryStrings.texts[68];
items: [
{label: root.selected-audio-muted ? GalleryStrings.tr("取消静音", "Unmute", "Со звуком") : GalleryStrings.tr("静音", "Mute", "Без звука"), enabled: root.selected-audio-available},
{label: GalleryStrings.tr("分发文件", "Send file", "Отправить файл"), enabled: root.selected-count > 0},
{label: root.selected-audio-muted ? GalleryStrings.texts[160] : GalleryStrings.texts[161], enabled: root.selected-audio-available},
{label: GalleryStrings.texts[162], enabled: root.selected-count > 0},
];
opened => { root.device-tooltip-id = ""; root.action("release", ""); }
selected(index) => {
if index == 0 { root.action(root.selected-audio-muted ? "unmute" : "mute", ""); }
else { root.confirm-action("file", GalleryStrings.tr("分发文件", "Send file", "Отправить файл")); }
else { root.confirm-action("file", GalleryStrings.texts[162]); }
}
}
}
@@ -357,8 +357,8 @@ export component GalleryView inherits Rectangle {
x: parent.width - self.width; y: 0px;
horizontal-padding: root.toolbar-button-padding;
primary: root.batch;
alternate-text: GalleryStrings.tr("选择", "Select", "Выбрать");
text: root.batch ? GalleryStrings.tr("完成", "Done", "Готово") : GalleryStrings.tr("选择", "Select", "Выбрать");
alternate-text: GalleryStrings.texts[163];
text: root.batch ? GalleryStrings.texts[164] : GalleryStrings.texts[163];
clicked => { root.batch = !root.batch; root.action("selection", ""); }
}
}
@@ -405,7 +405,7 @@ export component GalleryView inherits Rectangle {
function show-hint() {
if self.has-hover && !self.pressed && (self.edge-x != 0 || self.edge-y != 0) {
root.device-tooltip-id = "resize-grid";
root.device-tooltip-text = GalleryStrings.tr("拖动表格分隔线,同步调整所有设备大小", "Drag a table divider to resize all devices", "Потяните за разделитель таблицы, чтобы изменить размер всех устройств");
root.device-tooltip-text = GalleryStrings.texts[165];
root.device-tooltip-x = grid.x + self.mouse-x;
root.device-tooltip-y = grid.y + self.mouse-y + 16px;
} else if root.device-tooltip-id == "resize-grid" { root.device-tooltip-id = ""; }
@@ -461,8 +461,8 @@ export component GalleryView inherits Rectangle {
else if root.drag-source != "" { root.move-device-drag(self.mouse-x, self.mouse-y); }
}
for tile[index] in root.tiles: tile-card := Rectangle {
private property <string> presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети")
: tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно");
private property <string> presence: tile.online ? GalleryStrings.texts[166]
: tile.presence-known ? GalleryStrings.texts[167] : GalleryStrings.texts[100];
private property <string> details: tile.name + "\n" + tile.host-name + "\nID: " + tile.id + "\n" + tile.group-names + "\n" + self.presence;
accessible-role: button; accessible-label: self.details;
accessible-action-default => { root.action(root.batch && !root.simplified ? "select" : "focus", tile.id); }
@@ -522,7 +522,7 @@ export component GalleryView inherits Rectangle {
changed over-actions => { if self.has-hover { self.show-details(); } }
if tile-touch.has-hover && !root.dragging && !root.resizing && !root.batch && !root.simplified: DeviceCardActions {
x: parent.width - self.width; y: parent.height - 23px;
remove-label: GalleryStrings.tr("从桌面墙移除", "Remove from DeskHub", "Убрать из DeskHub");
remove-label: GalleryStrings.texts[168];
connected: tile.connected;
edit-alias => { root.action("release", ""); root.edit-alias(tile.id, tile.name); }
edit-groups => { root.edit-groups(tile.id, tile.name); }
@@ -557,8 +557,8 @@ export component GalleryView inherits Rectangle {
}
if root.count == 0: Text {
text: root.simplified
? GalleryStrings.tr("暂无设备,退出精简模式后可添加", "No devices. Exit compact mode to add devices", "Нет устройств. Выйдите из компактного режима, чтобы добавить их")
: GalleryStrings.tr("添加设备,建立你的桌面墙", "Add devices to build your DeskHub", "Добавьте устройства в DeskHub");
? GalleryStrings.texts[169]
: GalleryStrings.texts[170];
color: #808080; horizontal-alignment: center; vertical-alignment: center;
}
}
@@ -568,7 +568,7 @@ export component GalleryView inherits Rectangle {
height: footer.y - 12px - self.y;
HorizontalLayout {
x: 0px; y: 0px; width: parent.width - root.view-controls-space; height: 24px; spacing: 8px;
GalleryButton { width: 52px; text: GalleryStrings.tr("返回", "Back", "Назад"); clicked => { root.action("focus", ""); } }
GalleryButton { width: 52px; text: GalleryStrings.texts[135]; clicked => { root.action("focus", ""); } }
Rectangle {
horizontal-stretch: 1; min-width: 0px;
Text { x: 0px; width: parent.width; height: parent.height; text: root.focused.name; color: root.focused.online ? #303943 : DeviceCardStyle.offline-text; font-size: 12px; font-weight: 600; overflow: elide; horizontal-alignment: left; vertical-alignment: center; }
@@ -579,10 +579,10 @@ export component GalleryView inherits Rectangle {
background: root.controlling ? #e7f1fd : #f0f2f5;
focus-state := Text {
x: 8px; width: parent.width - 16px; height: parent.height;
text: root.group-controlling ? GalleryStrings.tr("群控中", "Group control", "Групп. управление")
: root.controlling ? GalleryStrings.tr("正在操控", "Controlling", "Управление")
text: root.group-controlling ? GalleryStrings.texts[171]
: root.controlling ? GalleryStrings.texts[172]
: root.focused.connected && root.focused.has-frame && !root.focused.stale
? GalleryStrings.tr("只读预览", "View only", "Просмотр") : root.focused.status;
? GalleryStrings.texts[173] : root.focused.status;
font-size: 10px; color: root.controlling ? #3e77b3 : #6b7480; vertical-alignment: center; overflow: elide;
}
}
@@ -590,14 +590,14 @@ export component GalleryView inherits Rectangle {
horizontal-padding: root.toolbar-button-padding;
primary: root.group-controlling;
enabled: root.group-controlling || (root.focused.connected && root.focused.has-frame && !root.focused.stale);
text: root.group-controlling ? GalleryStrings.tr("关闭群控", "Stop group control", "Откл. групп. ввод") : GalleryStrings.tr("开启群控", "Group control", "Групп. ввод");
alternate-text: root.group-controlling ? GalleryStrings.tr("开启群控", "Group control", "Групп. ввод") : GalleryStrings.tr("关闭群控", "Stop group control", "Откл. групп. ввод");
text: root.group-controlling ? GalleryStrings.texts[174] : GalleryStrings.texts[175];
alternate-text: root.group-controlling ? GalleryStrings.texts[175] : GalleryStrings.texts[174];
accessible-role: button;
accessible-label: self.text + GalleryStrings.tr(":同步键鼠到所有已连接设备", ": mirror input to all connected devices", ": ввод на всех подключённых устройствах");
accessible-label: self.text + GalleryStrings.texts[176];
accessible-action-default => { self.clicked(); }
clicked => { root.action(root.group-controlling ? "group-release" : "group-control", root.focused.id); }
}
GalleryButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.tr("释放操控", "Release", "Отпустить") : GalleryStrings.tr("接管操控", "Control", "Управлять")) : GalleryStrings.tr("连接", "Connect", "Подключить"); clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } }
GalleryButton { width: 100px; primary: true; enabled: !root.focused.connected || root.controlling || (root.focused.has-frame && !root.focused.stale); text: root.focused.connected ? (root.controlling ? GalleryStrings.texts[177] : GalleryStrings.texts[178]) : GalleryStrings.texts[37]; clicked => { root.action(root.focused.connected ? (root.controlling ? "release" : "control") : "connect", root.focused.id); } }
}
FlatSurface {
x: 0px; y: rail.visible ? focus-divider.y + focus-divider.height : 36px;
@@ -671,8 +671,8 @@ export component GalleryView inherits Rectangle {
width: tile.id == root.focused.id ? 0px : root.rail-slot-width;
height: root.rail-card-height; visible: tile.id != root.focused.id;
rail-card := FlatSurface {
private property <string> presence: tile.online ? GalleryStrings.tr("在线", "Online", "В сети")
: tile.presence-known ? GalleryStrings.tr("离线", "Offline", "Не в сети") : GalleryStrings.tr("未知", "Unknown", "Неизвестно");
private property <string> presence: tile.online ? GalleryStrings.texts[166]
: tile.presence-known ? GalleryStrings.texts[167] : GalleryStrings.texts[100];
private property <string> details: tile.name + "\n" + tile.host-name + "\nID: " + tile.id + "\n" + tile.group-names + "\n" + self.presence;
width: root.rail-preview-width + 10px; height: root.rail-card-height; border-radius: UiRadius.surface;
background: tile.online ? #f5f6f8 : DeviceCardStyle.offline-background; outline-color: UiSurface.edge;
@@ -744,7 +744,7 @@ export component GalleryView inherits Rectangle {
function show-hint() {
if self.has-hover && !root.focus-resizing {
root.device-tooltip-id = "resize-focus";
root.device-tooltip-text = GalleryStrings.tr("上下拖动,调整缩略图与主画面大小", "Drag up or down to resize the thumbnails and main preview", "Потяните вверх или вниз, чтобы изменить размер миниатюр и основного экрана");
root.device-tooltip-text = GalleryStrings.texts[179];
root.device-tooltip-x = focus-view.x + self.mouse-x;
root.device-tooltip-y = focus-view.y + focus-divider.y + self.mouse-y + 16px;
} else if root.device-tooltip-id == "resize-focus" { root.device-tooltip-id = ""; }
@@ -788,7 +788,7 @@ export component GalleryView inherits Rectangle {
connection-count := Text {
x: 0px; width: min(self.preferred-width, parent.width - network-button.width - 12px); height: parent.height;
text: root.group-controlling
? GalleryStrings.tr("群控中 · 所有已连接设备", "Group control · All connected devices", "Групповое управление · Все подключения")
? GalleryStrings.texts[180]
: root.connected-summary;
color: #62666d; vertical-alignment: center; font-size: 11px; overflow: elide;
}
@@ -796,7 +796,7 @@ export component GalleryView inherits Rectangle {
x: connection-count.x + connection-count.width + 12px;
width: 64px; height: parent.height;
outline-color: UiSurface.edge; border-radius: UiRadius.control;
text: GalleryStrings.tr("网络状态", "Network", "Сеть");
text: GalleryStrings.texts[181];
accessible-role: button;
accessible-label: self.text;
accessible-action-default => { self.clicked(); }
@@ -817,9 +817,9 @@ export component GalleryView inherits Rectangle {
compact-toggle := GalleryToolbarButton {
horizontal-padding: root.toolbar-button-padding;
primary: root.simplified;
alternate-text: GalleryStrings.tr("退出精简", "Exit compact", "Обычный вид");
text: root.simplified ? GalleryStrings.tr("退出精简", "Exit compact", "Обычный вид")
: GalleryStrings.tr("精简模式", "Compact", "Компактно");
alternate-text: GalleryStrings.texts[182];
text: root.simplified ? GalleryStrings.texts[182]
: GalleryStrings.texts[183];
clicked => { root.toggle-simplified(); }
}
IconButton {
@@ -827,7 +827,7 @@ export component GalleryView inherits Rectangle {
border-radius: UiRadius.control;
normal-background: ControlStyle.surface;
icon: root.fullscreen ? FontAwesomeIcons.compress : FontAwesomeIcons.expand;
tooltip: root.fullscreen ? GalleryStrings.tr("退出全屏", "Exit fullscreen", "Выйти из полноэкранного режима") : GalleryStrings.tr("全屏", "Fullscreen", "На весь экран");
tooltip: root.fullscreen ? GalleryStrings.texts[184] : GalleryStrings.texts[185];
clicked => { root.toggle-fullscreen(); }
}
}
@@ -859,9 +859,9 @@ export component GalleryView inherits Rectangle {
Text { x: 80px; y: 8px; width: parent.width - 88px; height: 18px; text: root.dragged-tile.name; font-size: 12px; color: #303943; overflow: elide; }
drag-hint := Text {
x: 80px; y: 29px; width: parent.width - 88px; height: max(16px, self.preferred-height);
text: root.drag-page-direction != 0 ? GalleryStrings.tr("停留翻页,松开取消", "Hold to turn page; release to cancel", "Удерживайте для перелистывания; отпустите для отмены")
: root.drag-target >= 0 ? GalleryStrings.tr("松开鼠标交换位置", "Release to swap", "Отпустите для обмена")
: GalleryStrings.tr("拖到另一台设备上", "Drag onto another device", "Перетащите на другое устройство");
text: root.drag-page-direction != 0 ? GalleryStrings.texts[186]
: root.drag-target >= 0 ? GalleryStrings.texts[187]
: GalleryStrings.texts[188];
font-size: 10px; color: #527ca8; wrap: word-wrap;
}
}
@@ -870,7 +870,7 @@ export component GalleryView inherits Rectangle {
width: min(420px, parent.width - 32px); height: 36px; z: 10;
background: #fff2f0; outline-color: UiSurface.edge; border-radius: UiRadius.surface;
Text { x: 12px; y: 0px; width: parent.width - 24px; height: parent.height; font-size: 12px; color: #9d4339; vertical-alignment: center;
text: GalleryStrings.tr("换位未保存,请重试", "Couldn't save positions. Please try again.", "Не удалось сохранить порядок. Повторите попытку."); overflow: elide;
text: GalleryStrings.texts[189]; overflow: elide;
}
}
// Device details are rendered by MainWindow; resize hints stay local.
@@ -896,36 +896,36 @@ export component GalleryView inherits Rectangle {
root.adding = false;
}
}
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.tr("添加设备", "Add device", "Добавить устройство"); font-size: 16px; }
Text { x: 18px; y: 56px; width: 72px; height: 30px; text: GalleryStrings.tr("设备 ID", "Device ID", "ID"); vertical-alignment: center; font-size: 12px; }
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.texts[190]; font-size: 16px; }
Text { x: 18px; y: 56px; width: 72px; height: 30px; text: GalleryStrings.texts[191]; vertical-alignment: center; font-size: 12px; }
device-id := RoundedLineEdit {
x: 96px; y: 56px; width: 286px; height: 30px;
placeholder-text: GalleryStrings.tr("9 位设备 ID", "9-digit device ID", "ID из 9 цифр");
placeholder-text: GalleryStrings.texts[192];
input-type: InputType.number;
accepted => { password.focus(); }
}
Text { x: 18px; y: 100px; width: 72px; height: 30px; text: GalleryStrings.tr("密码", "Password", "Пароль"); vertical-alignment: center; font-size: 12px; }
Text { x: 18px; y: 100px; width: 72px; height: 30px; text: GalleryStrings.texts[193]; vertical-alignment: center; font-size: 12px; }
password := RoundedLineEdit {
x: 96px; y: 100px; width: 286px; height: 30px;
placeholder-text: GalleryStrings.tr("设备连接密码", "Device password", "Пароль устройства");
placeholder-text: GalleryStrings.texts[194];
input-type: InputType.password;
accepted => { group.focus(); }
}
Text { x: 18px; y: 144px; width: 72px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); vertical-alignment: center; font-size: 12px; }
Text { x: 18px; y: 144px; width: 72px; height: 30px; text: GalleryStrings.texts[101]; vertical-alignment: center; font-size: 12px; }
group := RoundedLineEdit {
x: 96px; y: 144px; width: 286px; height: 30px;
placeholder-text: GalleryStrings.tr("分组名称(可选)", "Group name (optional)", "Группа (необязательно)");
placeholder-text: GalleryStrings.texts[195];
accepted => { add-dialog.submit(); }
}
remember := ImGuiCheckBox { x: 96px; y: 188px; }
Text { x: 123px; y: 188px; height: 21px; text: GalleryStrings.tr("记住密码", "Remember password", "Запомнить пароль"); font-size: 12px; vertical-alignment: center;
Text { x: 123px; y: 188px; height: 21px; text: GalleryStrings.texts[196]; font-size: 12px; vertical-alignment: center;
TouchArea { clicked => { remember.checked = !remember.checked; } }
}
Text { x: 18px; y: 216px; width: 364px; height: 28px; text: root.notice; color: #8e4740; font-size: 11px; wrap: word-wrap; overflow: elide; }
GalleryButton { x: 136px; y: 260px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { password.text = ""; root.adding = false; } }
GalleryButton { x: 136px; y: 260px; width: 72px; height: 28px; text: GalleryStrings.texts[33]; clicked => { password.text = ""; root.adding = false; } }
GalleryButton {
x: 218px; y: 260px; width: 164px; height: 28px; primary: true;
text: GalleryStrings.tr("添加并连接", "Add and connect", "Добавить и подключить");
text: GalleryStrings.texts[197];
enabled: device-id.text != "" && password.text != "";
clicked => { add-dialog.submit(); }
}
@@ -937,12 +937,12 @@ export component GalleryView inherits Rectangle {
FlatSurface {
x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; width: 440px; height: 378px;
background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.tr("从近期连接添加", "Add from recent connections", "Добавить из недавних"); font-size: 16px; }
Text { x: 18px; y: 44px; width: 404px; height: 18px; text: GalleryStrings.tr("加入桌面墙后,可通过批量选择连接", "Add to DeskHub, then select devices to connect.", "Добавьте в DeskHub, затем выберите устройства для подключения."); color: #62666d; font-size: 11px; }
Text { x: 18px; y: 16px; height: 24px; text: GalleryStrings.texts[155]; font-size: 16px; }
Text { x: 18px; y: 44px; width: 404px; height: 18px; text: GalleryStrings.texts[198]; color: #62666d; font-size: 11px; }
ImGuiCheckBox { x: 18px; y: 74px; enabled: root.recent-available > 0; checked: root.recent-devices.length > 0 && root.recent-selected == root.recent-available; }
Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.tr("全选", "Select all", "Выбрать все"); color: root.recent-available > 0 ? #24272d : #92969e; font-size: 12px; vertical-alignment: center; }
Text { x: 45px; y: 74px; height: 21px; text: GalleryStrings.texts[159]; color: root.recent-available > 0 ? #24272d : #92969e; font-size: 12px; vertical-alignment: center; }
TouchArea { x: 18px; y: 74px; width: 144px; height: 21px; enabled: root.recent-available > 0; clicked => { root.action("recent-all", ""); } }
Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.tr("待添加 ", "To add ", "К добавлению ") + root.recent-selected + " / " + root.recent-available; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; }
Text { x: 224px; y: 74px; width: 198px; height: 21px; text: GalleryStrings.texts[199] + root.recent-selected + " / " + root.recent-available; horizontal-alignment: right; vertical-alignment: center; font-size: 12px; color: #62666d; }
FlatSurface {
x: 18px; y: 102px; width: 404px; height: 160px;
outline-color: UiSurface.edge; border-radius: UiRadius.surface; clip: true;
@@ -961,13 +961,13 @@ export component GalleryView inherits Rectangle {
Rectangle { x: parent.width - 74px; y: 19px; width: 6px; height: 6px; border-radius: 3px; background: !device.added && device.online ? #21b567 : #92969e; }
Text {
x: parent.width - 61px; y: 12px; width: 57px; height: 20px;
text: device.added ? GalleryStrings.tr("已添加", "Added", "Добавлено") : device.online ? GalleryStrings.tr("在线", "Online", "В сети") : GalleryStrings.tr("离线", "Offline", "Не в сети");
text: device.added ? GalleryStrings.texts[200] : device.online ? GalleryStrings.texts[166] : GalleryStrings.texts[167];
color: device.added ? #92969e : device.online ? #17854c : #808080; font-size: 11px; vertical-alignment: center;
}
row-touch := TouchArea { enabled: !device.added; clicked => { root.action("recent-select", device.id); } }
}
}
if root.recent-devices.length == 0: Text { text: GalleryStrings.tr("暂无近期连接", "No recent connections", "Нет недавних подключений"); color: #808080; font-size: 12px; horizontal-alignment: center; vertical-alignment: center; }
if root.recent-devices.length == 0: Text { text: GalleryStrings.texts[89]; color: #808080; font-size: 12px; horizontal-alignment: center; vertical-alignment: center; }
Rectangle {
x: -UiRadius.surface; y: -UiRadius.surface;
width: parent.width + 2 * UiRadius.surface;
@@ -981,11 +981,11 @@ export component GalleryView inherits Rectangle {
outline-color: UiSurface.edge;
}
}
Text { x: 18px; y: 274px; width: 60px; height: 30px; text: GalleryStrings.tr("分组", "Group", "Группа"); font-size: 12px; vertical-alignment: center; }
import-group := RoundedLineEdit { x: 84px; y: 274px; width: 338px; height: 30px; text: root.import-group; placeholder-text: GalleryStrings.tr("分组名称(可选)", "Group name (optional)", "Группа (необязательно)"); }
Text { x: 18px; y: 274px; width: 60px; height: 30px; text: GalleryStrings.texts[101]; font-size: 12px; vertical-alignment: center; }
import-group := RoundedLineEdit { x: 84px; y: 274px; width: 338px; height: 30px; text: root.import-group; placeholder-text: GalleryStrings.texts[195]; }
Text { x: 18px; y: 309px; width: 404px; height: 20px; text: root.notice; font-size: 11px; color: #8e4740; overflow: elide; }
GalleryButton { x: 192px; y: 336px; width: 72px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.importing = false; } }
GalleryButton { x: 274px; y: 336px; width: 148px; primary: true; text: GalleryStrings.tr("加入桌面墙", "Add to DeskHub", "В DeskHub"); enabled: root.recent-selected > 0; clicked => { if root.import-recent(import-group.text) { root.importing = false; } } }
GalleryButton { x: 192px; y: 336px; width: 72px; text: GalleryStrings.texts[33]; clicked => { root.importing = false; } }
GalleryButton { x: 274px; y: 336px; width: 148px; primary: true; text: GalleryStrings.texts[201]; enabled: root.recent-selected > 0; clicked => { if root.import-recent(import-group.text) { root.importing = false; } } }
}
}
if root.confirming: Rectangle {
@@ -996,7 +996,7 @@ export component GalleryView inherits Rectangle {
background: white; border-radius: UiRadius.surface; outline-color: UiSurface.edge;
Text {
x: 20px; y: 18px; width: parent.width - 132px; height: 24px;
text: root.action-code == "disconnect" ? GalleryStrings.tr("断开连接", "Disconnect devices", "Отключение") : root.action-name;
text: root.action-code == "disconnect" ? GalleryStrings.texts[96] : root.action-name;
color: #24272d; font-size: 16px; font-weight: 600; vertical-alignment: center;
}
Rectangle {
@@ -1021,16 +1021,16 @@ export component GalleryView inherits Rectangle {
width: GalleryStrings.language == 0 ? 68px : 104px; height: 20px; border-radius: 10px;
background: device.connected ? #eaf6ee : #f0f2f5;
Rectangle { x: 9px; y: 7px; width: 6px; height: 6px; border-radius: 3px; background: device.connected ? #27a762 : #a0a6b0; }
Text { x: 21px; width: parent.width - 27px; height: parent.height; text: device.connected ? GalleryStrings.tr("已连接", "Connected", "Подключено") : GalleryStrings.tr("未连接", "Not connected", "Нет соединения"); color: device.connected ? #348259 : #858d98; font-size: 10px; vertical-alignment: center; }
Text { x: 21px; width: parent.width - 27px; height: parent.height; text: device.connected ? GalleryStrings.texts[202] : GalleryStrings.texts[203]; color: device.connected ? #348259 : #858d98; font-size: 10px; vertical-alignment: center; }
}
if index + 1 < root.selected-devices.length: SurfaceSeparator { x: 12px; y: parent.height - 1px; width: parent.width - 24px; height: 1px; outline-color: UiSurface.separator; }
}
}
}
GalleryButton { x: parent.width - 208px; y: parent.height - 46px; width: 72px; height: 28px; text: GalleryStrings.tr("取消", "Cancel", "Отмена"); clicked => { root.confirming = false; } }
GalleryButton { x: parent.width - 208px; y: parent.height - 46px; width: 72px; height: 28px; text: GalleryStrings.texts[33]; clicked => { root.confirming = false; } }
GalleryButton {
x: parent.width - 126px; y: parent.height - 46px; width: 108px; height: 28px; primary: true;
text: root.action-code == "disconnect" ? GalleryStrings.tr("确认断开", "Disconnect", "Отключить") : GalleryStrings.tr("确认执行", "Continue", "Продолжить");
text: root.action-code == "disconnect" ? GalleryStrings.texts[204] : GalleryStrings.texts[205];
clicked => { root.confirming = false; root.action(root.action-code, ""); }
}
}
+32 -32
View File
@@ -5,24 +5,24 @@ export global LanStrings {
// Slint Text does not expose a line-height property.
public pure function access-error-lines(code: string) -> [string] {
if code == "LanPortInUse" { return [
GalleryStrings.tr("此端口已被占用,", "This port is in use.", "Порт занят."),
GalleryStrings.tr("请更换端口。", "Choose another port.", "Выберите другой порт."),
GalleryStrings.tr("原设置已保留。", "Your previous settings are unchanged.", "Прежние настройки сохранены.")
GalleryStrings.texts[0],
GalleryStrings.texts[1],
GalleryStrings.texts[2]
]; }
if code == "LanPortCheckFailed" { return [
GalleryStrings.tr("无法确认此端口是否可用,", "Could not check this port.", "Не удалось проверить порт."),
GalleryStrings.tr("请重试或检查本机防火墙。", "Retry or check the local firewall.", "Повторите попытку или проверьте брандмауэр."),
GalleryStrings.tr("原设置已保留。", "Your previous settings are unchanged.", "Прежние настройки сохранены.")
GalleryStrings.texts[3],
GalleryStrings.texts[4],
GalleryStrings.texts[2]
]; }
if code == "LanPortPermissionDenied" { return [
GalleryStrings.tr("无法使用此端口,", "Cannot use this port.", "Порт недоступен."),
GalleryStrings.tr("请选择其他端口或检查系统权限。", "Choose another port or check system permissions.", "Смените порт или проверьте разрешения."),
GalleryStrings.tr("原设置已保留。", "Your previous settings are unchanged.", "Прежние настройки сохранены.")
GalleryStrings.texts[5],
GalleryStrings.texts[6],
GalleryStrings.texts[2]
]; }
if code == "Selected LAN interface is unavailable" { return [
GalleryStrings.tr("未找到可用的局域网连接。", "No local network connection was found.", "Нет доступного подключения к LAN."),
GalleryStrings.tr("请连接 Wi-Fi 或网线,", "Connect to Wi-Fi or Ethernet,", "Подключитесь к Wi-Fi или Ethernet"),
GalleryStrings.tr("或在主界面选择本机 IP。", "or select a local IP on the main screen.", "или выберите локальный IP в главном окне.")
GalleryStrings.texts[7],
GalleryStrings.texts[8],
GalleryStrings.texts[9]
]; }
// Keep all branches the same size for Slint's C++ model generator.
return ["", "", ""];
@@ -30,29 +30,29 @@ export global LanStrings {
public pure function error-message(code: string) -> string {
let lines = LanStrings.access-error-lines(code);
if lines[0] != "" {
let separator = GalleryStrings.tr("", " ", " ");
let separator = GalleryStrings.texts[10];
return lines[0] + separator + lines[1] + separator + lines[2];
}
if code == "" { return ""; }
if code == "InvalidCredentials" { return GalleryStrings.tr("密码不正确,或对方密码已更新。请查看对方的局域网访问密码后重试。", "The password is incorrect or has changed. Check the other desktop's current LAN password and try again.", "Пароль неверен или изменён. Проверьте текущий пароль LAN другого компьютера и повторите попытку."); }
if code == "AuthenticationRateLimited" { return GalleryStrings.tr("尝试次数过多,请等待倒计时结束后重试。", "Too many attempts. Try again when the countdown ends.", "Слишком много попыток. Дождитесь окончания обратного отсчёта."); }
if code == "CredentialsChanged" { return GalleryStrings.tr("对方密码已更新,请使用新密码重试。", "The password changed. Try again with the new password.", "Пароль изменился. Повторите с новым паролем."); }
if code == "Busy" { return GalleryStrings.tr("设备正忙,请稍后重试。", "The desktop is busy. Try again shortly.", "Компьютер занят. Повторите позже."); }
if code == "LAN password must contain exactly 6 ASCII letters or digits" { return GalleryStrings.tr("访问密码必须为 6 位英文字母或数字,区分大小写。", "Use exactly 6 letters (A–Z, a–z) or digits (0–9). Case-sensitive.", "Пароль: ровно 6 латинских букв или цифр. Регистр учитывается."); }
if code == "ConnectionFailed" || code == "ConnectionTimeout" || code == "IceConnectionTimeout" || code == "Timeout" || code == "ConnectionClosed" || code == "HeartbeatTimeout" || code == "SignalSendFailed" { return GalleryStrings.tr("无法连接,请检查对方 IP、程序是否运行,以及防火墙是否放行。", "Could not connect. Check the IP, whether the other desktop is running CrossDesk, and its firewall settings.", "Не удалось подключиться. Проверьте IP, работу CrossDesk на другом компьютере и брандмауэр."); }
if code == "Enter a numeric IPv4 address or IPv4:port" || code == "Invalid LAN endpoint" { return GalleryStrings.tr("请输入有效的 IPv4,例如 192.168.1.20;指定端口可用 IPv4:端口,端口范围 1–65535。", "Enter a valid IPv4 address such as 192.168.1.20, or IPv4:port (1–65535).", "Введите IPv4, например 192.168.1.20, или IPv4:порт (1–65535)."); }
if code == "Invalid LAN port" { return GalleryStrings.tr("请输入 1–65535 之间的整数端口。", "Enter a port number from 1 to 65535.", "Введите целый номер порта от 1 до 65535."); }
if code == "LAN password is not ready" { return GalleryStrings.tr("请点击主界面密码旁的修改按钮设置密码。", "Set a password using Edit next to the password on the main screen.", "Задайте пароль кнопкой редактирования рядом с паролем в главном окне."); }
if code == "LAN connection limit reached" { return GalleryStrings.tr("IP 连接数量已达上限,请先断开部分连接后重试。", "The IP connection limit has been reached. Disconnect some sessions and try again.", "Достигнут лимит IP-подключений. Завершите часть сеансов и повторите попытку."); }
if code == "UnsupportedNetworkPath" || code == "NetworkPolicyFailed" { return GalleryStrings.tr("当前网络路径不支持 IP 连接,请检查双方的局域网连接。", "This network path does not support IP connections. Check both devices' LAN connections.", "Этот сетевой маршрут не поддерживает IP-подключения. Проверьте подключения обоих устройств к LAN."); }
if code == "IncompatibleProtocol" || code == "RequiredCryptoUnsupported" || code == "TLS 1.3 is required for LAN connections" { return GalleryStrings.tr("双方的协议或加密能力不兼容,请将双方 CrossDesk 升级至最新版本后重试。", "The protocol or encryption capabilities are incompatible. Update CrossDesk on both devices and try again.", "Протоколы или возможности шифрования несовместимы. Обновите CrossDesk на обоих устройствах и повторите попытку."); }
if code == "InvalidCertificate" || code == "FingerprintMissing" || code == "FingerprintMismatch" { return GalleryStrings.tr("无法验证对方设备的身份,请确认连接地址和对方设备后重试。", "Could not verify the remote device's identity. Check the address and the remote device before retrying.", "Не удалось проверить подлинность удалённого устройства. Проверьте адрес и устройство перед повторной попыткой."); }
if code == "DtlsHandshakeFailed" || code == "SrtpInstallFailed" || code == "SrtpKeyExportFailed" || code == "SecureTransportFailed" { return GalleryStrings.tr("无法建立加密连接,请检查双方网络并重试。", "Could not establish an encrypted connection. Check both devices' networks and try again.", "Не удалось установить зашифрованное соединение. Проверьте сеть на обоих устройствах и повторите попытку."); }
if code == "InvalidSignal" || code == "InvalidLocalSignal" || code == "Invalid LAN protocol message" { return GalleryStrings.tr("连接协商数据无效,请重试并确认双方 CrossDesk 版本兼容。", "Invalid connection negotiation data. Try again and check that both CrossDesk versions are compatible.", "Неверные данные согласования соединения. Повторите попытку и проверьте совместимость версий CrossDesk."); }
if code == "ListenerChanged" { return GalleryStrings.tr("连接地址或监听端口已更改,请确认最新地址后重试。", "The connection address or listening port has changed. Check the current address and try again.", "Адрес подключения или порт прослушивания изменился. Проверьте текущий адрес и повторите попытку."); }
if code == "PeerPreparationFailed" { return GalleryStrings.tr("无法准备远程会话,请稍后重试。", "Could not prepare the remote session. Try again shortly.", "Не удалось подготовить удалённый сеанс. Повторите позже."); }
if code == "Cannot copy text" { return GalleryStrings.tr("复制失败,请重试。", "Could not copy. Try again.", "Не удалось скопировать. Повторите попытку."); }
if code == "InvalidCredentials" { return GalleryStrings.texts[11]; }
if code == "AuthenticationRateLimited" { return GalleryStrings.texts[12]; }
if code == "CredentialsChanged" { return GalleryStrings.texts[13]; }
if code == "Busy" { return GalleryStrings.texts[14]; }
if code == "LAN password must contain exactly 6 ASCII letters or digits" { return GalleryStrings.texts[15]; }
if code == "ConnectionFailed" || code == "ConnectionTimeout" || code == "IceConnectionTimeout" || code == "Timeout" || code == "ConnectionClosed" || code == "HeartbeatTimeout" || code == "SignalSendFailed" { return GalleryStrings.texts[16]; }
if code == "Enter a numeric IPv4 address or IPv4:port" || code == "Invalid LAN endpoint" { return GalleryStrings.texts[17]; }
if code == "Invalid LAN port" { return GalleryStrings.texts[18]; }
if code == "LAN password is not ready" { return GalleryStrings.texts[19]; }
if code == "LAN connection limit reached" { return GalleryStrings.texts[20]; }
if code == "UnsupportedNetworkPath" || code == "NetworkPolicyFailed" { return GalleryStrings.texts[21]; }
if code == "IncompatibleProtocol" || code == "RequiredCryptoUnsupported" || code == "TLS 1.3 is required for LAN connections" { return GalleryStrings.texts[22]; }
if code == "InvalidCertificate" || code == "FingerprintMissing" || code == "FingerprintMismatch" { return GalleryStrings.texts[23]; }
if code == "DtlsHandshakeFailed" || code == "SrtpInstallFailed" || code == "SrtpKeyExportFailed" || code == "SecureTransportFailed" { return GalleryStrings.texts[24]; }
if code == "InvalidSignal" || code == "InvalidLocalSignal" || code == "Invalid LAN protocol message" { return GalleryStrings.texts[25]; }
if code == "ListenerChanged" { return GalleryStrings.texts[26]; }
if code == "PeerPreparationFailed" { return GalleryStrings.texts[27]; }
if code == "Cannot copy text" { return GalleryStrings.texts[28]; }
// Keep diagnostic details for unexpected system errors, with a localized explanation.
return GalleryStrings.tr("IP 连接操作失败,详情:", "IP connection operation failed. Details: ", "Ошибка операции IP-подключения. Подробности: ") + code;
return GalleryStrings.texts[29] + code;
}
}
+84 -90
View File
@@ -265,7 +265,7 @@ component PasswordInput inherits FlatSurface {
}
export component MainWindow inherits Window {
title: root.gallery-mode ? "CrossDesk " + GalleryStrings.tr("桌面墙", "DeskHub", "DeskHub") : "CrossDesk";
title: root.gallery-mode ? "CrossDesk " + GalleryStrings.texts[54] : "CrossDesk";
icon: @image-url("../../../resources/linux/crossdesk_256x256.png");
preferred-width: root.minimum-window-width;
preferred-height: root.minimum-window-height;
@@ -455,7 +455,7 @@ export component MainWindow inherits Window {
root.lan-remember-password = false;
let error = root.lan-prepare-connect(address);
root.connection-input-error = error == "Enter a numeric IPv4 address or IPv4:port"
? GalleryStrings.tr("请输入有效的 IPv4 或 IPv4:端口", "Enter a valid IPv4 or IPv4:port", "Укажите IPv4 или IPv4:порт")
? GalleryStrings.texts[55]
: LanStrings.error-message(error);
if error == "" {
root.lan-open = true;
@@ -527,8 +527,8 @@ export component MainWindow inherits Window {
private property <bool> prompt-validating: root.lan-open ? root.lan-connecting : root.connection-validating;
private property <bool> prompt-pending: root.lan-open ? root.lan-connecting : root.connection-pending;
private property <string> prompt-status-text: !root.lan-open ? root.connection-status-text
: root.lan-connecting ? GalleryStrings.tr("正在连接…", "Connecting…", "Подключение…")
: root.lan-retry-after > 0 ? GalleryStrings.tr("请在 " + root.lan-retry-after + " 秒后重试", "Try again in " + root.lan-retry-after + "s", "Повторите через " + root.lan-retry-after + " с")
: root.lan-connecting ? GalleryStrings.texts[56]
: root.lan-retry-after > 0 ? GalleryStrings.retry-after(root.lan-retry-after)
: root.lan-message == "InvalidCredentials" ? UiStrings.reinput-password
: root.lan-message == "" || root.lan-message == "AuthenticationRateLimited" ? UiStrings.input-password
: LanStrings.error-message(root.lan-message);
@@ -556,7 +556,7 @@ export component MainWindow inherits Window {
in property <[RecentConnection]> recent-connections;
out property <length> recent-preview-width: max(1px, (recent-scroll.width - 24px) / 3);
out property <length> recent-preview-height: 80px;
in property <[string]> recent-groups: [GalleryStrings.tr("全部设备", "All devices", "Все устройства"), GalleryStrings.tr("未分组", "Ungrouped", "Без группы")];
in property <[string]> recent-groups: [GalleryStrings.texts[57], GalleryStrings.texts[31]];
in-out property <int> recent-group-index: 0;
callback recent-group-selected(int);
changed recent-group-index => {
@@ -628,7 +628,8 @@ export component MainWindow inherits Window {
in property <string> portable-service-status: "";
in-out property <bool> portable-service-do-not-remind: false;
in-out property <int> language-index: 0;
in-out property <int> language-index: 10;
in property <[string]> language-options;
in property <bool> capture-method-visible: false;
in-out property <int> capture-method-index: 0;
in-out property <int> codec-index: 0;
@@ -640,11 +641,11 @@ export component MainWindow inherits Window {
in property <bool> autostart-system-managed: false;
in property <AutostartStatus> autostart-status: AutostartStatus.checking;
out property <string> autostart-status-text:
root.autostart-status == AutostartStatus.checking ? GalleryStrings.tr("正在读取…", "Checking…", "Проверка…") :
root.autostart-status == AutostartStatus.enabled ? GalleryStrings.tr("已开启", "Enabled", "Включён") :
root.autostart-status == AutostartStatus.disabled ? GalleryStrings.tr("未开启", "Disabled", "Выключен") :
root.autostart-status == AutostartStatus.unsupported ? GalleryStrings.tr("设备不支持", "Unsupported", "Не поддерживается") :
GalleryStrings.tr("读取失败", "Read failed", "Ошибка чтения");
root.autostart-status == AutostartStatus.checking ? GalleryStrings.texts[58] :
root.autostart-status == AutostartStatus.enabled ? GalleryStrings.texts[59] :
root.autostart-status == AutostartStatus.disabled ? GalleryStrings.texts[60] :
root.autostart-status == AutostartStatus.unsupported ? GalleryStrings.texts[61] :
GalleryStrings.texts[62];
in-out property <bool> autostart-guide-open: false;
changed autostart-guide-open => { if root.autostart-guide-open { root.dialog-focus-pending = true; } }
in-out property <bool> daemon-enabled: false;
@@ -770,7 +771,7 @@ export component MainWindow inherits Window {
focus-sink.focus();
}
private property <CopyFeedback> copy-feedback: idle;
private property <bool> compact-language: root.language-index == 0;
private property <bool> compact-language: GalleryStrings.language == 0 || GalleryStrings.language == 3 || GalleryStrings.language == 4 || GalleryStrings.language == 5;
private property <bool> recent-tooltip-visible: false;
private property <string> recent-tooltip-name: "";
private property <string> recent-tooltip-host: "";
@@ -1114,9 +1115,9 @@ export component MainWindow inherits Window {
width: 136px; height: 28px;
background: WorkbenchStyle.field-surface;
enabled: !root.connection-pending && !root.lan-connecting;
tooltip: root.lan-mode ? GalleryStrings.tr("切换到 ID 连接模式", "Switch to ID connection mode", "Переключить в режим ID") : GalleryStrings.tr("切换到 IP 连接模式", "Switch to IP connection mode", "Переключить в режим IP");
tooltip: root.lan-mode ? GalleryStrings.texts[63] : GalleryStrings.texts[64];
accessible-role: switch;
accessible-label: GalleryStrings.tr("IP 连接模式", "IP connection mode", "Режим IP");
accessible-label: GalleryStrings.texts[65];
accessible-checked: root.lan-mode;
z: 8;
clicked => {
@@ -1138,7 +1139,7 @@ export component MainWindow inherits Window {
y: 2px;
width: (parent.width - 6px) / 2;
height: parent.height - 4px;
text: lan ? GalleryStrings.tr("IP 连接", "IP mode", "Режим IP") : GalleryStrings.tr("ID 连接", "ID mode", "Режим ID");
text: lan ? GalleryStrings.texts[66] : GalleryStrings.texts[67];
font-size: 12px;
font-weight: 500;
color: root.lan-mode == lan ? white : WorkbenchStyle.muted;
@@ -1154,7 +1155,7 @@ export component MainWindow inherits Window {
y: connection-mode-switch.y;
width: 80px; height: 28px;
normal-background: white;
text: GalleryStrings.tr("桌面墙", "DeskHub", "DeskHub");
text: GalleryStrings.texts[54];
z: 8;
clicked => { root.open-gallery(); }
}
@@ -1164,7 +1165,7 @@ export component MainWindow inherits Window {
width: 36px; height: 28px;
normal-background: white;
icon: FontAwesomeIcons.bars;
tooltip: GalleryStrings.tr("更多", "More", "Ещё");
tooltip: GalleryStrings.texts[68];
z: 8;
clicked => { root.gallery-action("release", ""); root.menu-open = !root.menu-open; }
}
@@ -1247,16 +1248,16 @@ export component MainWindow inherits Window {
x: 0px; y: 0px;
width: (parent.width - 16px) / 2; height: parent.height;
Text { x: 16px; y: 14px; width: parent.width - 32px; height: 24px; overflow: elide; text: UiStrings.local-desktop; font-size: 16px; font-weight: 600; color: WorkbenchStyle.text; }
Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("将 IP 和密码分享给连接方", "Share your IP and password to get connected", "Передайте IP и пароль для подключения") : GalleryStrings.tr("将 ID 和密码分享给连接方", "Share your ID and password to get connected", "Передайте ID и пароль для подключения"); font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; }
local-address-label := Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("本机 IP / 端口", "Local IP / Port", "IP / Порт") : UiStrings.local-id; font-size: 12px; color: WorkbenchStyle.muted; vertical-alignment: top; }
Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.texts[69] : GalleryStrings.texts[70]; font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; }
local-address-label := Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.texts[71] : UiStrings.local-id; font-size: 12px; color: WorkbenchStyle.muted; vertical-alignment: top; }
lan-switch-ip-metrics := Text {
visible: false; accessible-role: none;
text: GalleryStrings.tr("切换 IP", "Switch IP", "Сменить IP");
text: GalleryStrings.texts[72];
font-size: 12px; font-weight: 500;
}
lan-edit-port-metrics := Text {
visible: false; accessible-role: none;
text: GalleryStrings.tr("修改端口", "Edit port", "Изменить порт");
text: GalleryStrings.texts[73];
font-size: 12px; font-weight: 500;
}
lan-switch-ip-action := WorkbenchButton {
@@ -1328,7 +1329,7 @@ export component MainWindow inherits Window {
normal-outline-color: UiSurface.edge;
hover-outline-color: UiSurface.hover;
focus-outline-color: WorkbenchStyle.accent;
accessible-label: GalleryStrings.tr("切换本机 IP", "Switch local IP", "Сменить локальный IP");
accessible-label: GalleryStrings.texts[74];
selected(index) => {
root.copy-feedback = CopyFeedback.idle;
// Save the draft for its original IP before changing
@@ -1384,7 +1385,7 @@ export component MainWindow inherits Window {
font-size: 18px * local-address-field.lan-font-scale; font-weight: 600;
input-type: InputType.number; color: WorkbenchStyle.text;
enabled: root.lan-mode && !root.lan-password-pending;
accessible-label: GalleryStrings.tr("局域网端口", "LAN port", "Порт LAN");
accessible-label: GalleryStrings.texts[75];
vertical-alignment: center; horizontal-alignment: left;
selection-foreground-color: white; selection-background-color: WorkbenchStyle.accent;
changed has-focus => {
@@ -1403,7 +1404,7 @@ export component MainWindow inherits Window {
x: parent.width - 42px; y: 2px; width: 36px; quiet: true;
icon: root.copy-feedback == CopyFeedback.success ? FontAwesomeIcons.check : FontAwesomeIcons.copy;
icon-color: root.copy-feedback == CopyFeedback.success ? WorkbenchStyle.success : WorkbenchStyle.muted;
tooltip: root.lan-mode ? GalleryStrings.tr("复制 IP 和端口", "Copy IP and port", "Копировать IP и порт") : GalleryStrings.tr("复制本机 ID", "Copy local ID", "Копировать ID");
tooltip: root.lan-mode ? GalleryStrings.texts[76] : GalleryStrings.texts[77];
enabled: !root.lan-mode || root.lan-listener-address != "";
tooltip-suppressed: root.copy-feedback != CopyFeedback.idle;
// Let other controls receive the original click; moving
@@ -1420,8 +1421,8 @@ export component MainWindow inherits Window {
}
private property <string> edit-hint: !root.lan-mode || !root.sponsorship-unobstructed
|| root.lan-password-pending ? ""
: lan-ip-picker.has-hover ? GalleryStrings.tr("点击切换本机 IP", "Click to switch the local IP", "Нажмите, чтобы сменить IP")
: lan-port-hover.has-hover && !lan-port-editor.has-focus ? GalleryStrings.tr("点击修改端口", "Click to edit the port", "Нажмите, чтобы изменить порт") : "";
: lan-ip-picker.has-hover ? GalleryStrings.texts[78]
: lan-port-hover.has-hover && !lan-port-editor.has-focus ? GalleryStrings.texts[79] : "";
private property <bool> edit-hint-visible;
changed edit-hint => { local-address-field.edit-hint-visible = false; }
Timer {
@@ -1453,7 +1454,7 @@ export component MainWindow inherits Window {
WorkbenchButton {
x: parent.width - 112px; y: 0px; width: 36px; quiet: true;
icon: root.active-password-visible ? FontAwesomeIcons.eye : FontAwesomeIcons.eye-slash;
tooltip: root.active-password-visible ? GalleryStrings.tr("隐藏密码", "Hide password", "Скрыть пароль") : GalleryStrings.tr("显示密码", "Show password", "Показать пароль");
tooltip: root.active-password-visible ? GalleryStrings.texts[80] : GalleryStrings.texts[81];
clicked => { if root.lan-mode { root.lan-password-visible = !root.lan-password-visible; } else { root.toggle-password-visibility(); } }
}
WorkbenchButton {
@@ -1465,7 +1466,7 @@ export component MainWindow inherits Window {
WorkbenchButton {
x: parent.width - 36px; y: 0px; width: 36px; quiet: true;
icon: FontAwesomeIcons.pen;
tooltip: GalleryStrings.tr("修改密码", "Change password", "Изменить пароль");
tooltip: GalleryStrings.texts[82];
enabled: root.lan-mode ? !root.lan-password-pending : !root.password-change-pending;
clicked => { root.reset-password-open = true; }
}
@@ -1474,8 +1475,8 @@ export component MainWindow inherits Window {
WorkbenchCard {
x: local-card.width + 16px; y: 0px; width: local-card.width; height: parent.height;
Text { x: 16px; y: 14px; width: parent.width - 32px; height: 24px; overflow: elide; text: UiStrings.remote-desktop; font-size: 16px; font-weight: 600; color: WorkbenchStyle.text; }
Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("输入 IP:端口,通过局域网连接", "Enter IP:port to connect over LAN", "Введите IP:порт для подключения по LAN") : GalleryStrings.tr("输入设备 ID,连接远程桌面", "Enter a device ID to start a remote session", "Введите ID для удалённого подключения"); font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; }
Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.tr("远端 IP / 端口", "Remote IP / Port", "Удалённый IP / Порт") : UiStrings.remote-id; font-size: 12px; color: WorkbenchStyle.muted; }
Text { x: 16px; y: 38px; width: parent.width - 32px; height: 18px; text: root.lan-mode ? GalleryStrings.texts[83] : GalleryStrings.texts[84]; font-size: 11px; color: WorkbenchStyle.muted; overflow: elide; }
Text { x: 16px; y: 68px; height: 18px; text: root.lan-mode ? GalleryStrings.texts[85] : UiStrings.remote-id; font-size: 12px; color: WorkbenchStyle.muted; }
FlatSurface {
x: 16px; y: 90px; width: parent.width - 32px; height: 40px;
background: remote-id-editor.has-focus ? ControlStyle.input-focused : ControlStyle.input-surface;
@@ -1485,14 +1486,14 @@ export component MainWindow inherits Window {
animate outline-color { duration: 120ms; }
if root.remote-id-input == "": Text {
x: 12px; width: parent.width - 24px; height: parent.height;
text: root.lan-mode ? "192.168.1.20:52888" : GalleryStrings.tr("请输入 9 位设备 ID", "Enter a 9-digit device ID", "Введите ID из 9 цифр");
text: root.lan-mode ? "192.168.1.20:52888" : GalleryStrings.texts[86];
font-size: 13px; color: ControlStyle.input-placeholder; vertical-alignment: center;
}
remote-id-editor := TextInput {
x: 12px; width: parent.width - 24px; height: parent.height;
text <=> root.remote-id-input; font-size: root.lan-mode ? 18px : 24px; font-weight: 600; letter-spacing: root.lan-mode ? 0px : 1px;
input-type: root.lan-mode ? InputType.text : InputType.number; color: WorkbenchStyle.text;
accessible-label: root.lan-mode ? GalleryStrings.tr("远端 IP / 端口", "Remote IP / Port", "Удалённый IP / Порт") : UiStrings.remote-id;
accessible-label: root.lan-mode ? GalleryStrings.texts[85] : UiStrings.remote-id;
enabled: !root.connection-pending && !root.lan-connecting;
vertical-alignment: center;
selection-foreground-color: white; selection-background-color: WorkbenchStyle.accent;
@@ -1512,13 +1513,13 @@ export component MainWindow inherits Window {
Text {
x: 16px; y: 133px; width: parent.width - 32px; height: 24px;
text: root.connection-input-error != "" ? root.connection-input-error
: root.lan-mode ? GalleryStrings.tr("不填端口时,默认使用 52888", "Port defaults to 52888 if omitted", "Без порта используется 52888") : "";
: root.lan-mode ? GalleryStrings.texts[87] : "";
font-size: 11px; color: root.connection-input-error != "" ? #be3d42 : WorkbenchStyle.muted; wrap: word-wrap;
}
WorkbenchButton {
x: 16px; y: 158px; width: parent.width - 32px; height: 36px;
primary: true; icon: FontAwesomeIcons.arrow-right-long;
text: root.connection-pending || root.lan-connecting ? GalleryStrings.tr("正在连接…", "Connecting…", "Подключение…") : UiStrings.connect;
text: root.connection-pending || root.lan-connecting ? GalleryStrings.texts[56] : UiStrings.connect;
enabled: !root.connection-pending && !root.lan-connecting;
clicked => { root.submit-connection(); }
}
@@ -1566,19 +1567,19 @@ export component MainWindow inherits Window {
Text {
x: 84px; y: 36px; width: parent.width - 104px; height: 24px;
text: root.lan-mode
? GalleryStrings.tr("暂无近期 IP 连接", "No recent IP connections", "Нет недавних IP-подключений")
? GalleryStrings.texts[88]
: root.recent-group-index == 0
? GalleryStrings.tr("暂无近期连接", "No recent connections", "Нет недавних подключений")
: GalleryStrings.tr("该分组暂无近期连接", "No recent connections in this group", "В этой группе нет недавних подключений");
? GalleryStrings.texts[89]
: GalleryStrings.texts[90];
color: WorkbenchStyle.text; font-size: 14px; font-weight: 600;
}
Text {
x: 84px; y: 68px; width: parent.width - 104px; height: 20px;
text: root.lan-mode
? GalleryStrings.tr("通过 IP 连接过的设备会显示在这里。", "Devices you connect to by IP will appear here.", "Здесь появятся устройства, подключённые по IP.")
? GalleryStrings.texts[91]
: root.recent-group-index == 0
? GalleryStrings.tr("连接过的设备会显示在这里,方便下次快速访问。", "Devices you connect to will appear here for quick access.", "Устройства появятся здесь после первого подключения.")
: GalleryStrings.tr("切换设备分组,查看其他连接记录。", "Choose another group to see your other connections.", "Выберите другую группу для просмотра подключений.");
? GalleryStrings.texts[92]
: GalleryStrings.texts[93];
color: WorkbenchStyle.muted; font-size: 12px;
}
}
@@ -1762,12 +1763,12 @@ export component MainWindow inherits Window {
compact-trigger: true;
compact-menu: true;
enabled: !root.gallery-mode;
text: GalleryStrings.tr("更多操作", "More actions", "Другие действия");
menu-width: root.compact-language ? 116px : root.language-index == 2 ? 156px : 128px;
text: GalleryStrings.texts[94];
menu-width: root.compact-language ? 116px : 156px;
items: [
{label: GalleryStrings.tr("修改名称", "Rename", "Переименовать"), enabled: true},
{label: GalleryStrings.tr("修改分组", "Edit groups", "Изменить группы"), enabled: !connection.lan},
{label: GalleryStrings.tr("删除记录", "Delete record", "Удалить запись"), enabled: true},
{label: GalleryStrings.texts[35], enabled: true},
{label: GalleryStrings.texts[30], enabled: !connection.lan},
{label: GalleryStrings.texts[95], enabled: true},
];
item-icons: [FontAwesomeIcons.pen, FontAwesomeIcons.tags, FontAwesomeIcons.trash-can];
destructive-index: 2;
@@ -1794,7 +1795,7 @@ export component MainWindow inherits Window {
primary: !connection.connected;
destructive: connection.connected;
icon: connection.connected ? FontAwesomeIcons.xmark : FontAwesomeIcons.arrow-right-long;
tooltip: connection.connected ? GalleryStrings.tr("断开连接", "Disconnect", "Отключить") : UiStrings.connect;
tooltip: connection.connected ? GalleryStrings.texts[96] : UiStrings.connect;
tooltip-above: true;
clicked => {
root.copy-feedback = CopyFeedback.idle;
@@ -1884,7 +1885,7 @@ export component MainWindow inherits Window {
height: parent.height;
width: parent.width - 160px;
overflow: elide;
text: root.lan-mode ? (root.lan-message != "" ? LanStrings.error-message(root.lan-message) : root.lan-listener-error != "" ? (root.lan-listener-address == "" ? "" : root.lan-listener-address + " · ") + LanStrings.error-message(root.lan-listener-error) : root.lan-listening ? GalleryStrings.tr("IP 连接已启用", "IP connections enabled", "Подключения по IP включены") : root.lan-password-pending ? GalleryStrings.tr("IP 连接准备中…", "Preparing IP connections…", "Подготовка подключений по IP…") : GalleryStrings.tr("IP 连接未就绪", "IP connections not ready", "Подключения по IP не готовы"))
text: root.lan-mode ? (root.lan-message != "" ? LanStrings.error-message(root.lan-message) : root.lan-listener-error != "" ? (root.lan-listener-address == "" ? "" : root.lan-listener-address + " · ") + LanStrings.error-message(root.lan-listener-error) : root.lan-listening ? GalleryStrings.texts[97] : root.lan-password-pending ? GalleryStrings.texts[98] : GalleryStrings.texts[99])
: root.signal-tls-error ? UiStrings.tls-error
: root.signal-connected ? UiStrings.signal-connected
: root.signal-reconnecting ? UiStrings.signal-reconnecting
@@ -1969,7 +1970,7 @@ export component MainWindow inherits Window {
tooltip-status-text := Text {
x: 7px; width: parent.width - 14px; height: parent.height;
text: device-tip.online ? UiStrings.online : device-tip.presence-known ? UiStrings.offline
: GalleryStrings.tr("未知", "Unknown", "Неизвестно");
: GalleryStrings.texts[100];
color: device-tip.status-color; font-size: 10px; font-weight: 600;
vertical-alignment: center;
}
@@ -2013,14 +2014,14 @@ export component MainWindow inherits Window {
tooltip-group-label := Text {
x: 12px; y: tooltip-groups.y;
width: device-tip.label-width; height: 16px;
text: GalleryStrings.tr("分组", "Groups", "Группы");
text: GalleryStrings.texts[101];
color: WorkbenchStyle.muted; font-size: 10px;
vertical-alignment: center;
}
tooltip-groups := Text {
x: 20px + device-tip.label-width; y: tooltip-id-label.y + tooltip-id-label.height + 4px;
width: parent.width - self.x - 12px; height: max(16px, self.preferred-height);
text: device-tip.group-names == "" ? GalleryStrings.tr("未分组", "Ungrouped", "Без группы") : device-tip.group-names;
text: device-tip.group-names == "" ? GalleryStrings.texts[31] : device-tip.group-names;
color: device-tip.group-names == "" ? WorkbenchStyle.muted : WorkbenchStyle.accent;
font-size: 10px;
wrap: word-wrap; horizontal-alignment: right; vertical-alignment: center;
@@ -2220,8 +2221,8 @@ export component MainWindow inherits Window {
error: root.copy-feedback == CopyFeedback.failed;
icon: self.error ? FontAwesomeIcons.xmark : FontAwesomeIcons.check;
text: self.error
? GalleryStrings.tr("复制失败,请重试", "Couldn’t copy. Try again", "Не удалось скопировать. Повторите попытку")
: root.lan-mode ? GalleryStrings.tr("IP 和端口已复制", "IP and port copied", "IP и порт скопированы") : UiStrings.copied;
? GalleryStrings.texts[102]
: root.lan-mode ? GalleryStrings.texts[103] : UiStrings.copied;
z: 30;
}
@@ -2332,7 +2333,7 @@ export component MainWindow inherits Window {
height: 140px;
VerticalLayout {
padding: 20px; spacing: 18px;
DialogPromptText { prompt: root.delete-from-gallery ? (root.gallery-group-index > 0 ? GalleryStrings.tr("从当前分组移除此设备?", "Remove from this group?", "Убрать из этой группы?") : GalleryStrings.tr("从桌面墙移除此设备?", "Remove from DeskHub?", "Убрать из DeskHub?")) : UiStrings.confirm-delete; }
DialogPromptText { prompt: root.delete-from-gallery ? (root.gallery-group-index > 0 ? GalleryStrings.texts[104] : GalleryStrings.texts[105]) : UiStrings.confirm-delete; }
HorizontalLayout {
alignment: center; spacing: 8px;
PrimaryButton { text: UiStrings.ok; clicked => { if root.delete-from-gallery { root.gallery-action("remove", root.delete-remote-id); } else { root.recent-delete(root.delete-remote-id); } root.delete-open = false; } }
@@ -2385,7 +2386,7 @@ export component MainWindow inherits Window {
padding: 24px;
spacing: 16px;
Text {
text: GalleryStrings.tr("确认断开连接?", "Disconnect this session?", "Отключить этот сеанс?");
text: GalleryStrings.texts[106];
color: WorkbenchStyle.text;
font-size: 18px;
font-weight: 600;
@@ -2407,7 +2408,7 @@ export component MainWindow inherits Window {
}
}
Text {
text: GalleryStrings.tr("当前远程控制会话将结束。", "The current remote session will end.", "Текущий сеанс удалённого управления будет завершён.");
text: GalleryStrings.texts[107];
color: WorkbenchStyle.muted;
font-size: 13px;
wrap: word-wrap;
@@ -2420,7 +2421,7 @@ export component MainWindow inherits Window {
clicked => { root.dismiss-disconnect(); }
}
disconnect-confirm := WorkbenchButton {
text: GalleryStrings.tr("断开连接", "Disconnect", "Отключить");
text: GalleryStrings.texts[96];
destructive: true;
clicked => {
let remote-id = root.disconnect-remote-id;
@@ -2548,9 +2549,9 @@ export component MainWindow inherits Window {
spacing: 12px;
for permission[index] in [
{label: UiStrings.accessibility-permission, granted: root.accessibility-granted,
description: GalleryStrings.tr("允许远程键盘和鼠标控制", "Allow remote keyboard and mouse input", "Разрешить удалённый ввод с клавиатуры и мыши")},
description: GalleryStrings.texts[108]},
{label: UiStrings.screen-recording-permission, granted: root.screen-recording-granted,
description: GalleryStrings.tr("允许共享此 Mac 的屏幕画面", "Allow sharing this Mac’s screen", "Разрешить показ экрана этого Mac")},
description: GalleryStrings.texts[109]},
]: WorkbenchCard {
height: max(80px, permission-labels.preferred-height + 32px);
permission-labels := VerticalLayout {
@@ -2571,7 +2572,7 @@ export component MainWindow inherits Window {
height: 36px;
permission-action-metrics := Text {
visible: false; font-size: 13px; font-weight: 600;
text: GalleryStrings.tr("前往设置", "Open settings", "Настройки");
text: GalleryStrings.texts[110];
}
if !permission.granted: WorkbenchButton {
width: parent.width; height: parent.height; primary: true;
@@ -2590,7 +2591,7 @@ export component MainWindow inherits Window {
vertical-alignment: center; accessible-role: none;
}
Text {
text: GalleryStrings.tr("已授权", "Granted", "Разрешено");
text: GalleryStrings.texts[111];
font-size: 12px; color: WorkbenchStyle.success; vertical-alignment: center;
}
}
@@ -2601,9 +2602,7 @@ export component MainWindow inherits Window {
spacing: 12px;
SurfaceSeparator { height: 1px; outline-color: UiSurface.separator; }
Text {
text: GalleryStrings.tr("请在系统设置中允许 CrossDesk 访问。如系统提示重新打开应用,请按提示操作。",
"Allow CrossDesk access in System Settings. Reopen the app if macOS asks you to.",
"Разрешите доступ для CrossDesk в Системных настройках. Перезапустите приложение, если macOS попросит об этом.");
text: GalleryStrings.texts[112];
font-size: 11px; color: WorkbenchStyle.muted; wrap: word-wrap;
}
}
@@ -2645,7 +2644,7 @@ export component MainWindow inherits Window {
background: WorkbenchStyle.accent; border-radius: UiRadius.control;
animate x { duration: 180ms; easing: ease-in-out; }
}
for label[index] in [GalleryStrings.tr("常规", "General", "Общие"), GalleryStrings.tr("桌面墙", "DeskHub", "DeskHub")]: Rectangle {
for label[index] in [GalleryStrings.texts[113], GalleryStrings.texts[54]]: Rectangle {
x: index == 1 ? parent.width / 2 + 1px : 2px;
y: 2px; width: (parent.width - 6px) / 2; height: parent.height - 4px;
accessible-role: button; accessible-label: label;
@@ -2683,14 +2682,14 @@ export component MainWindow inherits Window {
padding-right: settings-dialog.scrollbar-gutter; padding-bottom: 4px;
spacing: 14px;
SettingsGroup {
title: GalleryStrings.tr("界面", "Interface", "Интерфейс"); rows: 1;
title: GalleryStrings.texts[114]; rows: 1;
SettingsRow {
label: UiStrings.language; divider: false;
SettingsSelect { x: parent.width - 116px; y: 4px; width: 104px; model: ["中文", "English", "Русский"]; current-index <=> root.language-index; }
SettingsSelect { x: parent.width - 192px; y: 4px; width: 180px; model: root.language-options; current-index <=> root.language-index; }
}
}
SettingsGroup {
title: GalleryStrings.tr("视频", "Video", "Видео");
title: GalleryStrings.texts[115];
rows: root.capture-method-visible ? 2 : 1;
if root.capture-method-visible: SettingsRow {
label: UiStrings.capture-method; enabled: !root.settings-session-active;
@@ -2702,9 +2701,9 @@ export component MainWindow inherits Window {
}
}
SettingsGroup {
title: GalleryStrings.tr("网络连接", "Network", "Сеть"); rows: 3;
title: GalleryStrings.texts[116]; rows: 3;
SettingsToggleRow {
label: GalleryStrings.tr("启用 IP 连接", "Enable IP connections", "Подключение по IP");
label: GalleryStrings.texts[117];
checked <=> root.ip-direct-enabled-draft;
}
SettingsToggleRow { label: UiStrings.force-relay; checked <=> root.force-relay-enabled; enabled: !root.settings-session-active; }
@@ -2715,7 +2714,7 @@ export component MainWindow inherits Window {
self-host-edit := SettingsButton {
x: parent.width - self.width - 60px; y: 4px;
width: self-host-edit-metrics.preferred-width + 24px; height: 28px;
text: GalleryStrings.tr("编辑", "Edit", "Изменить");
text: GalleryStrings.texts[118];
enabled: !root.settings-session-active;
clicked => { root.self-host-settings-open = true; }
self-host-edit-metrics := Text {
@@ -2726,21 +2725,21 @@ export component MainWindow inherits Window {
}
}
Text {
text: GalleryStrings.tr("关闭 IP 连接将隐藏模式按钮,并断开 IP 连接会话。", "Disabling IP connections hides the mode button and ends IP sessions.", "Отключение подключений по IP скроет кнопку режима и завершит IP-сеансы.");
text: GalleryStrings.texts[119];
font-size: 11px; color: WorkbenchStyle.muted; wrap: word-wrap;
}
SettingsGroup {
title: GalleryStrings.tr("隐私保护", "Privacy", "Приватность"); rows: 3;
title: GalleryStrings.texts[120]; rows: 3;
SettingsToggleRow { label: UiStrings.privacy-on-connect; checked <=> root.privacy-on-connect-enabled; enabled: root.privacy-setting-available; }
SettingsToggleRow { label: UiStrings.privacy-local-unlock; checked <=> root.privacy-local-unlock-allowed; enabled: root.privacy-setting-available; }
SettingsToggleRow { label: UiStrings.lock-on-disconnect; checked <=> root.lock-on-disconnect-enabled; enabled: root.lock-on-disconnect-available; divider: false; }
}
Text {
text: GalleryStrings.tr("最后一个远程控制端断开后,自动锁定本机。", "Lock this device when the last remote controller disconnects.", "Блокировать это устройство после отключения последнего управляющего клиента.");
text: GalleryStrings.texts[121];
font-size: 11px; color: WorkbenchStyle.muted; wrap: word-wrap;
}
SettingsGroup {
title: GalleryStrings.tr("本地存储", "Local storage", "Локальное хранение"); rows: 2;
title: GalleryStrings.texts[122]; rows: 2;
SettingsToggleRow { label: UiStrings.save-remote-previews; checked <=> root.save-remote-previews; }
SettingsRow {
label: UiStrings.file-save-path; enabled: !root.settings-session-active; divider: false;
@@ -2748,7 +2747,7 @@ export component MainWindow inherits Window {
}
}
SettingsGroup {
title: GalleryStrings.tr("启动与后台", "Startup & background", "Запуск и фоновая работа"); rows: 2;
title: GalleryStrings.texts[123]; rows: 2;
if !root.autostart-system-managed: SettingsToggleRow { label: UiStrings.autostart; checked <=> root.autostart-enabled; enabled: !root.settings-session-active; }
if root.autostart-system-managed: SettingsToggleRow {
label: UiStrings.autostart;
@@ -2784,12 +2783,12 @@ export component MainWindow inherits Window {
width: parent.width - 2 * settings-dialog.content-inset; height: 32px; spacing: 10px;
Rectangle { horizontal-stretch: 1; }
SettingsButton {
width: 80px; text: UiStrings.cancel;
width: max(80px, self.label-width); text: UiStrings.cancel;
clicked => { root.cancel-settings(); root.settings-open = false; root.self-host-settings-open = false; }
}
SettingsButton {
width: 92px; primary: true;
text: GalleryStrings.tr("保存", "Save", "Сохранить");
width: max(92px, self.label-width); primary: true;
text: GalleryStrings.texts[34];
clicked => { if root.save-settings() { root.settings-open = false; root.self-host-settings-open = false; } }
}
}
@@ -2807,7 +2806,7 @@ export component MainWindow inherits Window {
TouchArea { clicked => { focus-sink.focus(); } }
Text {
x: 24px; y: 18px; width: parent.width - 48px; height: 26px;
text: GalleryStrings.tr("设置开机自启动", "Set up auto start", "Настройка автозапуска");
text: GalleryStrings.texts[124];
font-size: 18px; font-weight: 600; color: WorkbenchStyle.text;
}
ScrollView {
@@ -2819,25 +2818,20 @@ export component MainWindow inherits Window {
width: parent.width; padding: 0px; spacing: 12px;
Text { text: root.autostart-status-text; color: WorkbenchStyle.accent; font-size: 13px; wrap: word-wrap; }
Text {
text: root.autostart-status == AutostartStatus.unsupported ? GalleryStrings.tr(
"当前设备不支持开机自启动。", "This device does not support auto start.", "Это устройство не поддерживает автозапуск.") :
root.autostart-status == AutostartStatus.failed ? GalleryStrings.tr(
"无法读取系统状态。请刷新重试,或在系统设置中核对。", "Could not read the system status. Refresh to retry, or check System Settings.", "Не удалось прочитать состояние. Повторите попытку или проверьте настройки системы.") :
GalleryStrings.tr("请在系统设置中添加或移除本应用,返回后会自动刷新状态。", "Add or remove this app in system Settings. The status refreshes when you return.", "Добавьте или удалите приложение в настройках системы. После возвращения статус обновится.");
text: root.autostart-status == AutostartStatus.unsupported ? GalleryStrings.texts[125] :
root.autostart-status == AutostartStatus.failed ? GalleryStrings.texts[126] :
GalleryStrings.texts[127];
color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap;
}
if root.autostart-status != AutostartStatus.unsupported: Text {
text: GalleryStrings.tr(
"设置 → 应用和元服务 → 应用启动管理\n\n开启:点击“添加应用”,选择 CrossDesk,然后点击“完成”。\n\n关闭:在自启动列表中找到本应用,点击“移除”。",
"Settings → Apps & services → App startup management\n\nEnable: choose Add app, select CrossDesk, then choose Done.\n\nDisable: find this app in the startup list and choose Remove.",
"Настройки → Приложения и сервисы → Управление запуском\n\nВключить: добавьте CrossDesk в список и подтвердите выбор.\n\nВыключить: удалите приложение из списка автозапуска.");
text: GalleryStrings.texts[128];
color: WorkbenchStyle.text; font-size: 13px; wrap: word-wrap;
}
}
}
SettingsButton {
x: 24px; y: parent.height - 48px; width: 112px;
text: GalleryStrings.tr("刷新状态", "Refresh status", "Обновить статус");
text: GalleryStrings.texts[129];
enabled: root.autostart-status != AutostartStatus.checking;
clicked => { root.refresh-autostart-status(); }
}
@@ -2882,7 +2876,7 @@ export component MainWindow inherits Window {
}
SurfaceSeparator { x: 18px; y: parent.height - 66px; width: parent.width - 36px; height: 1px; outline-color: UiSurface.separator; }
SettingsButton { x: parent.width - 114px; y: parent.height - 50px; width: 92px; primary: true; text: UiStrings.ok; clicked => { if root.save-self-hosted-settings() { root.self-host-settings-open = false; } } }
SettingsButton { x: parent.width - 204px; y: parent.height - 50px; width: 80px; text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } }
SettingsButton { x: parent.width - 124px - self.width; y: parent.height - 50px; width: max(80px, self.label-width); text: UiStrings.cancel; clicked => { root.cancel-self-hosted-settings(); root.self-host-settings-open = false; } }
}
}
@@ -11,6 +11,7 @@ export component SettingsButton inherits FlatSurface {
in property <bool> quiet: false;
in property <bool> align-left: false;
in property <bool> enabled: true;
out property <length> label-width: button-label.preferred-width + 2 * root.content-padding + (root.has-icon ? 24px : 0px);
private property <bool> has-icon: root.icon.width > 0;
private property <length> content-padding: root.has-icon ? 8px : 12px;
callback clicked();
+15 -1
View File
@@ -40,7 +40,21 @@ inline int ApplyMainWindowStrings(
int language_index) {
const int language =
localization::detail::ClampLanguageIndex(language_index);
window->global<ui::GalleryStrings>().set_language(language);
auto& gallery = window->global<ui::GalleryStrings>();
gallery.set_language(language);
const auto& retry = localization::Literal("请在 {0} 秒后重试", language);
const auto split = retry.find("{0}");
gallery.set_retry_prefix(Text(retry.substr(0, split)));
gallery.set_retry_suffix(Text(split == std::string::npos ? "" : retry.substr(split + 3)));
std::vector<slint::SharedString> literals;
for (const auto* key : localization::detail::kSlintLiterals)
literals.push_back(Text(localization::Literal(key, language)));
gallery.set_texts(std::make_shared<slint::VectorModel<slint::SharedString>>(literals));
std::vector<slint::SharedString> languages;
for (const auto& option : localization::GetSupportedLanguages())
languages.push_back(Text(option.display_name));
languages.push_back(Text(localization::Literal("跟随系统", language)));
window->set_language_options(std::make_shared<slint::VectorModel<slint::SharedString>>(languages));
auto& strings = window->global<ui::UiStrings>();
strings.set_window_minimize(Text(localization::window_minimize[language]));
strings.set_window_close(Text(localization::window_close[language]));
@@ -25,15 +25,15 @@ std::optional<bool> Boolean(const std::string& value) {
return std::nullopt;
}
const char* OnOff(bool value) { return value ? "on" : "off"; }
const char* languages[] = {"zh-CN", "en-US", "ru-RU"};
const char* languages[] = {"zh-CN", "en-US", "ru-RU", "zh-Hant", "ja", "ko", "de", "fr", "es", "pt-BR", "system"};
const char* turn_modes[] = {"off", "auto", "udp", "tcp"};
} // namespace
std::vector<ConsoleSetting> GetConsoleSettings(const ConfigCenter& config) {
const auto language = std::clamp(static_cast<int>(config.GetLanguage()), 0, 2);
const auto language = std::clamp(static_cast<int>(config.GetLanguage()), 0, 10);
const auto turn = std::clamp(static_cast<int>(config.GetTurnMode()), 0, 3);
return {
{"language", "console_setting_language", languages[language], "zh-CN / en-US / ru-RU"},
{"language", "console_setting_language", languages[language], "system / zh-CN / en-US / ru-RU / zh-Hant / ja / ko / de / fr / es / pt-BR"},
{"codec", "console_setting_codec", config.GetVideoEncodeFormat() == ConfigCenter::VIDEO_ENCODE_FORMAT::AV1 ? "av1" : "h264", "h264 / av1"},
{"turn", "console_setting_turn", turn_modes[turn], "off / auto / udp / tcp"},
{"self_hosted", "console_setting_self_hosted", OnOff(config.IsSelfHosted()), "on / off"},
@@ -70,6 +70,10 @@ SettingResult ApplyConsoleSetting(ConfigCenter& config, const std::string& key,
int language = value == "zh-cn" || value == "zh" || value == "0" ? 0
: value == "en-us" || value == "en" || value == "1" ? 1
: value == "ru-ru" || value == "ru" || value == "2" ? 2 : -1;
if (language < 0) {
for (int i = 0; i <= 10; ++i)
if (value == Lower(languages[i]) || value == std::to_string(i)) language = i;
}
if (language < 0) return result;
canonical = languages[language];
save = [&config, language] { return config.SetLanguage(static_cast<ConfigCenter::LANGUAGE>(language)); };
+2 -1
View File
@@ -1,3 +1,4 @@
import { i18n } from '@kit.LocalizationKit';
import native from 'libslint_desktop.so';
import { TextInputHost } from './TextInputHost';
import { TrayHost } from './TrayHost';
@@ -128,7 +129,7 @@ export class DesktopHost {
if (event.op === 'clipboardChanged') DesktopHost.clipboardChanged();
else DesktopHost.send(event);
}, hostPermissions);
native.start(JSON.stringify({ filesDir: context.filesDir, cacheDir: context.cacheDir,
native.start(JSON.stringify({ language: i18n.System.getSystemLanguage(), filesDir: context.filesDir, cacheDir: context.cacheDir,
width: screen.width, height: screen.height, density: screen.densityPixels,
displayId: screen.id, displays: screens, name: 'Harmony PC',
certificates: certificateManager.getCertificateStorePath({ certType: certificateManager.CertType.CA_CERT_SYSTEM }) }));
+2
View File
@@ -307,6 +307,8 @@ napi_value Start(napi_env env, napi_callback_info info) {
stopping = false;
gui_thread = std::thread([] {
try {
const auto language = Environment().value("language", "en");
setenv("LANGUAGE", language.c_str(), 1);
setenv("SLINT_DEFAULT_FONT", "/system/fonts/HarmonyOS_Sans.ttf", 1);
setenv("SLINT_FONT_PATH", "/system/fonts/HarmonyOS_Sans_SC.ttf", 1);
const auto certificates = Environment().value("certificates", "");
@@ -1,3 +1,4 @@
import { tr } from './Localization';
// Platform-independent controller rules ported from the Android client.
// Keep gesture geometry, acknowledgement ordering and expiry behavior identical.
export function clamp(value: number, low: number, high: number): number {
@@ -96,7 +97,7 @@ export class VideoSettings {
this.pending = false; this.failed = !accepted; this.selection = this.applied;
}
expire(id: number): void { if (this.pending && id === this.requestId) { this.pending = false; this.failed = true; this.selection = this.applied; } }
feedback(): string { return !this.supported ? '连接建立后可调整,需被控端支持。' : this.failed ? '调整失败,请重试。' : this.pending ? '等待电脑端确认…' : ''; }
feedback(): string { return !this.supported ? tr("连接建立后可调整,需被控端支持。") : this.failed ? tr("调整失败,请重试。") : this.pending ? tr("等待电脑端确认…") : ''; }
}
export interface Traffic { inbound: number; outbound: number; loss: number; }
export interface NetworkReport { rtt: number; mode: number; srtp: boolean; decoder?: string; video: Traffic; audio: Traffic; data: Traffic; total: Traffic; }
@@ -133,7 +134,7 @@ export class NetworkStatistics {
return count ? valid.reduce((n: number, f: FrameSample) => n + f.latency * f.latencyCount, 0) / count : -1;
}
current(now: number): NetworkReport | undefined { return now - this.reportTime < 3000 ? this.report : undefined; }
mode(now: number): string { const report = this.current(now); return !report ? '—' : report.mode === 0 ? 'P2P 直连' : report.mode === 1 ? 'TURN 中继' : '—'; }
mode(now: number): string { const report = this.current(now); return !report ? '—' : report.mode === 0 ? tr("P2P 直连") : report.mode === 1 ? tr("TURN 中继") : '—'; }
static rate(n: number): string { return !Number.isFinite(n) || n < 0 ? '—' : n >= 1000000 ? `${(n / 1000000).toFixed(1)} Mbps` : n >= 1000 ? `${(n / 1000).toFixed(0)} Kbps` : `${n} bps`; }
static loss(n: number): string { return Number.isFinite(n) && n >= 0 ? `${(n * 100).toFixed(1)}%` : '—'; }
static delay(n: number): string { return !Number.isFinite(n) || n < 0 ? '—' : n < 1 ? '<1 ms' : `${n.toFixed(0)} ms`; }
@@ -1,3 +1,5 @@
import { i18n } from '@kit.LocalizationKit';
import { setLanguageProvider } from './Localization';
import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
@@ -5,6 +7,7 @@ import { MobileLifecycle } from './MobilePlatform';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(stage: window.WindowStage): void {
setLanguageProvider(() => i18n.System.getSystemLanguage());
stage.loadContent('pages/Mobile', (error) => {
if (error.code) hilog.error(0, 'CrossDesk', 'Unable to load the main page: %{public}d', error.code);
});
@@ -0,0 +1,37 @@
import { translations } from './LocalizationData';
let languageProvider: (() => string) | undefined = undefined;
export function setLanguageProvider(provider: () => string): void {
languageProvider = provider;
}
export function languageIndex(tag: string): number {
const value = tag.toLowerCase().replace(/_/g, '-').split('.')[0].split('@')[0];
const base = value.split('-')[0];
if (base === 'zh') {
const parts = '-' + value + '-';
if (parts.includes('-hans-')) return 0;
return ['-hant-', '-tw-', '-hk-', '-mo-'].some((part: string) => parts.includes(part)) ? 3 : 0;
}
const codes = ['zh', 'en', 'ru', 'zh-hant', 'ja', 'ko', 'de', 'fr', 'es', 'pt'];
const index = codes.indexOf(base);
return index < 0 ? 1 : index;
}
export function currentLanguage(): number {
try { return languageProvider ? languageIndex(languageProvider()) : 1; }
catch { return 1; }
}
export function tr(key: string, args: string[] = []): string {
// The pure models can also run before an ability installs its locale provider.
const values = translations[key];
let text = languageProvider && values ? values[currentLanguage()] || values[1] : key;
// A single replacement pass avoids interpreting placeholder-like user data.
text = text.replace(/\{(\d+)\}/g, (match: string, index: string) => {
const value = args[Number(index)];
return value === undefined ? match : value;
});
return text;
}
File diff suppressed because one or more lines are too long
@@ -1,3 +1,4 @@
import { tr } from './Localization';
import { RemotePrivacyScreen, PrivacyStatus } from './RemotePrivacyScreen';
import bridge from 'libcrossdesk.so';
import { common, bundleManager } from '@kit.AbilityKit';
@@ -44,7 +45,7 @@ export class MobileState {
password: string = '';
remember: boolean = false;
signal: number = -1;
signalText: string = '未连接';
signalText: string = tr("未连接");
identity: string = '';
connecting: boolean = false;
ready: boolean = false;
@@ -76,7 +77,7 @@ export class MobileState {
videoSupported: boolean = false;
videoFeedback: string = '';
privacyStatus: string = '';
privacyButtonTitle: string = '隐私屏';
privacyButtonTitle: string = tr("隐私屏");
privacyFeedback: string = '';
privacyEnabled: boolean = false;
privacyActive: boolean = false;
@@ -160,9 +161,9 @@ export class MobileController {
now(): number { return systemDateTime.getUptime(systemDateTime.TimeType.STARTUP, false); }
toast(text: string): void { if (this.foreground) promptAction.showToast({ message: text, duration: 3000 }); else this.state.notice = text; }
async consent(): Promise<void> {
if (!this.state.privacyAvailable) { this.state.notice = '隐私政策无法读取,请重新安装应用'; return; }
if (!this.state.privacyAvailable) { this.state.notice = tr("隐私政策无法读取,请重新安装应用"); return; }
try { this.store.put('consent', true); this.state.consent = true; this.state.modal = ''; await this.ensureSignal(); this.checkLocal(); }
catch { this.toast('无法保存联网授权,请重试'); }
catch { this.toast(tr("无法保存联网授权,请重试")); }
}
revoke(): void {
this.end(''); this.stopSignal(); this.state.consent = false; this.state.previews = false;
@@ -188,16 +189,16 @@ export class MobileController {
// A public ID alone is not a login credential. Discard legacy incomplete records.
if (identity && !/^[0-9]{9}@.+$/.test(identity)) { await this.store.removeSecret(`identity:${scope}`); identity = ''; }
if (session !== this.session || !this.foreground || !this.state.consent) return;
this.state.signalText = '正在连接…'; this.state.signal = 0; this.started = true;
this.state.signalText = tr("正在连接…"); this.state.signal = 0; this.started = true;
this.send({ op: 'start', session: session, server: this.state.server, port: Number(this.state.port), identity: identity,
logs: this.context.cacheDir, certificates: '/etc/security/certificates', directory: this.store.directory,
previewDirectory: this.store.previewDirectory, version: this.state.version });
} catch { this.state.signalText = '安全存储不可用,请解锁设备后重试'; this.state.signal = -1; }
} catch { this.state.signalText = tr("安全存储不可用,请解锁设备后重试"); this.state.signal = -1; }
finally { if (session === this.session) this.starting = false; }
}
private stopSignal(): void {
this.session++; this.starting = this.started = false; this.signalGeneration = -1;
this.send({ op: 'stop' }); this.state.signal = -1; this.state.identity = ''; this.state.signalText = '未连接';
this.send({ op: 'stop' }); this.state.signal = -1; this.state.identity = ''; this.state.signalText = tr("未连接");
this.presence.connect(false, this.now()); this.inbox.connect(false); this.platform.cancelRequests();
this.localVersionRequest = this.remoteVersionRequest = 0; this.checkingLocal = false;
}
@@ -205,7 +206,7 @@ export class MobileController {
saveSettings(server: string, port: string): void {
server = server.trim(); const number = Number(port);
if (!server || server.length > 253 || /[\s/@?#]/.test(server) || !Number.isInteger(number) || number < 1 || number > 65535) {
this.toast('请输入有效的服务器地址和端口'); return;
this.toast(tr("请输入有效的服务器地址和端口")); return;
}
const changed = server !== this.state.server || port !== this.state.port;
this.store.put('server', server); this.store.put('port', port); this.state.server = server; this.state.port = port;
@@ -219,11 +220,11 @@ export class MobileController {
.concat(values.filter((d: RecentDevice) => !this.presence?.online(d.id, now)));
}
async deleteRecent(id: string): Promise<void> {
try { await this.store.delete(this.scope(), id); this.history(); } catch { this.toast('记录或密码删除失败,请重试'); }
try { await this.store.delete(this.scope(), id); this.history(); } catch { this.toast(tr("记录或密码删除失败,请重试")); }
}
async requestConnection(id: string): Promise<void> {
id = id.replace(/\s/g, ''); this.state.remote = id;
if (!/^[0-9]{9}$/.test(id)) { this.toast('请输入 9 位设备 ID'); return; }
if (!/^[0-9]{9}$/.test(id)) { this.toast(tr("请输入 9 位设备 ID")); return; }
if (!this.state.consent) { this.state.modal = 'consent'; return; }
const epoch = ++this.uiEpoch, scope = this.scope();
this.state.password = ''; this.state.notice = '';
@@ -232,11 +233,11 @@ export class MobileController {
const password = await this.store.secret(`password:${scope}:${id}`);
if (epoch !== this.uiEpoch || !this.foreground || !this.state.consent) return;
if (password) { this.state.remember = true; this.begin(password); return; }
} catch { if (epoch === this.uiEpoch) this.state.notice = '已保存密码无法读取,请重新输入'; }
} catch { if (epoch === this.uiEpoch) this.state.notice = tr("已保存密码无法读取,请重新输入"); }
if (epoch === this.uiEpoch) this.state.modal = 'password';
}
begin(password: string): void {
if (!this.state.consent || !/^[0-9]{9}$/.test(this.state.remote) || !password || password.length > 128 || password.includes('@')) { this.toast('请输入有效的访问密码'); return; }
if (!this.state.consent || !/^[0-9]{9}$/.test(this.state.remote) || !password || password.length > 128 || password.includes('@')) { this.toast(tr("请输入有效的访问密码")); return; }
this.joinSent = false; this.connection++; this.connectingId = this.state.remote; this.pendingPassword = password;
this.state.connecting = true; this.state.ready = false; this.state.modal = 'connecting'; this.state.notice = '';
this.state.muted = false; this.state.platform = ''; this.state.hostName = ''; this.state.receivedPath = this.state.receivedName = this.state.transfer = '';
@@ -276,7 +277,7 @@ export class MobileController {
}
shortcut(code: number): void { this.chord(code, [this.state.platform === 'macos' ? 91 : 17]); }
text(text: string): void {
if (text.length > 2048 || Array.from(text).some((c: string) => asciiKey(c) === 0)) { this.toast('中文等文本请通过剪贴板发送,再点击粘贴'); return; }
if (text.length > 2048 || Array.from(text).some((c: string) => asciiKey(c) === 0)) { this.toast(tr("中文等文本请通过剪贴板发送,再点击粘贴")); return; }
for (const c of text) { const code = asciiKey(c); this.chord(code & 255, code & 256 ? [16] : []); }
}
setRelative(value: boolean): void { this.release(); this.state.relative = value; this.store.put('relative', value); }
@@ -301,27 +302,27 @@ export class MobileController {
if (!value) { this.send({ op: 'cancelPreview' }); this.previewEligible = false; this.store.clearPreviews(); this.history(); }
this.state.modal = '';
}
clearPreviews(): void { this.send({ op: 'cancelPreview' }); this.previewEligible = false; this.store.clearPreviews(); this.history(); this.toast('已清除预览'); }
clearPreviews(): void { this.send({ op: 'cancelPreview' }); this.previewEligible = false; this.store.clearPreviews(); this.history(); this.toast(tr("已清除预览")); }
clipboard(): void {
const text = this.state.clipboardText;
if (new util.TextEncoder().encodeInto(text).length > 128 * 1024) { this.toast('文本超过 128 KB'); return; }
if (text) { this.send({ op: 'clipboard', text: text }); this.toast('已发送,请在远端粘贴'); }
if (new util.TextEncoder().encodeInto(text).length > 128 * 1024) { this.toast(tr("文本超过 128 KB")); return; }
if (text) { this.send({ op: 'clipboard', text: text }); this.toast(tr("已发送,请在远端粘贴")); }
}
async copyRemote(): Promise<void> {
if (!this.state.remoteClipboard) { this.toast('尚未收到远端文本'); return; }
try { await pasteboard.getSystemPasteboard().setData(pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.state.remoteClipboard)); this.toast('已复制远端文本'); }
catch { this.toast('复制失败,请重试'); }
if (!this.state.remoteClipboard) { this.toast(tr("尚未收到远端文本")); return; }
try { await pasteboard.getSystemPasteboard().setData(pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, this.state.remoteClipboard)); this.toast(tr("已复制远端文本")); }
catch { this.toast(tr("复制失败,请重试")); }
}
sas(): void {
if (this.state.platform !== 'windows') return;
if (!this.serviceAvailable) { this.toast('请在远端启用 CrossDesk 系统服务后重试'); return; }
if (!this.serviceAvailable) { this.toast(tr("请在远端启用 CrossDesk 系统服务后重试")); return; }
this.send({ op: 'sas' });
}
async chooseFile(): Promise<void> {
if (this.sendingFile) { this.toast('请等待当前文件发送完成'); return; }
if (this.sendingFile) { this.toast(tr("请等待当前文件发送完成")); return; }
const connection = this.connection; const file = await this.platform.choose(this.store.directory);
if (connection !== this.connection || !this.state.ready || !file) { if (file) this.store.discardOutgoing(file.path); return; }
this.sendingFile = true; this.state.transfer = `发送 ${file.name} · 0%`;
this.sendingFile = true; this.state.transfer = tr("发送 {0} · 0%", [String(file.name)]);
this.send({ op: 'sendFile', path: file.path, name: file.name });
}
saveFile(): void { if (this.state.receivedPath) this.platform.save(this.state.receivedPath, this.state.receivedName); }
@@ -331,17 +332,17 @@ export class MobileController {
async checkLocal(manual: boolean = false): Promise<void> {
if (!this.state.consent || !this.foreground || this.checkingLocal) return;
this.checkingLocal = true; const request = ++this.versionRequest; this.localVersionRequest = request;
const epoch = this.uiEpoch; if (manual) this.state.updateStatus = '正在检查…';
const epoch = this.uiEpoch; if (manual) this.state.updateStatus = tr("正在检查…");
const body = await this.platform.version();
if (request !== this.localVersionRequest || epoch !== this.uiEpoch || !this.foreground || !this.state.consent) return;
this.checkingLocal = false;
if (body) this.send({ op: 'version', body: body, local: true, version: this.state.version, request: request });
else if (manual) this.state.updateStatus = '检查失败,请稍后重试';
else if (manual) this.state.updateStatus = tr("检查失败,请稍后重试");
}
private async checkRemote(version: string): Promise<void> {
if (this.checkedRemote || !this.foreground || !this.state.consent || !this.state.ready) return;
this.checkedRemote = true;
if (!version || !/^\d+\.\d+\.\d+/.test(version)) { this.state.notice = '被控端版本过低,请升级到最新版本。'; this.state.modal = 'upgrade'; return; }
if (!version || !/^\d+\.\d+\.\d+/.test(version)) { this.state.notice = tr("被控端版本过低,请升级到最新版本。"); this.state.modal = 'upgrade'; return; }
const request = ++this.versionRequest, connection = this.connection; this.remoteVersionRequest = request;
const body = await this.platform.version();
if (body && request === this.remoteVersionRequest && connection === this.connection && this.foreground && this.state.consent)
@@ -351,7 +352,7 @@ export class MobileController {
try {
this.state.documentText = name === 'PRIVACY.md' ? this.privacy : util.TextDecoder.create('utf-8').decodeWithStream(this.context.resourceManager.getRawFileContentSync(name));
this.state.documentTitle = title; this.state.documentBack = back; this.state.page = 'document'; this.state.modal = '';
} catch { this.toast('无法读取文档'); }
} catch { this.toast(tr("无法读取文档")); }
}
dismissPrivacyUnlock(): void {
this.privacyScreen.unlockNotice = false;
@@ -389,7 +390,7 @@ export class MobileController {
}
private poll(): void {
try { const events = JSON.parse(bridge.mobilePoll()) as NativeEvent[]; events.forEach((event: NativeEvent) => this.event(event)); }
catch { this.state.signalText = '原生通信库不可用,请重新构建并安装'; }
catch { this.state.signalText = tr("原生通信库不可用,请重新构建并安装"); }
if (++this.seconds % 20 === 0) {
if (this.state.ready) this.syncPrivacy();
this.state.networkTime = this.now(); this.presence.tick(this.state.networkTime); this.history();
@@ -401,13 +402,13 @@ export class MobileController {
}
if (e.type === 'identity' && e.identity) {
this.state.identity = e.identity.split('@')[0]; const scope = this.scope();
if (e.identity.includes('@')) this.store.saveSecret(`identity:${scope}`, e.identity).catch(() => this.toast('设备身份保存失败,请检查安全存储'));
if (e.identity.includes('@')) this.store.saveSecret(`identity:${scope}`, e.identity).catch(() => this.toast(tr("设备身份保存失败,请检查安全存储")));
this.joinIfReady(); this.configureInbox();
} else if (e.type === 'signal' && !e.controller) {
if (e.generation !== this.signalGeneration) { this.signalGeneration = e.generation ?? -1; this.presence.connect(false, this.now()); this.inbox.connect(false); }
this.state.signal = e.status ?? -1; this.state.identity = e.id || this.state.identity;
const labels = ['正在连接…', '已连接', '连接失败', '服务器已断开', '正在重连…', '服务器已断开', '服务器证书校验失败'];
this.state.signalText = labels[this.state.signal] ?? '未连接'; this.presence.connect(this.state.signal === 1, this.now()); this.configureInbox(); this.joinIfReady();
const labels = [tr("正在连接…"), tr("已连接"), tr("连接失败"), tr("服务器已断开"), tr("正在重连…"), tr("服务器已断开"), tr("服务器证书校验失败")];
this.state.signalText = labels[this.state.signal] ?? tr("未连接"); this.presence.connect(this.state.signal === 1, this.now()); this.configureInbox(); this.joinIfReady();
} else if (e.type === 'signalMessage' && e.generation === this.signalGeneration && e.message) {
const type = (e.message as SignalType).type;
if (type === 'announcements_changed') this.inbox.refresh();
@@ -417,10 +418,10 @@ export class MobileController {
else if (e.type === 'connection') {
if (e.status === 1 && !this.state.ready) this.connected();
else if (e.status === 6) {
this.end(''); this.state.remote = this.connectingId; this.state.notice = '访问密码错误,请重新输入'; this.state.modal = 'password';
this.end(''); this.state.remote = this.connectingId; this.state.notice = tr("访问密码错误,请重新输入"); this.state.modal = 'password';
} else if ((e.status ?? 0) >= 3) {
const reasons = ['', '', '', '连接已断开', '连接失败', '远端已关闭连接', '访问密码错误', '找不到该设备 ID', '远端当前不可用'];
this.end(reasons[e.status ?? 0] ?? '连接已结束');
const reasons = ['', '', '', tr("连接已断开"), tr("连接失败"), tr("远端已关闭连接"), tr("访问密码错误"), tr("找不到该设备 ID"), tr("远端当前不可用")];
this.end(reasons[e.status ?? 0] ?? tr("连接已结束"));
}
} else if (e.type === 'ended') this.end(e.reason ?? '');
else if (e.type === 'data' && e.message) this.data(e.message as ControlData);
@@ -437,7 +438,7 @@ export class MobileController {
else if (e.type === 'feedback') this.toast(e.text ?? '');
else if (e.type === 'file') {
if (e.sending && ((e.progress ?? 0) < 0 || (e.progress ?? 0) >= 1)) this.sendingFile = false;
this.state.transfer = (e.progress ?? 0) < 0 ? `${e.name} 传输失败` : `${e.sending ? '发送' : '接收'} ${e.name} · ${(e.progress ?? 0) >= 1 ? '已完成' : Math.floor((e.progress ?? 0) * 100) + '%'}`;
this.state.transfer = (e.progress ?? 0) < 0 ? tr("{0} 传输失败", [String(e.name)]) : `${e.sending ? '发送' : '接收'} ${e.name} · ${(e.progress ?? 0) >= 1 ? '已完成' : Math.floor((e.progress ?? 0) * 100) + '%'}`;
if (e.path) { this.state.receivedPath = e.path; this.state.receivedName = e.name ?? 'received-file'; }
} else if (e.type === 'privacyFailed') { this.privacyScreen.failed(Number(e.request)); this.syncPrivacy(); }
else if (e.type === 'settingsFailed') { this.video.expire(Number(e.request)); this.syncVideo(); }
@@ -448,14 +449,14 @@ export class MobileController {
} else if (e.path) this.store.discardRaw(e.path);
} else if (e.type === 'version') {
if (e.request === this.localVersionRequest && this.foreground && this.state.consent) {
if (e.ok) { this.state.update = e.latest ?? ''; this.state.updateStatus = this.state.update ? `发现新版本 ${this.state.update}` : '已是最新版本'; }
else this.state.updateStatus = '暂无鸿蒙版本信息,请稍后重试';
if (e.ok) { this.state.update = e.latest ?? ''; this.state.updateStatus = this.state.update ? tr("发现新版本 {0}", [String(this.state.update)]) : tr("已是最新版本"); }
else this.state.updateStatus = tr("暂无鸿蒙版本信息,请稍后重试");
} else if (e.request === this.remoteVersionRequest && e.ok && e.latest && this.foreground && this.state.ready) {
this.state.notice = '被控端版本过低,请升级到最新版本。'; this.state.modal = 'upgrade';
this.state.notice = tr("被控端版本过低,请升级到最新版本。"); this.state.modal = 'upgrade';
}
} else if (e.type === 'error') {
this.state.signalText = e.text ?? '连接失败'; if (this.state.connecting) { this.end(e.text ?? '连接失败'); this.stopSignal(); }
else if (!this.state.ready) { const text = e.text ?? '操作失败'; this.stopSignal(); this.state.signalText = text; } else this.toast(e.text ?? '操作失败');
this.state.signalText = e.text ?? tr("连接失败"); if (this.state.connecting) { this.end(e.text ?? tr("连接失败")); this.stopSignal(); }
else if (!this.state.ready) { const text = e.text ?? tr("操作失败"); this.stopSignal(); this.state.signalText = text; } else this.toast(e.text ?? tr("操作失败"));
}
}
private configureInbox(): void {
@@ -474,7 +475,7 @@ export class MobileController {
if (this.state.hostName) this.store.metadata(scope, device.id, this.state.hostName, this.state.platform);
this.history();
}
}).catch(() => this.toast('密码保存失败,本次连接仍可使用'));
}).catch(() => this.toast(tr("密码保存失败,本次连接仍可使用")));
this.platform.connected(this.state.autoLandscape);
if (this.video.supported && this.video.requestId === 0) this.updateVideo(0, this.video.selection.quality);
if (this.receivedHostInfo) this.checkRemote(this.remoteVersion);
@@ -1,3 +1,4 @@
import { tr } from './Localization';
import { deviceInfo } from '@kit.BasicServicesKit';
import { common, wantAgent } from '@kit.AbilityKit';
import { PiPWindow, window, display } from '@kit.ArkUI';
@@ -34,7 +35,7 @@ export class MobilePlatform {
private feedback: (message: string) => void;
private backgroundCancelled: () => void = () => {
this.backgroundActive = false;
if (this.active && !this.foreground && !this.pipActive && !this.choosing) this.ended('系统已停止后台会话');
if (this.active && !this.foreground && !this.pipActive && !this.choosing) this.ended(tr("系统已停止后台会话"));
};
constructor(context: common.UIAbilityContext, ended: (reason: string) => void, release: () => void, feedback: (message: string) => void) {
this.context = context; this.ended = ended; this.release = release; this.feedback = feedback;
@@ -82,7 +83,7 @@ export class MobilePlatform {
if (state === PiPWindow.PiPState.STOPPED) {
this.pipActive = false;
this.onVisibility(this.foreground);
if (!this.foreground && !this.choosing && this.active) this.ended('画中画已关闭');
if (!this.foreground && !this.choosing && this.active) this.ended(tr("画中画已关闭"));
}
});
} catch { /* A continuous data-transfer task remains the fallback. */ }
@@ -104,7 +105,7 @@ export class MobilePlatform {
}
await this.backgroundQueue;
if (generation === this.generation && !this.foreground && this.active && !this.backgroundActive && !this.pipActive && !this.choosing)
this.ended('系统未允许后台会话,连接已断开');
this.ended(tr("系统未允许后台会话,连接已断开"));
}
resume(): void { this.foreground = true; this.dismissPiP(); this.orientation(this.active); this.onVisibility(true); }
private async dismissPiP(): Promise<void> {
@@ -164,7 +165,7 @@ export class MobilePlatform {
try { await fs.copyFile(input.fd, path); } finally { await fs.close(input); }
if (generation !== this.generation || !this.active) { await fs.unlink(path); return undefined; }
return { path: path, name: name };
} catch { if (path) try { await fs.unlink(path); } catch {} this.feedback('无法读取所选文件或存储空间不足'); return undefined; }
} catch { if (path) try { await fs.unlink(path); } catch {} this.feedback(tr("无法读取所选文件或存储空间不足")); return undefined; }
finally { if (generation === this.generation) this.setChoosing(false); }
}
async save(path: string, name: string): Promise<void> {
@@ -177,8 +178,8 @@ export class MobilePlatform {
if (!uris.length || generation !== this.generation || !this.active) return;
const output = await fs.open(uris[0], fs.OpenMode.WRITE_ONLY | fs.OpenMode.TRUNC);
try { await fs.copyFile(path, output.fd); } finally { await fs.close(output); }
if (generation === this.generation) this.feedback('文件已保存');
} catch { if (generation === this.generation) this.feedback('文件保存失败,请重试'); }
if (generation === this.generation) this.feedback(tr("文件已保存"));
} catch { if (generation === this.generation) this.feedback(tr("文件保存失败,请重试")); }
finally { if (generation === this.generation) this.setChoosing(false); }
}
async version(): Promise<string | undefined> {
@@ -196,7 +197,7 @@ export class MobilePlatform {
cancelRequests(): void { for (const request of this.requests) request.destroy(); this.requests = []; }
openLink(url: string): void {
if (!isAnnouncementURL(url)) return;
this.context.startAbility({ action: 'ohos.want.action.viewData', uri: url }).catch(() => this.feedback('无法打开网页'));
this.context.startAbility({ action: 'ohos.want.action.viewData', uri: url }).catch(() => this.feedback(tr("无法打开网页")));
}
}
export interface PreparedFile { path: string; name: string; }
@@ -1,3 +1,4 @@
import { tr } from './Localization';
import { MobileController, MobileState } from './MobileController';
import { RemoteViewport, MouseInput, TouchInput, TouchPoint, clamp, NetworkStatistics, Traffic, asciiKey } from './ControllerModels';
import { MobileTheme, MobileSegment, MobileIcon, MobileIconButton } from './MobileWidgets';
@@ -136,7 +137,7 @@ export struct MobileRemoteView {
if (index === 10) this.controller?.togglePrivacy();
}
private labels(): string[] {
return ['键盘', this.state.relative ? '相对鼠标' : '绝对鼠标', this.mouseVisible ? '收起鼠标' : '虚拟鼠标', '显示器', '画面设置', this.state.muted ? '静音' : '声音', '发送文件', '网络状态', 'Ctrl+Alt+Del', '剪贴板', this.state.privacyButtonTitle];
return [tr("键盘"), this.state.relative ? tr("相对鼠标") : tr("绝对鼠标"), this.mouseVisible ? tr("收起鼠标") : tr("虚拟鼠标"), tr("显示器"), tr("画面设置"), this.state.muted ? tr("静音") : tr("声音"), tr("发送文件"), tr("网络状态"), 'Ctrl+Alt+Del', tr("剪贴板"), this.state.privacyButtonTitle];
}
private controlIds(): number[] {
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter((index: number) =>
@@ -155,22 +156,33 @@ export struct MobileRemoteView {
return { x: clamp(x, 8, this.viewWidth - 148), y: clamp(y, 8, this.viewHeight - 56) };
}
private keyboardWidth(): number { return Math.max(1, Math.min(this.computerKeyboard ? 520 : 440, this.viewWidth - 16)); }
private keyboardTitle(key: string): string {
switch (key) {
case 'copy': return tr("复制");
case 'paste': return tr("粘贴");
case 'select_all': return tr("全选");
case 'space': return tr("空格");
case 'computer': return tr("电脑");
case 'clipboard': return tr("剪贴板");
default: return this.shift && key.length === 1 ? key.toUpperCase() : key;
}
}
private keyboardRows(): KeyRow[] {
return this.computerKeyboard ? [
{ keys: ['Esc', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6'] }, { keys: ['Tab', 'Ctrl', 'Alt', 'Win', '↑', '⌫'] },
{ keys: ['复制', '粘贴', '全选', '←', '↓', '→'] }, { keys: ['ABC', '空格', 'Enter'] }
{ keys: ['copy', 'paste', 'select_all', '←', '↓', '→'] }, { keys: ['ABC', 'space', 'Enter'] }
] : [{ keys: ['1','2','3','4','5','6','7','8','9','0'] }, { keys: ['q','w','e','r','t','y','u','i','o','p'] },
{ keys: ['a','s','d','f','g','h','j','k','l'] }, { keys: ['⇧','z','x','c','v','b','n','m','⌫'] },
{ keys: ['电脑','剪贴板','空格','.','Enter'] }];
{ keys: ['computer','clipboard','space','.','Enter'] }];
}
private keyboardKey(value: string): void {
if (value === '⇧') { this.shift = !this.shift; return; }
if (value === '电脑' || value === 'ABC') { this.computerKeyboard = value === '电脑'; return; }
if (value === '剪贴板') { this.state.modal = 'clipboard'; return; }
if (value === 'computer' || value === 'ABC') { this.computerKeyboard = value === 'computer'; return; }
if (value === 'clipboard') { this.state.modal = 'clipboard'; return; }
const modifier = value === 'Ctrl' ? 17 : value === 'Alt' ? 18 : value === 'Win' ? 91 : 0;
if (modifier) { this.modifiers = this.modifiers.includes(modifier) ? this.modifiers.filter((m: number) => m !== modifier) : [...this.modifiers, modifier]; return; }
if (value === '复制' || value === '粘贴' || value === '全选') { this.controller?.shortcut(value === '复制' ? 67 : value === '粘贴' ? 86 : 65); return; }
const names = ['空格','⌫','Enter','Esc','Tab','↑','↓','←','→']; const codes = [32,8,13,27,9,38,40,37,39];
if (value === 'copy' || value === 'paste' || value === 'select_all') { this.controller?.shortcut(value === 'copy' ? 67 : value === 'paste' ? 86 : 65); return; }
const names = ['space','⌫','Enter','Esc','Tab','↑','↓','←','→']; const codes = [32,8,13,27,9,38,40,37,39];
let code = names.includes(value) ? codes[names.indexOf(value)] : /^F[1-6]$/.test(value) ? 111 + Number(value.substring(1)) : 0;
if (!code && this.modifiers.length) { const key = asciiKey(this.shift ? value.toUpperCase() : value); code = key & 255; if (key & 256) this.modifiers = [...this.modifiers, 16]; }
if (code) { this.controller?.chord(code, this.modifiers); this.modifiers = []; }
@@ -183,11 +195,11 @@ export struct MobileRemoteView {
@Builder
NetworkRow(title: string, traffic: Traffic | undefined) {
Row({ space: 4 }) {
Text(title).fontSize(11).fontWeight(title === '合计' ? 600 : 400).fontColor(MobileTheme.ink).width(28)
Text(title).fontSize(11).minFontSize(8).maxFontSize(11).maxLines(1).fontWeight(title === tr("合计") ? 600 : 400).fontColor(MobileTheme.ink).width(48)
Text(NetworkStatistics.rate(traffic?.inbound ?? -1)).fontSize(11).minFontSize(8).maxLines(1).layoutWeight(1).textAlign(TextAlign.End)
Text(NetworkStatistics.rate(traffic?.outbound ?? -1)).fontSize(11).minFontSize(8).maxLines(1).layoutWeight(1).textAlign(TextAlign.End)
Text(NetworkStatistics.loss(traffic?.loss ?? -1)).fontSize(11).width(46).textAlign(TextAlign.End)
}.padding({ left: 4, right: 4 }).height(26).width('100%').borderRadius(5).backgroundColor(title === '合计' ? '#122E7ADB' : Color.Transparent)
}.padding({ left: 4, right: 4 }).height(26).width('100%').borderRadius(5).backgroundColor(title === tr("合计") ? '#122E7ADB' : Color.Transparent)
}
@Builder
NetworkDetail(label: string, value: string, encrypted: boolean = false) {
@@ -210,12 +222,12 @@ export struct MobileRemoteView {
Menu() {
Column({ space: 10 }) {
Row({ space: 6 }) {
if (this.menu !== 'controls') { MobileIconButton({ icon: 'back', label: '返回控制栏', iconExtent: 30, iconSize: 19, action: () => this.menu = 'controls' }) }
if (this.menu !== 'controls') { MobileIconButton({ icon: 'back', label: tr("返回控制栏"), iconExtent: 30, iconSize: 19, action: () => this.menu = 'controls' }) }
Row({ space: 6 }) {
if (this.menu === 'controls') { Row() {}.width(6).height(6).borderRadius(3).backgroundColor(MobileTheme.green) }
Text(this.menu === 'controls' ? '已连接' : this.menu === 'video' ? '画面设置' : '网络状态').fontSize(14).fontWeight(600).fontColor(MobileTheme.ink)
Text(this.menu === 'controls' ? tr("已连接") : this.menu === 'video' ? tr("画面设置") : tr("网络状态")).fontSize(14).fontWeight(600).fontColor(MobileTheme.ink)
}.layoutWeight(1)
MobileIconButton({ icon: 'close', label: '关闭控制栏', iconExtent: 30, iconSize: 20, action: () => this.menu = '' })
MobileIconButton({ icon: 'close', label: tr("关闭控制栏"), iconExtent: 30, iconSize: 20, action: () => this.menu = '' })
}.width('100%')
Scroll() {
Column({ space: 8 }) {
@@ -225,7 +237,7 @@ export struct MobileRemoteView {
GridCol() {
Column({ space: 4 }) {
MobileIcon({ name: this.icons()[index], iconExtent: 19 })
Text(this.labels()[index]).fontSize(10).fontColor(MobileTheme.ink).maxLines(1)
Text(this.labels()[index]).fontSize(10).minFontSize(8).maxFontSize(10).fontColor(MobileTheme.ink).maxLines(2).textAlign(TextAlign.Center)
}
.height(52).width('100%').justifyContent(FlexAlign.Center)
.backgroundColor('#F0F0F2').borderRadius(10)
@@ -238,44 +250,44 @@ export struct MobileRemoteView {
}
if (this.state.privacyFeedback) { Text(this.state.privacyFeedback).fontSize(11).fontColor(MobileTheme.secondary).width('100%') }
if (this.state.transfer) { Text(this.state.transfer).fontSize(11).fontColor(MobileTheme.secondary).width('100%') }
if (this.state.receivedPath) { Button('保存收到的文件', { type: ButtonType.Normal }).fontSize(14).fontColor(MobileTheme.blue).backgroundColor(Color.White).height(42).width('100%').onClick(() => this.controller?.saveFile()) }
if (this.state.receivedPath) { Button(tr("保存收到的文件"), { type: ButtonType.Normal }).fontSize(14).fontColor(MobileTheme.blue).backgroundColor(Color.White).height(42).width('100%').onClick(() => this.controller?.saveFile()) }
} else if (this.menu === 'video') {
Text(this.state.videoFeedback).fontSize(12).fontColor(MobileTheme.secondary).width('100%')
Text('画面质量').fontSize(12).width('100%')
MobileSegment({ labels: ['低', '中', '高'], selection: this.state.videoQuality,
Text(tr("画面质量")).fontSize(12).width('100%')
MobileSegment({ labels: [tr("低"), tr("中"), tr("高")], selection: this.state.videoQuality,
available: this.state.videoSupported, changed: (i: number) => this.controller?.updateVideo(0, i) })
Text('画面采集帧率').fontSize(12).width('100%')
Text(tr("画面采集帧率")).fontSize(12).width('100%')
MobileSegment({ labels: ['30 fps', '60 fps'], selection: this.state.videoRate === 60 ? 1 : 0,
available: this.state.videoSupported, changed: (i: number) => this.controller?.updateVideo(1, i ? 60 : 30) })
Text('画面偏好').fontSize(12).width('100%')
MobileSegment({ labels: ['帧率优先', '画质优先', '平衡'], selection: this.state.videoSelectedPreference,
Text(tr("画面偏好")).fontSize(12).width('100%')
MobileSegment({ labels: [tr("帧率优先"), tr("画质优先"), tr("平衡")], selection: this.state.videoSelectedPreference,
available: this.state.videoSupported, changed: (i: number) => this.controller?.updateVideo(2, i) })
} else {
Row({ space: 4 }) {
Text('').width(28)
Text('接收').fontSize(11).fontColor(MobileTheme.secondary).layoutWeight(1).textAlign(TextAlign.End)
Text('发送').fontSize(11).fontColor(MobileTheme.secondary).layoutWeight(1).textAlign(TextAlign.End)
Text('丢包率').fontSize(11).fontColor(MobileTheme.secondary).width(46).textAlign(TextAlign.End)
Text('').width(48)
Text(tr("接收")).fontSize(11).fontColor(MobileTheme.secondary).layoutWeight(1).textAlign(TextAlign.End)
Text(tr("发送")).fontSize(11).fontColor(MobileTheme.secondary).layoutWeight(1).textAlign(TextAlign.End)
Text(tr("丢包率")).fontSize(11).minFontSize(8).maxFontSize(11).maxLines(2).fontColor(MobileTheme.secondary).width(46).textAlign(TextAlign.End)
}.padding({ left: 4, right: 4 }).height(24).width('100%')
Column() {
this.NetworkRow('视频', this.controller?.network.current(this.state.networkTime)?.video)
this.NetworkRow('音频', this.controller?.network.current(this.state.networkTime)?.audio)
this.NetworkRow('数据', this.controller?.network.current(this.state.networkTime)?.data)
this.NetworkRow('合计', this.controller?.network.current(this.state.networkTime)?.total)
this.NetworkRow(tr("视频"), this.controller?.network.current(this.state.networkTime)?.video)
this.NetworkRow(tr("音频"), this.controller?.network.current(this.state.networkTime)?.audio)
this.NetworkRow(tr("数据"), this.controller?.network.current(this.state.networkTime)?.data)
this.NetworkRow(tr("合计"), this.controller?.network.current(this.state.networkTime)?.total)
}.width('100%')
Divider().strokeWidth(0.5).color(MobileTheme.line).margin({ top: 2, bottom: 2 })
this.NetworkDetail('帧率', `${this.controller?.network.fps(this.state.networkTime) ?? '—'} FPS`)
this.NetworkDetail('分辨率', this.state.hasFrame ? `${this.state.width} × ${this.state.height}` : '—')
this.NetworkDetail('画面延时', NetworkStatistics.delay(this.controller?.network.latency(this.state.networkTime) ?? -1))
this.NetworkDetail('连接延时(RTT)', NetworkStatistics.delay(this.state.networkTime - (this.controller?.network.rttTime ?? 0) < 3000 ? this.controller?.network.rtt ?? -1 : -1))
this.NetworkDetail('连接方式', this.controller?.network.mode(this.state.networkTime) ?? '—')
this.NetworkDetail('媒体加密', this.controller?.network.current(this.state.networkTime) === undefined ? '—' : this.controller?.network.report?.srtp ? 'SRTP 已启用' : '未启用', this.controller?.network.current(this.state.networkTime)?.srtp === true)
this.NetworkCodec('被控端编码器', this.controller?.network.remoteEncoder || '—')
this.NetworkCodec('控制端解码器', this.controller?.network.report?.decoder || '—')
this.NetworkDetail(tr("帧率"), `${this.controller?.network.fps(this.state.networkTime) ?? '—'} FPS`)
this.NetworkDetail(tr("分辨率"), this.state.hasFrame ? `${this.state.width} × ${this.state.height}` : '—')
this.NetworkDetail(tr("画面延时"), NetworkStatistics.delay(this.controller?.network.latency(this.state.networkTime) ?? -1))
this.NetworkDetail(tr("连接延时(RTT)"), NetworkStatistics.delay(this.state.networkTime - (this.controller?.network.rttTime ?? 0) < 3000 ? this.controller?.network.rtt ?? -1 : -1))
this.NetworkDetail(tr("连接方式"), this.controller?.network.mode(this.state.networkTime) ?? '—')
this.NetworkDetail(tr("媒体加密"), this.controller?.network.current(this.state.networkTime) === undefined ? '—' : this.controller?.network.report?.srtp ? tr("SRTP 已启用") : tr("未启用"), this.controller?.network.current(this.state.networkTime)?.srtp === true)
this.NetworkCodec(tr("被控端编码器"), this.controller?.network.remoteEncoder || '—')
this.NetworkCodec(tr("控制端解码器"), this.controller?.network.report?.decoder || '—')
}
}.width('100%')
}.constraintSize({ maxHeight: Math.max(48, this.viewHeight - 132) }).width('100%').scrollBar(BarState.Off)
Button('断开连接', { type: ButtonType.Normal }).fontSize(14).fontColor(Color.White).width('100%').height(34).borderRadius(8).backgroundColor(MobileTheme.red)
Button(tr("断开连接"), { type: ButtonType.Normal }).fontSize(14).fontColor(Color.White).width('100%').height(34).borderRadius(8).backgroundColor(MobileTheme.red)
.onClick(() => { this.release(); this.state.modal = 'disconnect'; this.menu = ''; this.keyboard = false; })
}.padding(12).width(this.menuWidth()).backgroundColor(Color.White).borderRadius(18)
.border({ width: 0.5, color: MobileTheme.line }).shadow({ radius: 24, color: '#33000000', offsetY: 6 })
@@ -286,21 +298,21 @@ export struct MobileRemoteView {
Keyboard() {
Column({ space: 3 }) {
Row() {
Stack() { Row() {}.width(32).height(4).backgroundColor(MobileTheme.secondary).borderRadius(2) }.layoutWeight(1).height(28).accessibilityText('拖动键盘')
Stack() { Row() {}.width(32).height(4).backgroundColor(MobileTheme.secondary).borderRadius(2) }.layoutWeight(1).height(28).accessibilityText(tr("拖动键盘"))
.gesture(PanGesture().onActionStart(() => { this.keyboardOriginX = this.keyboardX; this.keyboardOriginY = this.keyboardY; })
.onActionUpdate((e: GestureEvent) => this.keyboardDrag(e)))
MobileIconButton({ icon: 'close', label: '收起键盘', iconExtent: 28, iconSize: 18, action: () => { this.keyboard = false; this.modifiers = []; } })
MobileIconButton({ icon: 'close', label: tr("收起键盘"), iconExtent: 28, iconSize: 18, action: () => { this.keyboard = false; this.modifiers = []; } })
}.width('100%')
Scroll() {
Column({ space: 3 }) {
ForEach(this.keyboardRows(), (row: KeyRow) => {
Row({ space: 3 }) {
ForEach(row.keys, (key: string) => {
Text(this.shift && key.length === 1 ? key.toUpperCase() : key).fontSize(key.length > 2 ? 11 : 14)
.height(31).layoutWeight(key === '空格' ? 3 : 1).textAlign(TextAlign.Center).borderRadius(5).fontColor(MobileTheme.ink)
Text(this.keyboardTitle(key)).fontSize(key.length > 2 ? 11 : 14).minFontSize(8).maxFontSize(key.length > 2 ? 11 : 14).maxLines(1)
.height(31).layoutWeight(key === 'space' ? 3 : 1).textAlign(TextAlign.Center).borderRadius(5).fontColor(MobileTheme.ink)
.shadow({ radius: 0.5, color: '#33000000', offsetY: 1 })
.backgroundColor((key === '⇧' && this.shift) || this.modifiers.includes(key === 'Ctrl' ? 17 : key === 'Alt' ? 18 : key === 'Win' ? 91 : -1) ? '#9BC7F7' : key.length > 1 || key === '⇧' ? '#AFB8C5' : '#FFFFFF')
.accessibilityText(key).accessibilityLevel('yes').onClick(() => this.keyboardKey(key))
.accessibilityText(this.keyboardTitle(key)).accessibilityLevel('yes').onClick(() => this.keyboardKey(key))
}, (key: string) => key)
}.width('100%')
})
@@ -324,7 +336,7 @@ export struct MobileRemoteView {
} else { MobileIcon({ name: button === 0 ? 'mouse_left' : 'mouse_right', iconExtent: 22 }) }
}.width(44).height(44).borderRadius(7).clip(true)
.backgroundColor(button === 0 && this.leftHeld || button === 2 && this.rightHeld ? '#9BC7F7' : '#E9E9EE')
.accessibilityText(button === 0 ? '鼠标左键,按住拖动' : button === 1 ? '滚轮,上下滑动' : '鼠标右键,按住拖动')
.accessibilityText(button === 0 ? tr("鼠标左键,按住拖动") : button === 1 ? tr("滚轮,上下滑动") : tr("鼠标右键,按住拖动"))
.onTouch((event: TouchEvent) => this.barTouch(event, button))
})
}.padding(2).width(140).height(48).backgroundColor('#FFFFFF').borderRadius(10)
@@ -339,7 +351,7 @@ export struct MobileRemoteView {
if (!this.state.hasFrame) {
Column({ space: 12 }) {
LoadingProgress().width(32).height(32).color(Color.White)
Text('等待远端画面…').fontSize(14).fontColor(Color.White)
Text(tr("等待远端画面…")).fontSize(14).fontColor(Color.White)
}.padding(20).constraintSize({ minWidth: 200 }).backgroundColor('#B0202024').borderRadius(14)
.position({ x: (this.viewWidth - 200) / 2, y: (this.viewHeight - 104) / 2 }).hitTestBehavior(HitTestMode.None)
}
@@ -348,7 +360,7 @@ export struct MobileRemoteView {
.onAxisEvent((event: AxisEvent) => { const delta = event.getVerticalAxisValue(); if (delta) this.mouse.scroll(-delta); })
.onKeyEvent((e: KeyEvent) => { if (e.type === KeyType.Down || e.type === KeyType.Up) { this.controller?.physicalKey(e.keyCode, e.type === KeyType.Down); return true; } return false; })
.onBlur(() => this.controller?.release())
.accessibilityText('远程桌面:轻触点击、长按右键、双指捏合缩放、放大后双指拖动画面,原始比例下双指滚动')
.accessibilityText(tr("远程桌面:轻触点击、长按右键、双指捏合缩放、放大后双指拖动画面,原始比例下双指滚动"))
MobileIcon({ name: 'cursor', iconExtent: 24, color: '#000000' })
.position({ x: this.cursorX - 1, y: this.cursorY - 1 }).hitTestBehavior(HitTestMode.None)
if (this.menu) { Column() {}.width('100%').height('100%').onClick(() => this.menu = ''); this.Menu() }
@@ -357,7 +369,7 @@ export struct MobileRemoteView {
Stack() {
Image($r('app.media.mobile_brand')).width(46).height(46).objectFit(ImageFit.Cover).borderRadius(23).draggable(false).hitTestBehavior(HitTestMode.None)
}.width(54).height(54).backgroundColor('#F5F5F7').borderRadius(27).shadow({ radius: 14, color: '#33000000', offsetY: 4 })
.position({ x: this.orbX, y: this.orbY }).accessibilityText('远程控制菜单,拖动可移动').accessibilityLevel('yes')
.position({ x: this.orbX, y: this.orbY }).accessibilityText(tr("远程控制菜单,拖动可移动")).accessibilityLevel('yes')
.onTouch((e: TouchEvent) => this.orbTouch(e))
}.width('100%').height('100%').backgroundColor(Color.Black).clip(true)
.onAreaChange((_old: Area, next: Area) => {
@@ -1,3 +1,4 @@
import { tr } from './Localization';
import { common } from '@kit.AbilityKit';
import { preferences } from '@kit.ArkData';
import { asset } from '@kit.AssetStoreKit';
@@ -42,7 +43,7 @@ export class MobileStore {
return value instanceof Uint8Array ? util.TextDecoder.create('utf-8').decodeWithStream(value) : '';
} catch (error) {
if ((error as ErrorWithCode).code === asset.ErrorCode.NOT_FOUND) return '';
throw new Error('安全存储不可用,请解锁设备后重试');
throw new Error(tr("安全存储不可用,请解锁设备后重试"));
}
}
async saveSecret(key: string, value: string): Promise<void> {
@@ -56,7 +57,7 @@ export class MobileStore {
}
async removeSecret(key: string): Promise<void> {
try { await asset.remove(await this.query(key)); }
catch (error) { if ((error as ErrorWithCode).code !== asset.ErrorCode.NOT_FOUND) throw new Error('无法删除已保存的凭据'); }
catch (error) { if ((error as ErrorWithCode).code !== asset.ErrorCode.NOT_FOUND) throw new Error(tr("无法删除已保存的凭据")); }
}
recent(scope: string): RecentDevice[] {
try {
@@ -1,3 +1,4 @@
import { tr } from './Localization';
import { LengthMetricsUnit } from '@kit.ArkUI';
export class MobileTheme {
@@ -86,7 +87,7 @@ export struct MobileIconButton {
}
@Component
export struct MobilePrimaryButton {
label: string = '连接';
label: string = tr("连接");
@Prop available: boolean = true;
arrow: boolean = false;
buttonHeight: number = 48;
@@ -94,7 +95,7 @@ export struct MobilePrimaryButton {
build() {
Button({ type: ButtonType.Normal, stateEffect: true }) {
Row({ space: 8 }) {
Text(this.label).fontSize(16).fontWeight(600).fontColor(Color.White)
Text(this.label).fontSize(16).minFontSize(11).maxFontSize(16).maxLines(2).textAlign(TextAlign.Center).fontWeight(600).fontColor(Color.White)
if (this.arrow) { MobileIcon({ name: 'arrow', iconExtent: 18, color: '#FFFFFF' }) }
}.justifyContent(FlexAlign.Center).width('100%')
}.height(this.buttonHeight).width('100%').padding({ left: 12, right: 12 }).borderRadius(12)
@@ -118,7 +119,7 @@ export struct MobileSegment {
.backgroundColor(Color.White).borderRadius(7).shadow({ radius: 2, color: '#18000000', offsetY: 1 }).translate({ x: 2 + this.selected * Math.max(0, this.viewWidth - 4) / Math.max(1, this.labels.length) })
Row() {
ForEach(this.labels, (label: string, index: number) => {
Text(label).fontSize(12).fontWeight(this.selection === index ? 600 : 400).fontColor(MobileTheme.ink)
Text(label).fontSize(12).minFontSize(9).maxFontSize(12).maxLines(2).fontWeight(this.selection === index ? 600 : 400).fontColor(MobileTheme.ink)
.textAlign(TextAlign.Center).height(32).layoutWeight(1).enabled(this.available)
.accessibilityLevel('yes').accessibilityText(label).accessibilitySelected(this.selection === index)
.onClick(() => this.changed(index))
@@ -1,3 +1,4 @@
import { tr } from './Localization';
export interface PrivacyStatus {
state: number; supported: boolean; input_block_supported: boolean;
overlay_active: boolean; input_blocked: boolean; remote_paused: boolean;
@@ -44,10 +45,10 @@ export class RemotePrivacyScreen {
return s !== undefined && this.fresh(now) && s.state === 3 && s.overlay_active && !s.remote_paused;
}
buttonTitle(now: number): string {
if (this.enabled(now)) return '隐私屏已开启';
if (this.enabled(now)) return tr("隐私屏已开启");
const s = this.snapshot;
if (this.fresh(now) && s?.supported && s.state === 1 && !this.active()) return '隐私屏已关闭';
return '隐私屏';
if (this.fresh(now) && s?.supported && s.state === 1 && !this.active()) return tr("隐私屏已关闭");
return tr("隐私屏");
}
feedback(now: number): string {
if (!this.desktopAvailable) return '';
@@ -73,26 +74,26 @@ export class RemotePrivacyScreen {
return this.requestId;
}
failed(id: number): void {
if (this.pending && this.requestId === id) { this.pending = false; this.failure = '指令发送失败,请重试'; }
if (this.pending && this.requestId === id) { this.pending = false; this.failure = tr("指令发送失败,请重试"); }
}
tick(now: number): void {
if (this.pending && now - this.requestedAt >= 15000) {
this.pending = false; this.failure = '请求超时,尚未确认电脑的隐私屏状态';
this.pending = false; this.failure = tr("请求超时,尚未确认电脑的隐私屏状态");
}
}
status(now: number): string {
if (this.failure) return this.failure;
const s = this.snapshot;
if (!s) return '等待电脑返回隐私屏状态;旧版电脑端可能不支持';
if (!this.fresh(now)) return '隐私屏状态已过期,等待电脑确认';
if (s.state === 2) return '正在开启隐私屏…';
if (s.state === 4) return '正在关闭隐私屏…';
if (this.pending) return '指令已发送,等待电脑确认…';
if (s.state === 5) return '隐私屏异常';
if (!s.supported || s.state === 0) return '隐私屏暂不可用';
if (!s) return tr("等待电脑返回隐私屏状态;旧版电脑端可能不支持");
if (!this.fresh(now)) return tr("隐私屏状态已过期,等待电脑确认");
if (s.state === 2) return tr("正在开启隐私屏…");
if (s.state === 4) return tr("正在关闭隐私屏…");
if (this.pending) return tr("指令已发送,等待电脑确认…");
if (s.state === 5) return tr("隐私屏异常");
if (!s.supported || s.state === 0) return tr("隐私屏暂不可用");
if (s.state === 3 && s.overlay_active && !s.remote_paused)
return '隐私屏已开启';
return s.state === 1 && !this.active() ? '隐私屏已关闭' : '隐私屏状态待确认';
return tr("隐私屏已开启");
return s.state === 1 && !this.active() ? tr("隐私屏已关闭") : tr("隐私屏状态待确认");
}
detail(now: number): string {
const s = this.snapshot;
+116 -113
View File
@@ -1,3 +1,4 @@
import { tr, currentLanguage } from '../mobile/Localization';
import { common } from '@kit.AbilityKit';
import { MobileController, MobileState, RemoteDisplay } from '../mobile/MobileController';
import { MobileRemoteView } from '../mobile/MobileRemoteView';
@@ -69,9 +70,9 @@ struct Mobile {
}
private title(): string {
switch (this.state.page) {
case 'settings': return '设置'; case 'privacy': return '隐私与授权';
case 'about': return '关于 CrossDesk'; case 'licenses': return '开源组件'; case 'component': return this.component?.name ?? '';
case 'source': return '源码与构建说明'; case 'announcements': return '通知公告'; case 'announcement': return '公告详情';
case 'settings': return tr("设置"); case 'privacy': return tr("隐私与授权");
case 'about': return tr("关于 CrossDesk"); case 'licenses': return tr("开源组件"); case 'component': return this.component?.name ?? '';
case 'source': return tr("源码与构建说明"); case 'announcements': return tr("通知公告"); case 'announcement': return tr("公告详情");
case 'document': return this.state.documentTitle; default: return '';
}
}
@@ -80,7 +81,9 @@ struct Mobile {
private policyParagraphs(body: string): string[] {
const text = body.replace(/\r\n/g, '\n');
const start = text.indexOf('## 中文\n'), end = start < 0 ? -1 : text.indexOf('## English', start);
const localized = start >= 0 && end > start ? text.substring(start + '## 中文\n'.length, end) : text;
const chinese = currentLanguage() === 0 || currentLanguage() === 3;
const localized = chinese && start >= 0 && end > start ? text.substring(start + '## 中文\n'.length, end) :
!chinese && end >= 0 ? text.substring(end + '## English'.length) : text;
return localized.split(/\n\s*\n/).map((paragraph: string) => paragraph.trim()).filter((paragraph: string) => !!paragraph && paragraph !== '---');
}
private openAnnouncement(item: AnnouncementItem): void { this.controller?.inbox.markRead(item); this.state.announcement = item; this.navigate('announcement'); }
@@ -141,16 +144,16 @@ struct Mobile {
@Builder
ConnectionCard() {
Column({ space: 16 }) {
Text('远程桌面').fontSize(20).fontWeight(600).fontColor(MobileTheme.ink).width('100%')
Text(tr("远程桌面")).fontSize(20).fontWeight(600).fontColor(MobileTheme.ink).width('100%')
Row({ space: 10 }) {
TextInput({ text: this.formatId(this.state.remote), placeholder: '对端 ID' }).type(InputType.Number).maxLength(11)
TextInput({ text: this.formatId(this.state.remote), placeholder: tr("对端 ID") }).type(InputType.Number).maxLength(11)
.fontSize(20).fontWeight(500).fontColor(MobileTheme.ink).placeholderColor(MobileTheme.secondary)
.height(54).layoutWeight(1).backgroundColor(MobileTheme.background).borderRadius(12)
.border({ width: this.idFocused ? 2 : 1, color: this.idFocused ? MobileTheme.blue : MobileTheme.line })
.padding({ left: 14, right: 14 }).onFocus(() => this.idFocused = true).onBlur(() => this.idFocused = false)
.onChange((value: string) => this.state.remote = this.deviceId(value)).onSubmit(() => this.controller?.requestConnection(this.state.remote))
Column() {
MobilePrimaryButton({ label: '连接', buttonHeight: 54, available: this.state.remote.length > 0,
MobilePrimaryButton({ label: tr("连接"), buttonHeight: 54, available: this.state.remote.length > 0,
action: () => this.controller?.requestConnection(this.state.remote) })
}.width(96)
}.width('100%')
@@ -161,7 +164,7 @@ struct Mobile {
RecentCard(device: RecentDevice) {
Column() {
Stack() {
Text(device.platform === 'windows' ? 'Windows' : device.platform === 'macos' ? 'macOS' : device.platform === 'linux' ? 'Linux' : '远程电脑')
Text(device.platform === 'windows' ? 'Windows' : device.platform === 'macos' ? 'macOS' : device.platform === 'linux' ? 'Linux' : tr("远程电脑"))
.fontSize(19).fontWeight(600).fontColor(Color.White).width('100%').height('100%').textAlign(TextAlign.Center)
if (this.state.previews && device.preview) { Image(`file://${device.preview}`).objectFit(ImageFit.Cover).width('100%').height('100%') }
}.aspectRatio(16 / 9).width('100%').backgroundColor(device.platform === 'windows' ? '#316AB7' : device.platform === 'macos' ? '#8057B9' : device.platform === 'linux' ? '#AE642B' : '#677587')
@@ -169,8 +172,8 @@ struct Mobile {
Text(device.name || device.id).fontSize(12).fontWeight(600).maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis }).width('100%')
Row({ space: 4 }) {
Text(`ID ${device.id}`).fontSize(10).fontColor(MobileTheme.secondary).layoutWeight(1)
if (device.remember) { MobileIcon({ name: 'key', iconExtent: 11, color: MobileTheme.secondary }).accessibilityText('已保存密码') }
Text(this.controller?.presence.online(device.id, this.controller?.now() ?? 0) ? '在线' : '离线').fontSize(10)
if (device.remember) { MobileIcon({ name: 'key', iconExtent: 11, color: MobileTheme.secondary }).accessibilityText(tr("已保存密码")) }
Text(this.controller?.presence.online(device.id, this.controller?.now() ?? 0) ? tr("在线") : tr("离线")).fontSize(10)
.fontColor(this.controller?.presence.online(device.id, this.controller?.now() ?? 0) ? MobileTheme.green : MobileTheme.secondary)
}.width('100%')
}.padding(9).width('100%')
@@ -182,14 +185,14 @@ struct Mobile {
Recent() {
Column({ space: 14 }) {
Row() {
Text('最近连接').fontSize(20).fontWeight(600).fontColor(MobileTheme.ink).layoutWeight(1)
if (this.state.recent.length) { Text(`${this.state.recent.length} 台设备`).fontSize(12).fontColor(MobileTheme.secondary) }
Text(tr("最近连接")).fontSize(20).fontWeight(600).fontColor(MobileTheme.ink).layoutWeight(1)
if (this.state.recent.length) { Text(tr("{0} 台设备", [String(this.state.recent.length)])).fontSize(12).fontColor(MobileTheme.secondary) }
}.width('100%')
if (this.state.recent.length === 0) {
Column() {
MobileIcon({ name: 'history', iconExtent: 34, color: '#B6B6BB' })
Text('还没有连接记录').fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).margin({ top: 12, bottom: 8 })
Text('成功连接后,这里会显示最近的远程设备。').fontSize(13).lineHeight(19).fontColor(MobileTheme.secondary).textAlign(TextAlign.Center)
Text(tr("还没有连接记录")).fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).margin({ top: 12, bottom: 8 })
Text(tr("成功连接后,这里会显示最近的远程设备。")).fontSize(13).lineHeight(19).fontColor(MobileTheme.secondary).textAlign(TextAlign.Center)
}.layoutWeight(1).width('100%').justifyContent(FlexAlign.Center).padding({ left: 6, right: 6 })
} else {
Scroll() {
@@ -212,7 +215,7 @@ struct Mobile {
}.padding({ left: 11, right: 11 }).height(34).backgroundColor(Color.White).borderRadius(17).onClick(() => this.controller?.reconnect())
Blank()
Stack({ alignContent: Alignment.TopEnd }) {
MobileIconButton({ icon: 'envelope', label: '通知公告', action: () => this.navigate('announcements') })
MobileIconButton({ icon: 'envelope', label: tr("通知公告"), action: () => this.navigate('announcements') })
if (this.state.unread > 0) {
Text(this.state.unread > 99 ? '99+' : `${this.state.unread}`).fontSize(10).fontWeight(600).fontColor(Color.White)
.textAlign(TextAlign.Center).padding({ left: 4, right: 4 }).constraintSize({ minWidth: 16 }).height(16)
@@ -220,7 +223,7 @@ struct Mobile {
}
}
Stack({ alignContent: Alignment.TopEnd }) {
MobileIconButton({ icon: 'settings', label: '设置', action: () => this.navigate('settings') })
MobileIconButton({ icon: 'settings', label: tr("设置"), action: () => this.navigate('settings') })
if (this.state.update) {
Text('!').fontSize(10).fontWeight(600).fontColor(Color.White).textAlign(TextAlign.Center).width(16).height(16)
.backgroundColor(MobileTheme.red).borderRadius(8).translate({ x: 5, y: -3 }).hitTestBehavior(HitTestMode.None)
@@ -246,72 +249,72 @@ struct Mobile {
@Builder
PreviewRow() {
Row({ space: 12 }) {
Text('保存远程画面预览').fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Text(tr("保存远程画面预览")).fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.previewToggle }).selectedColor(MobileTheme.green)
.onChange((value: boolean) => {
this.previewToggle = value;
if (value && !this.state.previews) this.state.modal = 'previews';
else if (!value) this.controller?.setPreviews(false);
}).accessibilityText('保存远程画面预览')
}).accessibilityText(tr("保存远程画面预览"))
}.constraintSize({ minHeight: 52 }).padding({ left: 16, right: 16, top: 8, bottom: 8 }).width('100%')
}
@Builder
Settings() {
Column() {
this.SectionTitle('鼠标控制')
this.SectionTitle(tr("鼠标控制"))
Column() {
MobileSegment({ labels: ['相对位置', '绝对位置'], selection: this.state.relative ? 0 : 1,
MobileSegment({ labels: [tr("相对位置"), tr("绝对位置")], selection: this.state.relative ? 0 : 1,
changed: (i: number) => this.controller?.setRelative(i === 0) })
}.padding(12).width('100%').backgroundColor(Color.White).borderRadius(26).border({ width: 0.5, color: MobileTheme.line })
this.SectionTitle('画面偏好')
this.SectionTitle(tr("画面偏好"))
Column() {
MobileSegment({ labels: ['帧率优先', '画质优先', '平衡'], selection: this.state.videoPreference,
MobileSegment({ labels: [tr("帧率优先"), tr("画质优先"), tr("平衡")], selection: this.state.videoPreference,
changed: (i: number) => this.controller?.setPreference(i) })
}.padding(12).width('100%').backgroundColor(Color.White).borderRadius(26).border({ width: 0.5, color: MobileTheme.line })
this.SectionTitle('屏幕方向')
this.SectionTitle(tr("屏幕方向"))
Row() {
Text('连接建立后锁定横屏').fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Text(tr("连接建立后锁定横屏")).fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.state.autoLandscape }).selectedColor(MobileTheme.green)
.onChange((value: boolean) => this.controller?.setAutoLandscape(value)).accessibilityText('连接建立后锁定横屏')
.onChange((value: boolean) => this.controller?.setAutoLandscape(value)).accessibilityText(tr("连接建立后锁定横屏"))
}.padding({ left: 16, right: 16, top: 10, bottom: 10 }).width('100%').backgroundColor(Color.White)
.borderRadius(26).border({ width: 0.5, color: MobileTheme.line })
Text('默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。')
Text(tr("默认关闭。开启后,手机连接时进入并保持横屏;关闭时跟随设备方向。平板和多窗口不受影响。"))
.fontSize(12).fontColor(MobileTheme.secondary).width('100%').padding({ left: 16, right: 16, top: 8, bottom: 12 })
Column() {
this.SettingRow('服务器', this.customServer ? '自定义' : '默认', () => this.serverExpanded = !this.serverExpanded)
this.SettingRow(tr("服务器"), this.customServer ? tr("自定义") : tr("默认"), () => this.serverExpanded = !this.serverExpanded)
if (this.serverExpanded) {
this.Rule()
Row() {
Text('使用自定义服务器').fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Text(tr("使用自定义服务器")).fontSize(16).fontColor(MobileTheme.ink).layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.customServer }).selectedColor(MobileTheme.green).onChange((value: boolean) => {
this.customServer = value;
if (!value) { this.editingServer = 'api.crossdesk.cn'; this.editingPort = '9099'; this.controller?.saveSettings(this.editingServer, this.editingPort); }
}).accessibilityText('使用自定义服务器')
}).accessibilityText(tr("使用自定义服务器"))
}.padding({ left: 16, right: 16, top: 8, bottom: 8 }).width('100%')
if (this.customServer) {
Column({ space: 8 }) {
TextInput({ text: this.editingServer, placeholder: '服务器地址' }).fontSize(17).height(48).borderRadius(12)
TextInput({ text: this.editingServer, placeholder: tr("服务器地址") }).fontSize(17).height(48).borderRadius(12)
.backgroundColor(MobileTheme.background).onChange((v: string) => this.editingServer = v)
TextInput({ text: this.editingPort, placeholder: '服务器端口' }).fontSize(17).height(48).borderRadius(12)
TextInput({ text: this.editingPort, placeholder: tr("服务器端口") }).fontSize(17).height(48).borderRadius(12)
.backgroundColor(MobileTheme.background).type(InputType.Number).onChange((v: string) => this.editingPort = v)
this.Action('完成', () => { this.saveServer(); })
this.Action(tr("完成"), () => { this.saveServer(); })
}.padding({ left: 12, right: 12, bottom: 12 }).width('100%')
}
}
}.width('100%').backgroundColor(Color.White).borderRadius(26).clip(true).border({ width: 0.5, color: MobileTheme.line }).margin({ top: 28 })
Text(this.state.identity ? `本机 ID ${this.formatId(this.state.identity)}` : this.state.signalText).fontSize(12).fontColor(MobileTheme.secondary)
Text(this.state.identity ? tr("本机 ID {0}", [String(this.formatId(this.state.identity))]) : this.state.signalText).fontSize(12).fontColor(MobileTheme.secondary)
.textAlign(TextAlign.Center).padding({ left: 16, right: 16, top: 12, bottom: 16 }).width('100%').copyOption(CopyOptions.LocalDevice)
this.SectionTitle('隐私')
this.SectionTitle(tr("隐私"))
Column() {
this.SettingRow('隐私与授权', this.state.consent ? '已授权' : '未授权', () => this.navigate('privacy'))
this.SettingRow(tr("隐私与授权"), this.state.consent ? tr("已授权") : tr("未授权"), () => this.navigate('privacy'))
this.Rule()
this.PreviewRow()
this.Rule()
Button({ type: ButtonType.Normal }) { Text('清除预览图').fontSize(16).fontColor(MobileTheme.red).width('100%').textAlign(TextAlign.Start) }
Button({ type: ButtonType.Normal }) { Text(tr("清除预览图")).fontSize(16).fontColor(MobileTheme.red).width('100%').textAlign(TextAlign.Start) }
.height(48).width('100%').padding({ left: 16 }).backgroundColor(Color.White)
.onClick(() => this.state.modal = 'clearPreviews')
}.width('100%').backgroundColor(Color.White).borderRadius(26).clip(true).border({ width: 0.5, color: MobileTheme.line })
Column() { this.SettingRow('关于', this.state.update ? '新版本 ●' : '', () => this.navigate('about')) }
Column() { this.SettingRow(tr("关于"), this.state.update ? tr("新版本 ●") : '', () => this.navigate('about')) }
.width('100%').backgroundColor(Color.White).borderRadius(26).clip(true).border({ width: 0.5, color: MobileTheme.line }).margin({ top: 28 })
}.width('100%')
}
@@ -319,45 +322,45 @@ struct Mobile {
Privacy() {
Column() {
Column() {
this.ValueRow('授权状态', this.state.consent ? '已授权' : '未授权')
this.ValueRow(tr("授权状态"), this.state.consent ? tr("已授权") : tr("未授权"))
this.Rule()
this.SettingRow('隐私政策', '', () => this.controller?.document('PRIVACY.md', '隐私政策', 'privacy'))
this.SettingRow(tr("隐私政策"), '', () => this.controller?.document('PRIVACY.md', tr("隐私政策"), 'privacy'))
}.width('100%').backgroundColor(Color.White).borderRadius(12).clip(true).border({ width: 0.5, color: MobileTheme.line })
this.Footer('设备身份及选择保存的访问密码加密保存在本机。画面预览默认关闭,开启后从下一次连接起保存。剪贴板内容在结束会话时清除。离开应用后通过画中画或后台任务保持会话,可随时返回应用断开。')
if (this.state.consent) { this.Action('撤回联网授权', () => this.state.modal = 'revoke', true) }
else { MobilePrimaryButton({ label: '阅读并授权', action: () => this.state.modal = 'consent' }) }
this.Footer(tr("设备身份及选择保存的访问密码加密保存在本机。画面预览默认关闭,开启后从下一次连接起保存。剪贴板内容在结束会话时清除。离开应用后通过画中画或后台任务保持会话,可随时返回应用断开。"))
if (this.state.consent) { this.Action(tr("撤回联网授权"), () => this.state.modal = 'revoke', true) }
else { MobilePrimaryButton({ label: tr("阅读并授权"), action: () => this.state.modal = 'consent' }) }
}.width('100%')
}
@Builder
About() {
Column() {
Column() {
this.ValueRow('版本', this.state.version)
if (this.state.update) { this.Rule(); this.SettingRow('新版本可用', `v${this.state.update}`, () => this.controller?.platform.openLink('https://crossdesk.cn')) }
this.ValueRow(tr("版本"), this.state.version)
if (this.state.update) { this.Rule(); this.SettingRow(tr("新版本可用"), `v${this.state.update}`, () => this.controller?.platform.openLink('https://crossdesk.cn')) }
if (this.state.updateStatus) { this.Footer(this.state.updateStatus) }
this.Rule()
this.Action('检查更新', () => this.controller?.checkLocal(true))
this.Action(tr("检查更新"), () => this.controller?.checkLocal(true))
}.width('100%').backgroundColor(Color.White).borderRadius(12).clip(true).border({ width: 0.5, color: MobileTheme.line })
Column() {
this.SettingRow('软件许可', 'GPL-3.0-only', () => this.controller?.document('LICENSE', '软件许可'))
this.SettingRow(tr("软件许可"), 'GPL-3.0-only', () => this.controller?.document('LICENSE', tr("软件许可")))
this.Rule()
this.SettingRow('开源软件权利说明', '', () => this.controller?.document('OPEN_SOURCE_NOTICE.txt', '开源软件权利说明'))
this.SettingRow(tr("开源软件权利说明"), '', () => this.controller?.document('OPEN_SOURCE_NOTICE.txt', tr("开源软件权利说明")))
this.Rule()
this.SettingRow('开源组件', '', () => this.navigate('licenses'))
this.SettingRow(tr("开源组件"), '', () => this.navigate('licenses'))
this.Rule()
this.SettingRow('源码与构建说明', '', () => this.navigate('source'))
this.SettingRow(tr("源码与构建说明"), '', () => this.navigate('source'))
}.backgroundColor(Color.White).borderRadius(12).clip(true).width('100%').border({ width: 0.5, color: MobileTheme.line }).margin({ top: 28 })
this.Footer('许可与版权声明可离线查阅。')
this.Footer(tr("许可与版权声明可离线查阅。"))
}.width('100%')
}
@Builder
Announcements() {
Column({ space: 12 }) {
if (!this.state.consent) { Text('同意联网后可查看公告').fontSize(14); this.Action('查看联网说明', () => this.state.modal = 'consent') }
if (this.controller?.inbox.failed) { this.Action('加载失败,点击重试', () => this.controller?.inbox.refresh()) }
if (!this.state.consent) { Text(tr("同意联网后可查看公告")).fontSize(14); this.Action(tr("查看联网说明"), () => this.state.modal = 'consent') }
if (this.controller?.inbox.failed) { this.Action(tr("加载失败,点击重试"), () => this.controller?.inbox.refresh()) }
if (this.controller?.inbox.loading) { LoadingProgress().width(28).height(28) }
if (this.controller?.inbox.saveFailed) { Text('已读或删除状态保存失败,请重试').fontSize(12).fontColor(MobileTheme.red) }
if (!this.state.announcements.length && this.controller?.inbox.loaded && !this.controller?.inbox.loading) { Text('暂无公告').fontSize(16).fontColor(MobileTheme.secondary).padding(40) }
if (this.controller?.inbox.saveFailed) { Text(tr("已读或删除状态保存失败,请重试")).fontSize(12).fontColor(MobileTheme.red) }
if (!this.state.announcements.length && this.controller?.inbox.loaded && !this.controller?.inbox.loading) { Text(tr("暂无公告")).fontSize(16).fontColor(MobileTheme.secondary).padding(40) }
ForEach(this.state.announcements, (item: AnnouncementItem) => {
Column({ space: 8 }) {
Row({ space: 8 }) { if (!item.read) { Row() {}.width(6).height(6).borderRadius(3).backgroundColor(MobileTheme.blue) } Text(item.title).fontSize(17).fontWeight(600).layoutWeight(1) }.width('100%')
@@ -366,7 +369,7 @@ struct Mobile {
}.padding(18).width('100%').backgroundColor(Color.White).borderRadius(18).border({ width: 0.5, color: MobileTheme.line })
.onClick(() => this.openAnnouncement(item)).gesture(LongPressGesture().onAction(() => { this.state.announcement = item; this.state.modal = 'deleteAnnouncement'; }))
}, (item: AnnouncementItem) => `${item.id}:${item.revision}:${item.read}`)
if ((this.controller?.inbox.total ?? 0) > this.state.announcements.length) { this.Action('加载更多', () => this.controller?.inbox.more()) }
if ((this.controller?.inbox.total ?? 0) > this.state.announcements.length) { this.Action(tr("加载更多"), () => this.controller?.inbox.more()) }
}.width('100%')
}
@Builder
@@ -385,10 +388,10 @@ struct Mobile {
} else { Span(part.text) }
})
}.fontSize(16).lineHeight(25).width('100%').copyOption(CopyOptions.LocalDevice)
this.Action('删除公告', () => this.state.modal = 'deleteAnnouncement', true)
this.Action(tr("删除公告"), () => this.state.modal = 'deleteAnnouncement', true)
}.width('100%')
} else if (this.state.page === 'document') {
if (this.state.documentTitle === '隐私政策') { this.PolicyText(this.state.documentText) }
if (this.state.documentTitle === tr("隐私政策")) { this.PolicyText(this.state.documentText) }
else {
Column({ space: 12 }) {
ForEach(this.state.documentText.match(/[\s\S]{1,4000}/g) ?? [], (paragraph: string) => {
@@ -398,8 +401,8 @@ struct Mobile {
}
} else if (this.state.page === 'licenses') {
Column() {
this.SectionTitle('OpenFEC 告知')
Column() { this.Footer('OpenFEC 提供前向纠错能力,遵循 CeCILL-C 1.0 许可。其许可规定了有限保证及责任限制,请阅读组件详情。') }
this.SectionTitle(tr("OpenFEC 告知"))
Column() { this.Footer(tr("OpenFEC 提供前向纠错能力,遵循 CeCILL-C 1.0 许可。其许可规定了有限保证及责任限制,请阅读组件详情。")) }
.width('100%').backgroundColor(Color.White).borderRadius(12).border({ width: 0.5, color: MobileTheme.line })
Column() {
ForEach(this.licenses, (component: LicenseComponent, index: number) => {
@@ -415,18 +418,18 @@ struct Mobile {
}.width('100%').padding(0).backgroundColor(Color.White).onClick(() => { this.component = component; this.navigate('component'); })
}, (component: LicenseComponent) => component.id)
}.width('100%').backgroundColor(Color.White).borderRadius(12).clip(true).border({ width: 0.5, color: MobileTheme.line }).margin({ top: 28 })
this.Footer('各组件遵循所列开源许可证。许可与版权声明可离线查阅,查看源码需要网络连接。')
this.Footer(tr("各组件遵循所列开源许可证。许可与版权声明可离线查阅,查看源码需要网络连接。"))
}.width('100%')
} else if (this.state.page === 'component') {
Column() {
Column() {
this.ValueRow('版本', this.component?.version ?? '')
this.ValueRow(tr("版本"), this.component?.version ?? '')
this.Rule()
this.ValueRow('许可', this.component?.license ?? '')
this.ValueRow(tr("许可"), this.component?.license ?? '')
this.Rule()
this.SettingRow('查看源码', '', () => this.controller?.platform.openLink(this.component?.sourceURL ?? ''))
this.SettingRow(tr("查看源码"), '', () => this.controller?.platform.openLink(this.component?.sourceURL ?? ''))
}.width('100%').backgroundColor(Color.White).borderRadius(12).clip(true).border({ width: 0.5, color: MobileTheme.line })
this.SectionTitle('许可与版权声明')
this.SectionTitle(tr("许可与版权声明"))
Column() {
ForEach(this.component?.documents ?? [], (document: LicenseDocument, index: number) => {
if (index > 0) { this.Rule() }
@@ -436,16 +439,16 @@ struct Mobile {
}.width('100%')
} else if (this.state.page === 'source') {
Column() {
this.SectionTitle('本版本源码与构建说明')
this.SectionTitle(tr("本版本源码与构建说明"))
Column() {
this.ValueRow('源码版本', (this.source?.revision ?? '').substring(0, 12))
this.ValueRow(tr("源码版本"), (this.source?.revision ?? '').substring(0, 12))
this.Rule()
this.ValueRow('MiniRTC', (this.source?.miniRTCRevision ?? '').substring(0, 12))
if (this.source?.isModified) { this.Rule(); this.Footer('开发版本包含尚未发布的修改,以下链接为基础版本源码。') }
if (this.source?.isModified) { this.Rule(); this.Footer(tr("开发版本包含尚未发布的修改,以下链接为基础版本源码。")) }
this.Rule()
this.SettingRow('查看源码', '', () => this.controller?.platform.openLink(this.source?.sourceURL ?? ''))
this.SettingRow(tr("查看源码"), '', () => this.controller?.platform.openLink(this.source?.sourceURL ?? ''))
}.width('100%').backgroundColor(Color.White).borderRadius(12).clip(true).border({ width: 0.5, color: MobileTheme.line })
this.SectionTitle('构建文档')
this.SectionTitle(tr("构建文档"))
Column() {
ForEach(this.source?.buildDocuments ?? [], (document: LicenseDocument, index: number) => {
if (index > 0) { this.Rule() }
@@ -462,7 +465,7 @@ struct Mobile {
Text(title).fontSize(22).fontWeight(600).fontColor(MobileTheme.ink).width('100%')
if (subtitle) { Text(subtitle).fontSize(15).fontColor(MobileTheme.secondary).width('100%') }
}.layoutWeight(1)
MobileIconButton({ icon: 'close', label: '关闭', iconExtent: 36, iconSize: 20, action: () => this.dismissModal() })
MobileIconButton({ icon: 'close', label: tr("关闭"), iconExtent: 36, iconSize: 20, action: () => this.dismissModal() })
}.width('100%')
}
@Builder
@@ -470,24 +473,24 @@ struct Mobile {
Scroll() {
Column() {
Row() {}.width(36).height(5).backgroundColor('#C6C6CB').borderRadius(3).margin({ bottom: 20 })
this.SheetHeader('连接远程桌面', `对端 ID ${this.formatId(this.state.remote)}`)
this.SheetHeader(tr("连接远程桌面"), tr("对端 ID {0}", [String(this.formatId(this.state.remote))]))
Row() {
TextInput({ text: this.state.password, placeholder: '访问密码' }).type(this.showPassword ? InputType.Normal : InputType.Password)
TextInput({ text: this.state.password, placeholder: tr("访问密码") }).type(this.showPassword ? InputType.Normal : InputType.Password)
.showPasswordIcon(false).maxLength(128).fontSize(17).height(50).layoutWeight(1).backgroundColor(Color.Transparent)
.padding({ left: 14, right: 4 }).onChange((value: string) => this.state.password = value).onSubmit(() => this.controller?.begin(this.state.password))
MobileIconButton({ icon: this.showPassword ? 'eye_off' : 'eye', label: this.showPassword ? '隐藏密码' : '显示密码', iconExtent: 44,
MobileIconButton({ icon: this.showPassword ? 'eye_off' : 'eye', label: this.showPassword ? tr("隐藏密码") : tr("显示密码"), iconExtent: 44,
action: () => this.showPassword = !this.showPassword })
}.width('100%').backgroundColor(Color.White).borderRadius(10).border({ width: 0.5, color: MobileTheme.line }).margin({ top: 18 })
if (this.state.notice) { Text(this.state.notice).fontSize(13).fontColor(MobileTheme.red).width('100%').margin({ top: 8 }) }
Row({ space: 12 }) {
Column({ space: 3 }) {
Text('保存密码').fontSize(15).fontWeight(600).fontColor(MobileTheme.ink).width('100%')
Text('密码将加密保存在此设备上').fontSize(11).fontColor(MobileTheme.secondary).width('100%')
Text(tr("保存密码")).fontSize(15).fontWeight(600).fontColor(MobileTheme.ink).width('100%')
Text(tr("密码将加密保存在此设备上")).fontSize(11).fontColor(MobileTheme.secondary).width('100%')
}.layoutWeight(1)
Toggle({ type: ToggleType.Switch, isOn: this.state.remember }).selectedColor(MobileTheme.green)
.onChange((value: boolean) => this.state.remember = value).accessibilityText('保存密码')
.onChange((value: boolean) => this.state.remember = value).accessibilityText(tr("保存密码"))
}.width('100%').margin({ top: 18, bottom: 18 })
MobilePrimaryButton({ label: '连接', available: this.state.password.length > 0, action: () => this.controller?.begin(this.state.password) })
MobilePrimaryButton({ label: tr("连接"), available: this.state.password.length > 0, action: () => this.controller?.begin(this.state.password) })
}.padding({ left: 22, right: 22, top: 10, bottom: 22 }).width('100%')
}.width(this.viewWidth < 600 ? '100%' : Math.min(520, this.viewWidth - 48)).constraintSize({ maxHeight: this.viewHeight * 0.92 })
.backgroundColor(MobileTheme.background).borderRadius(24).scrollBar(BarState.Off)
@@ -506,11 +509,11 @@ struct Mobile {
@Builder
ConsentSheet() {
Column() {
Text('隐私政策').fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).height(56).width('100%').textAlign(TextAlign.Center)
Text(tr("隐私政策")).fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).height(56).width('100%').textAlign(TextAlign.Center)
this.Rule()
Scroll() {
Column({ space: 12 }) {
if (!this.state.privacyAvailable) { Text('隐私政策无法读取,请重新安装应用').fontSize(14).fontColor(MobileTheme.red) }
if (!this.state.privacyAvailable) { Text(tr("隐私政策无法读取,请重新安装应用")).fontSize(14).fontColor(MobileTheme.red) }
this.PolicyText(this.privacyText)
}.padding(20).width('100%')
}.layoutWeight(1).width('100%').align(Alignment.Top).scrollBar(BarState.Off)
@@ -518,11 +521,11 @@ struct Mobile {
Column({ space: 12 }) {
Row({ space: 8 }) {
Checkbox({ name: 'consent' }).width(20).height(20).margin(0).select(this.accepted).selectedColor(MobileTheme.blue)
.onChange((value: boolean) => this.accepted = value).accessibilityText('我已阅读并同意隐私政策')
Text('我已阅读并同意《隐私政策》').fontSize(14).fontColor(MobileTheme.ink).layoutWeight(1).onClick(() => this.accepted = !this.accepted)
.onChange((value: boolean) => this.accepted = value).accessibilityText(tr("我已阅读并同意隐私政策"))
Text(tr("我已阅读并同意《隐私政策》")).fontSize(14).fontColor(MobileTheme.ink).layoutWeight(1).onClick(() => this.accepted = !this.accepted)
}.width('100%').constraintSize({ minHeight: 44 })
MobilePrimaryButton({ label: '同意并继续', available: this.accepted && this.state.privacyAvailable, action: () => this.controller?.consent() })
Button('暂不同意', { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.secondary).height(44).width('100%')
MobilePrimaryButton({ label: tr("同意并继续"), available: this.accepted && this.state.privacyAvailable, action: () => this.controller?.consent() })
Button(tr("暂不同意"), { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.secondary).height(44).width('100%')
.backgroundColor(Color.Transparent).onClick(() => this.dismissModal())
}.padding({ left: 20, right: 20, top: 12, bottom: 12 }).width('100%')
}.width(this.viewWidth < 600 ? '100%' : Math.min(640, this.viewWidth - 48)).height(Math.min(760, this.viewHeight * 0.9))
@@ -533,42 +536,42 @@ struct Mobile {
if (this.state.modal === 'connecting') {
Column({ space: 14 }) {
LoadingProgress().width(34).height(34).color(MobileTheme.blue)
Text('正在连接').fontSize(20).fontWeight(600).fontColor(MobileTheme.ink)
Text(this.state.signal === 1 ? '正在建立安全连接…' : this.state.signalText).fontSize(14).fontColor(MobileTheme.secondary).textAlign(TextAlign.Center).constraintSize({ minHeight: 36 })
Text(tr("正在连接")).fontSize(20).fontWeight(600).fontColor(MobileTheme.ink)
Text(this.state.signal === 1 ? tr("正在建立安全连接…") : this.state.signalText).fontSize(14).fontColor(MobileTheme.secondary).textAlign(TextAlign.Center).constraintSize({ minHeight: 36 })
}.padding({ left: 26, right: 26, top: 24, bottom: 20 }).width('100%')
this.Rule()
this.Action('取消连接', () => this.controller?.end(''), true)
this.Action(tr("取消连接"), () => this.controller?.end(''), true)
} else if (this.state.modal === 'privacyUnlock') {
Column({ space: 12 }) {
Text('隐私屏已被解锁').fontSize(20).fontWeight(600).fontColor(MobileTheme.ink)
Text('被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。')
Text(tr("隐私屏已被解锁")).fontSize(20).fontWeight(600).fontColor(MobileTheme.ink)
Text(tr("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。"))
.fontSize(14).lineHeight(21).fontColor(MobileTheme.secondary).width('100%')
if (this.state.privacyFeedback) { Text(this.state.privacyFeedback).fontSize(12).fontColor(MobileTheme.secondary) }
}.padding(24).width('100%')
this.Rule()
Row() {
Button('确认', { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
Button(tr("确认"), { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
.backgroundColor(Color.White).onClick(() => this.dismissModal())
Divider().vertical(true).height(50).strokeWidth(0.5).color(MobileTheme.line)
Button('锁定', { type: ButtonType.Normal }).fontSize(16).fontWeight(600).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
Button(tr("锁定"), { type: ButtonType.Normal }).fontSize(16).fontWeight(600).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
.backgroundColor(Color.White).enabled(this.state.privacyCanLock).onClick(() => this.controller?.lockPrivacy())
}.width('100%')
} else if (this.state.modal === 'clipboard') {
Column({ space: 16 }) {
this.SheetHeader('剪贴板')
Text('发送文本后,点击“粘贴到远端”使用。').fontSize(13).fontColor(MobileTheme.secondary).width('100%')
TextArea({ text: this.state.clipboardText, placeholder: '要发送到远端的文本' }).fontSize(15).height(120).borderRadius(12).barState(BarState.Off)
this.SheetHeader(tr("剪贴板"))
Text(tr("发送文本后,点击“粘贴到远端”使用。")).fontSize(13).fontColor(MobileTheme.secondary).width('100%')
TextArea({ text: this.state.clipboardText, placeholder: tr("要发送到远端的文本") }).fontSize(15).height(120).borderRadius(12).barState(BarState.Off)
.backgroundColor(MobileTheme.background).onChange((value: string) => this.state.clipboardText = value)
MobilePrimaryButton({ label: '发送文本', action: () => this.controller?.clipboard() })
MobilePrimaryButton({ label: tr("发送文本"), action: () => this.controller?.clipboard() })
Row({ space: 8 }) {
Column() { this.Action('粘贴到远端', () => this.controller?.shortcut(86)) }.layoutWeight(1)
Column() { this.Action('复制远端文本', () => this.controller?.copyRemote()) }.layoutWeight(1)
Column() { this.Action(tr("粘贴到远端"), () => this.controller?.shortcut(86)) }.layoutWeight(1)
Column() { this.Action(tr("复制远端文本"), () => this.controller?.copyRemote()) }.layoutWeight(1)
}.width('100%')
}.padding(20).width('100%')
} else if (this.state.modal === 'displays') {
Column({ space: 12 }) {
this.SheetHeader('显示器')
if (!this.state.displays.length) { this.Footer('等待远端屏幕信息') }
this.SheetHeader(tr("显示器"))
if (!this.state.displays.length) { this.Footer(tr("等待远端屏幕信息")) }
ForEach(this.state.displays, (screen: RemoteDisplay, index: number) => {
Button({ type: ButtonType.Normal }) {
Row({ space: 12 }) {
@@ -584,31 +587,31 @@ struct Mobile {
Text(this.state.recent.find((d: RecentDevice) => d.id === this.deleteId)?.name || this.formatId(this.deleteId))
.fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).padding(20).width('100%').textAlign(TextAlign.Center)
this.Rule()
this.Action('连接', () => { this.dismissModal(); this.controller?.requestConnection(this.deleteId); })
this.Action(tr("连接"), () => { this.dismissModal(); this.controller?.requestConnection(this.deleteId); })
this.Rule()
this.Action('删除记录', () => this.state.modal = 'delete', true)
this.Action(tr("删除记录"), () => this.state.modal = 'delete', true)
this.Rule()
this.Action('取消', () => this.dismissModal())
this.Action(tr("取消"), () => this.dismissModal())
} else {
Column({ space: 12 }) {
Text(this.state.modal === 'notice' ? '连接提示' : this.state.modal === 'upgrade' ? '升级提示' : this.state.modal === 'disconnect' ? '断开远程连接?' :
this.state.modal === 'revoke' ? '撤回联网授权?' : this.state.modal === 'previews' ? '保存远程画面预览?' : this.state.modal === 'clearPreviews' ? '清除预览图?' : this.state.modal === 'deleteAnnouncement' ? '删除公告?' : '删除记录?')
Text(this.state.modal === 'notice' ? tr("连接提示") : this.state.modal === 'upgrade' ? tr("升级提示") : this.state.modal === 'disconnect' ? tr("断开远程连接?") :
this.state.modal === 'revoke' ? tr("撤回联网授权?") : this.state.modal === 'previews' ? tr("保存远程画面预览?") : this.state.modal === 'clearPreviews' ? tr("清除预览图?") : this.state.modal === 'deleteAnnouncement' ? tr("删除公告?") : tr("删除记录?"))
.fontSize(20).fontWeight(600).fontColor(MobileTheme.ink).textAlign(TextAlign.Center).width('100%')
Text(this.state.modal === 'notice' || this.state.modal === 'upgrade' ? this.state.notice : this.state.modal === 'disconnect' ? '断开后将返回首页。' :
this.state.modal === 'previews' ? '预览可能包含远程设备上的敏感内容。仅保存在本机,从下一次连接起生效,可随时关闭并清除。' :
this.state.modal === 'clearPreviews' ? '将清除此设备保存的所有远程画面预览,保留连接记录和访问密码。' :
this.state.modal === 'revoke' ? '将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。' :
this.state.modal === 'delete' ? '同时删除该设备保存的密码和预览。' : '此公告将从列表中移除。')
Text(this.state.modal === 'notice' || this.state.modal === 'upgrade' ? this.state.notice : this.state.modal === 'disconnect' ? tr("断开后将返回首页。") :
this.state.modal === 'previews' ? tr("预览可能包含远程设备上的敏感内容。仅保存在本机,从下一次连接起生效,可随时关闭并清除。") :
this.state.modal === 'clearPreviews' ? tr("将清除此设备保存的所有远程画面预览,保留连接记录和访问密码。") :
this.state.modal === 'revoke' ? tr("将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和密码会保留,重新连接前需再次授权。") :
this.state.modal === 'delete' ? tr("同时删除该设备保存的密码和预览。") : tr("此公告将从列表中移除。"))
.fontSize(14).lineHeight(21).fontColor(MobileTheme.secondary).textAlign(TextAlign.Center).width('100%')
}.padding(24).width('100%')
this.Rule()
if (this.state.modal === 'notice' || this.state.modal === 'upgrade') { this.Action('知道了', () => this.dismissModal()) }
if (this.state.modal === 'notice' || this.state.modal === 'upgrade') { this.Action(tr("知道了"), () => this.dismissModal()) }
else {
Row() {
Button('取消', { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
Button(tr("取消"), { type: ButtonType.Normal }).fontSize(16).fontColor(MobileTheme.blue).height(50).layoutWeight(1)
.backgroundColor(Color.White).onClick(() => this.dismissModal())
Divider().vertical(true).height(50).strokeWidth(0.5).color(MobileTheme.line)
Button(this.state.modal === 'previews' ? '开启' : this.state.modal === 'disconnect' ? '断开连接' : this.state.modal === 'revoke' ? '撤回授权' : this.state.modal === 'clearPreviews' ? '清除' : '删除', { type: ButtonType.Normal })
Button(this.state.modal === 'previews' ? tr("开启") : this.state.modal === 'disconnect' ? tr("断开连接") : this.state.modal === 'revoke' ? tr("撤回授权") : this.state.modal === 'clearPreviews' ? tr("清除") : tr("删除"), { type: ButtonType.Normal })
.fontSize(16).fontWeight(600).fontColor(this.state.modal === 'previews' ? MobileTheme.blue : MobileTheme.red).height(50).layoutWeight(1)
.backgroundColor(Color.White).onClick(() => this.confirmModal())
}.width('100%')
@@ -637,11 +640,11 @@ struct Mobile {
else {
Column() {
Row({ space: 0 }) {
MobileIconButton({ icon: 'back', label: '返回', iconExtent: 44, iconSize: 22, action: () => this.back() })
MobileIconButton({ icon: 'back', label: tr("返回"), iconExtent: 44, iconSize: 22, action: () => this.back() })
Text(this.title()).fontSize(17).fontWeight(600).fontColor(MobileTheme.ink).layoutWeight(1).textAlign(TextAlign.Center)
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
if (this.state.page === 'announcements') {
MobileIconButton({ icon: 'refresh', label: '刷新公告', iconExtent: 44, iconSize: 22, action: () => this.controller?.inbox.refresh() })
MobileIconButton({ icon: 'refresh', label: tr("刷新公告"), iconExtent: 44, iconSize: 22, action: () => this.controller?.inbox.refresh() })
} else { Blank().width(44) }
}.width('100%').constraintSize({ maxWidth: 720 }).height(56).padding({ left: 12, right: 12 })
if (this.state.page === 'announcements') {
@@ -35,6 +35,8 @@
10000000000000000000001A /* AnnouncementText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000001A /* AnnouncementText.swift */; };
10000000000000000000001B /* AnnouncementInboxView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000001B /* AnnouncementInboxView.swift */; };
10000000000000000000001C /* AppUpdateChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20000000000000000000001C /* AppUpdateChecker.swift */; };
3C550ED141D4AEA30950BB7A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5BD6CC3A43B86A2E2267994F; };
4870B14C46F4C4B8F10FEF08 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A0A3C0DA09EE562AE21DF986; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -70,6 +72,28 @@
20000000000000000000001A /* AnnouncementText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementText.swift; sourceTree = "<group>"; };
20000000000000000000001B /* AnnouncementInboxView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnnouncementInboxView.swift; sourceTree = "<group>"; };
20000000000000000000001C /* AppUpdateChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChecker.swift; sourceTree = "<group>"; };
51B43C654308785E6FC27C98 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en"; path = "Resources/en.lproj/Localizable.strings"; sourceTree = "<group>"; };
C04C96DC6D837D5C45257B37 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "Resources/zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
4E23FD4A4331F613A7BF3C81 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Resources/zh-Hant.lproj/Localizable.strings"; sourceTree = "<group>"; };
10718835138E0FDDFDE9B567 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ru"; path = "Resources/ru.lproj/Localizable.strings"; sourceTree = "<group>"; };
BA8BCB85D00055B796EBB9E1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja"; path = "Resources/ja.lproj/Localizable.strings"; sourceTree = "<group>"; };
10083BB5398965A4432CEC1E /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ko"; path = "Resources/ko.lproj/Localizable.strings"; sourceTree = "<group>"; };
8CEF64DB2A646A3C710CAAE7 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de"; path = "Resources/de.lproj/Localizable.strings"; sourceTree = "<group>"; };
467DE174FF993E9771FAEE5D /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr"; path = "Resources/fr.lproj/Localizable.strings"; sourceTree = "<group>"; };
2EA0435DFAEE685F04391B3B /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es"; path = "Resources/es.lproj/Localizable.strings"; sourceTree = "<group>"; };
1C906F7AD7B701DB61B512D2 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Resources/pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
B6189259C1AE18847A742A13 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt"; path = "Resources/pt.lproj/Localizable.strings"; sourceTree = "<group>"; };
23D5AD87A9BF0CBF3A6AADDF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en"; path = "Resources/en.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
2E83770B40B2D299796EB0CD /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "Resources/zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
36B135DFBFBD8F03E16FB09A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Resources/zh-Hant.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
56DE65D659C5D65945CF834F /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ru"; path = "Resources/ru.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
02A713C4B5A1623480A005D6 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja"; path = "Resources/ja.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
EA650495031B45A6FE5892FD /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ko"; path = "Resources/ko.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
E653CAF5C4E87548BF6D8670 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "de"; path = "Resources/de.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
0BB7EC0F2A5A6A313F782DCA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr"; path = "Resources/fr.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
093D4362C938821DE02B9032 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es"; path = "Resources/es.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
125E3BF17EC58E43B32F2AC3 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "Resources/pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
7DDF05F5D35AB063DB447879 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt"; path = "Resources/pt.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -125,6 +149,8 @@
200000000000000000000003 /* ContentView.swift */,
20000000000000000000000E /* ThirdPartyLicensesView.swift */,
20000000000000000000000F /* ThirdPartyLicenses.json */,
A0A3C0DA09EE562AE21DF986 /* InfoPlist.strings */,
5BD6CC3A43B86A2E2267994F /* Localizable.strings */,
200000000000000000000010 /* PrivacyInfo.xcprivacy */,
200000000000000000000004 /* RemoteSessionView.swift */,
400000000000000000000003 /* Bridge */,
@@ -206,6 +232,11 @@
};
/* End PBXNativeTarget section */
/* Begin PBXVariantGroup section */
5BD6CC3A43B86A2E2267994F /* Localizable.strings */ = {isa = PBXVariantGroup; children = (51B43C654308785E6FC27C98, C04C96DC6D837D5C45257B37, 4E23FD4A4331F613A7BF3C81, 10718835138E0FDDFDE9B567, BA8BCB85D00055B796EBB9E1, 10083BB5398965A4432CEC1E, 8CEF64DB2A646A3C710CAAE7, 467DE174FF993E9771FAEE5D, 2EA0435DFAEE685F04391B3B, 1C906F7AD7B701DB61B512D2, B6189259C1AE18847A742A13, ); name = Localizable.strings; sourceTree = "<group>"; };
A0A3C0DA09EE562AE21DF986 /* InfoPlist.strings */ = {isa = PBXVariantGroup; children = (23D5AD87A9BF0CBF3A6AADDF, 2E83770B40B2D299796EB0CD, 36B135DFBFBD8F03E16FB09A, 56DE65D659C5D65945CF834F, 02A713C4B5A1623480A005D6, EA650495031B45A6FE5892FD, E653CAF5C4E87548BF6D8670, 0BB7EC0F2A5A6A313F782DCA, 093D4362C938821DE02B9032, 125E3BF17EC58E43B32F2AC3, 7DDF05F5D35AB063DB447879, ); name = InfoPlist.strings; sourceTree = "<group>"; };
/* End PBXVariantGroup section */
/* Begin PBXProject section */
800000000000000000000001 /* Project object */ = {
isa = PBXProject;
@@ -221,13 +252,9 @@
};
buildConfigurationList = 900000000000000000000001 /* Build configuration list for PBXProject "CrossDeskMobile" */;
compatibilityVersion = "Xcode 15.0";
developmentRegion = zh_CN;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
Base,
en,
zh_CN,
);
knownRegions = (Base, "en", "zh-Hans", "zh-Hant", "ru", "ja", "ko", "de", "fr", "es", "pt-BR", "pt", );
mainGroup = 400000000000000000000001;
productRefGroup = 400000000000000000000007 /* Products */;
projectDirPath = "";
@@ -245,6 +272,8 @@
files = (
100000000000000000000008 /* Assets.xcassets in Resources */,
10000000000000000000000A /* ThirdPartyLicenses.json in Resources */,
4870B14C46F4C4B8F10FEF08 /* InfoPlist.strings in Resources */,
3C550ED141D4AEA30950BB7A /* Localizable.strings in Resources */,
10000000000000000000000B /* PrivacyInfo.xcprivacy in Resources */,
100000000000000000000015 /* PRIVACY.md in Resources */,
);
@@ -21,8 +21,8 @@ struct AnnouncementBadge: View {
}
}
.accessibilityElement(children: .ignore)
.accessibilityLabel("通知公告")
.accessibilityValue(inbox.state.unread > 0 ? "\(inbox.state.unread) 条未读" : "无未读公告")
.accessibilityLabel(NSLocalizedString("通知公告", comment: ""))
.accessibilityValue(inbox.state.unread > 0 ? String(format: NSLocalizedString("%1$@ 条未读", comment: ""), String(describing: inbox.state.unread)) : NSLocalizedString("无未读公告", comment: ""))
}
}
@@ -49,14 +49,14 @@ struct AnnouncementInboxView: View {
var body: some View {
List {
if inbox.state.deleteFailed {
Text("无法删除这条公告,请重试。")
Text(NSLocalizedString("无法删除这条公告,请重试。", comment: ""))
.foregroundStyle(.red)
}
if inbox.state.failed && session.announcementsConnected {
HStack {
Text("公告加载失败,请重试。")
Text(NSLocalizedString("公告加载失败,请重试。", comment: ""))
Spacer()
Button("重试") { session.refreshAnnouncements() }
Button(NSLocalizedString("重试", comment: "")) { session.refreshAnnouncements() }
.disabled(!session.announcementsConnected)
}
}
@@ -82,42 +82,42 @@ struct AnnouncementInboxView: View {
}
.padding(.vertical, 5)
.accessibilityElement(children: .combine)
.accessibilityValue(item.read ? "已读" : "未读")
.accessibilityValue(item.read ? NSLocalizedString("已读", comment: "") : NSLocalizedString("未读", comment: ""))
}
.swipeActions {
Button("删除", role: .destructive) { pendingDelete = item }
Button(NSLocalizedString("删除", comment: ""), role: .destructive) { pendingDelete = item }
}
.contextMenu {
Button("删除公告", role: .destructive) { pendingDelete = item }
Button(NSLocalizedString("删除公告", comment: ""), role: .destructive) { pendingDelete = item }
}
}
if inbox.state.loading {
HStack { Spacer(); ProgressView("加载中…"); Spacer() }
HStack { Spacer(); ProgressView(NSLocalizedString("加载中…", comment: "")); Spacer() }
} else if inbox.state.items.count < inbox.state.total && !inbox.state.failed {
Button("加载更多") { inbox.loadMore() }
Button(NSLocalizedString("加载更多", comment: "")) { inbox.loadMore() }
.frame(maxWidth: .infinity)
.disabled(!session.announcementsConnected)
.onAppear { inbox.loadMore() }
} else if inbox.state.loaded && inbox.state.total == 0 &&
!inbox.state.failed && session.announcementsConnected {
Text("暂无公告")
Text(NSLocalizedString("暂无公告", comment: ""))
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, minHeight: 120)
}
} header: {
Text("共 \(inbox.state.total) 条 · \(inbox.state.unread) 条未读")
Text(String(format: NSLocalizedString("共 %1$@ 条 · %2$@ 条未读", comment: ""), String(describing: inbox.state.total), String(describing: inbox.state.unread)))
}
}
.readableContentWidth()
.background(Color(.systemGroupedBackground).ignoresSafeArea())
.navigationTitle("通知公告")
.navigationTitle(NSLocalizedString("通知公告", comment: ""))
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button { session.refreshAnnouncements() } label: {
Image(systemName: "arrow.clockwise")
}
.accessibilityLabel("刷新公告")
.accessibilityLabel(NSLocalizedString("刷新公告", comment: ""))
.disabled(inbox.state.loading || !session.announcementsConnected)
}
}
@@ -128,17 +128,17 @@ struct AnnouncementInboxView: View {
}
}
.onAppear { session.refreshAnnouncements() }
.alert("删除公告", isPresented: Binding(
.alert(NSLocalizedString("删除公告", comment: ""), isPresented: Binding(
get: { pendingDelete != nil },
set: { if !$0 { pendingDelete = nil } }
)) {
Button("取消", role: .cancel) { pendingDelete = nil }
Button("删除", role: .destructive) {
Button(NSLocalizedString("取消", comment: ""), role: .cancel) { pendingDelete = nil }
Button(NSLocalizedString("删除", comment: ""), role: .destructive) {
if let item = pendingDelete { inbox.dismiss(item) }
pendingDelete = nil
}
} message: {
Text("删除这条公告?")
Text(NSLocalizedString("删除这条公告?", comment: ""))
}
}
}
@@ -158,13 +158,13 @@ struct AnnouncementDetailView: View {
VStack(alignment: .leading, spacing: 16) {
Text(verbatim: item.title)
.font(.title2.weight(.semibold))
Text("\(AnnouncementDate.string(item)) · \(item.read ? "已读" : "未读")")
Text("\(AnnouncementDate.string(item)) · \(NSLocalizedString(item.read ? "已读" : "未读", comment: ""))")
.font(.caption)
.foregroundStyle(.secondary)
if inbox.state.readSaveFailed {
Text("无法保存本地阅读状态,请重试。")
Text(NSLocalizedString("无法保存本地阅读状态,请重试。", comment: ""))
.foregroundStyle(.red)
Button("重试保存阅读状态") { inbox.markRead(item) }
Button(NSLocalizedString("重试保存阅读状态", comment: "")) { inbox.markRead(item) }
}
Text(AnnouncementText.format(item.body))
.textSelection(.enabled)
@@ -178,13 +178,13 @@ struct AnnouncementDetailView: View {
.padding(20)
.readableContentWidth()
} else {
Text("这条公告已更新或删除,请返回公告列表查看。")
Text(NSLocalizedString("这条公告已更新或删除,请返回公告列表查看。", comment: ""))
.foregroundStyle(.secondary)
.padding(20)
.readableContentWidth()
}
}
.navigationTitle("公告详情")
.navigationTitle(NSLocalizedString("公告详情", comment: ""))
.navigationBarTitleDisplayMode(.inline)
.onAppear {
if let item { inbox.markRead(item) }
+61 -68
View File
@@ -65,11 +65,11 @@ struct ContentView: View {
.presentationDragIndicator(.hidden)
.interactiveDismissDisabled()
}
.alert("连接提示", isPresented: Binding(
.alert(NSLocalizedString("连接提示", comment: ""), isPresented: Binding(
get: { session.connectionFailureMessage != nil },
set: { if !$0 { session.dismissConnectionFailure() } }
)) {
Button("确定", role: .cancel) { session.dismissConnectionFailure() }
Button(NSLocalizedString("确定", comment: ""), role: .cancel) { session.dismissConnectionFailure() }
} message: {
Text(session.connectionFailureMessage ?? "")
}
@@ -196,17 +196,8 @@ private struct ConnectionHomeView: View {
)
}
private var signalIsConnected: Bool {
session.signalStatus == "已连接服务器"
}
private var signalHasError: Bool {
session.signalStatus.contains("失败") ||
session.signalStatus.contains("无法") ||
session.signalStatus.contains("无效") ||
session.signalStatus.contains("关闭") ||
session.signalStatus.contains("未知")
}
private var signalIsConnected: Bool { session.signalConnected }
private var signalHasError: Bool { session.signalHasError }
private var signalTint: Color {
if signalIsConnected { return .green }
@@ -303,13 +294,13 @@ private struct ConnectionHomeView: View {
.presentationDetents([.height(340)])
.presentationDragIndicator(.visible)
}
.alert("设备离线", isPresented: Binding(
.alert(NSLocalizedString("设备离线", comment: ""), isPresented: Binding(
get: { session.deviceOfflineAlertVisible },
set: { visible in
if !visible { session.dismissDeviceOfflineAlert() }
}
)) {
Button("确定") {
Button(NSLocalizedString("确定", comment: "")) {
session.dismissDeviceOfflineAlert()
}
}
@@ -337,7 +328,7 @@ private struct ConnectionHomeView: View {
.fixedSize(horizontal: true, vertical: false)
}
.buttonStyle(.plain)
.accessibilityHint("打开隐私政策")
.accessibilityHint(NSLocalizedString("打开隐私政策", comment: ""))
}
}
ToolbarItem(placement: .navigationBarTrailing) {
@@ -369,7 +360,7 @@ private struct ConnectionHomeView: View {
}
.foregroundStyle(.primary)
.disabled(session.isConnecting)
.accessibilityLabel(appUpdates.updateAvailable ? "设置,有新版本" : "设置")
.accessibilityLabel(appUpdates.updateAvailable ? NSLocalizedString("设置,有新版本", comment: "") : NSLocalizedString("设置", comment: ""))
}
}
@@ -398,13 +389,13 @@ private struct ConnectionHomeView: View {
private var remoteConnectionPanel: some View {
VStack(alignment: .leading, spacing: 16) {
HStack {
Text("远程桌面")
Text(NSLocalizedString("远程桌面", comment: ""))
.font(.title3.weight(.bold))
Spacer()
}
HStack(spacing: 10) {
TextField("对端 ID", text: groupedRemoteID)
TextField(NSLocalizedString("对端 ID", comment: ""), text: groupedRemoteID)
.keyboardType(.numberPad)
.textContentType(.username)
.textInputAutocapitalization(.never)
@@ -431,8 +422,10 @@ private struct ConnectionHomeView: View {
Button {
presentPasswordPrompt(for: trimmedRemoteID)
} label: {
Text("连接")
Text(NSLocalizedString("连接", comment: ""))
.font(.system(size: 16, weight: .bold))
.lineLimit(1)
.minimumScaleFactor(0.65)
.frame(width: 96, height: 54)
.foregroundStyle(.white)
.background(LinearGradient(colors: [
@@ -447,7 +440,7 @@ private struct ConnectionHomeView: View {
.buttonStyle(.plain)
.disabled(trimmedRemoteID.isEmpty || session.isConnecting)
.opacity(trimmedRemoteID.isEmpty || session.isConnecting ? 0.45 : 1)
.accessibilityLabel("连接")
.accessibilityLabel(NSLocalizedString("连接", comment: ""))
}
}
.padding(18)
@@ -464,11 +457,11 @@ private struct ConnectionHomeView: View {
private func recentConnectionsPanel(windowWidth: CGFloat) -> some View {
VStack(alignment: .leading, spacing: 14) {
HStack(alignment: .firstTextBaseline) {
Text("最近连接")
Text(NSLocalizedString("最近连接", comment: ""))
.font(.title3.bold())
Spacer()
if !session.recentConnections.isEmpty {
Text("\(session.recentConnections.count) 台设备")
Text(String(format: NSLocalizedString("%1$@ 台设备", comment: ""), String(describing: session.recentConnections.count)))
.font(.caption)
.foregroundStyle(.secondary)
}
@@ -479,9 +472,9 @@ private struct ConnectionHomeView: View {
Image(systemName: "clock.arrow.circlepath")
.font(.system(size: 30, weight: .medium))
.foregroundStyle(.tertiary)
Text("还没有连接记录")
Text(NSLocalizedString("还没有连接记录", comment: ""))
.font(.headline)
Text("成功连接后,这里会显示远端桌面的缩略图。")
Text(NSLocalizedString("成功连接后,这里会显示远端桌面的缩略图。", comment: ""))
.font(.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
@@ -532,7 +525,7 @@ private struct ConnectionHomeView: View {
.tint(connectionAccent)
VStack(spacing: 6) {
Text("正在连接")
Text(NSLocalizedString("正在连接", comment: ""))
.font(.title3.weight(.semibold))
.foregroundStyle(.primary)
Text(session.connectionStatus)
@@ -550,7 +543,7 @@ private struct ConnectionHomeView: View {
Divider()
Button(role: .cancel, action: session.disconnect) {
Text("取消连接")
Text(NSLocalizedString("取消连接", comment: ""))
.font(.subheadline.weight(.semibold))
.foregroundStyle(Color.red)
.frame(maxWidth: .infinity)
@@ -656,7 +649,7 @@ private struct RecentConnectionCard: View {
.minimumScaleFactor(0.8)
.foregroundStyle(.white)
.padding(8)
.accessibilityLabel("被控端平台:\(connection.platform.displayName)")
.accessibilityLabel(String(format: NSLocalizedString("被控端平台:%1$@", comment: ""), String(describing: connection.platform.displayName)))
}
}
.frame(maxWidth: .infinity)
@@ -678,12 +671,12 @@ private struct RecentConnectionCard: View {
Image(systemName: "key.fill")
.font(.system(size: 9, weight: .semibold))
.foregroundStyle(.secondary)
.accessibilityLabel("已保存密码")
.accessibilityLabel(NSLocalizedString("已保存密码", comment: ""))
}
Text(online ? "在线" : "离线")
Text(online ? NSLocalizedString("在线", comment: "") : NSLocalizedString("离线", comment: ""))
.font(.caption2.weight(.semibold))
.foregroundStyle(online ? Color.green : Color.secondary)
.accessibilityLabel(online ? "在线" : "离线")
.accessibilityLabel(online ? NSLocalizedString("在线", comment: "") : NSLocalizedString("离线", comment: ""))
}
}
.padding(9)
@@ -698,10 +691,10 @@ private struct RecentConnectionCard: View {
.buttonStyle(.plain)
.contextMenu {
Button(action: connect) {
Text("连接")
Text(NSLocalizedString("连接", comment: ""))
}
Button(role: .destructive, action: delete) {
Label("删除记录", systemImage: "trash")
Label(NSLocalizedString("删除记录", comment: ""), systemImage: "trash")
}
}
}
@@ -722,9 +715,9 @@ private struct PasswordPromptView: View {
VStack(alignment: .leading, spacing: 18) {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("连接远程桌面")
Text(NSLocalizedString("连接远程桌面", comment: ""))
.font(.title2.bold())
Text("对端 ID \(remoteID)")
Text(String(format: NSLocalizedString("对端 ID %1$@", comment: ""), String(describing: remoteID)))
.font(.subheadline.monospacedDigit())
.foregroundStyle(.secondary)
}
@@ -736,15 +729,15 @@ private struct PasswordPromptView: View {
.font(.title2)
.foregroundStyle(.secondary)
}
.accessibilityLabel("关闭")
.accessibilityLabel(NSLocalizedString("关闭", comment: ""))
}
HStack(spacing: 10) {
Group {
if passwordVisible {
TextField("访问密码", text: $password)
TextField(NSLocalizedString("访问密码", comment: ""), text: $password)
} else {
SecureField("访问密码", text: $password)
SecureField(NSLocalizedString("访问密码", comment: ""), text: $password)
}
}
.keyboardType(.asciiCapable)
@@ -759,7 +752,7 @@ private struct PasswordPromptView: View {
Image(systemName: passwordVisible ? "eye.slash" : "eye")
.foregroundStyle(.secondary)
}
.accessibilityLabel(passwordVisible ? "隐藏密码" : "显示密码")
.accessibilityLabel(passwordVisible ? NSLocalizedString("隐藏密码", comment: "") : NSLocalizedString("显示密码", comment: ""))
}
.padding(.horizontal, 13)
.frame(height: 50)
@@ -773,16 +766,16 @@ private struct PasswordPromptView: View {
Toggle(isOn: $rememberPassword) {
VStack(alignment: .leading, spacing: 2) {
Text("保存密码")
Text(NSLocalizedString("保存密码", comment: ""))
.font(.subheadline.weight(.semibold))
Text("密码将安全保存在本机 Keychain 中")
Text(NSLocalizedString("密码将安全保存在本机 Keychain 中", comment: ""))
.font(.caption)
.foregroundStyle(.secondary)
}
}
Button(action: connect) {
Text("连接")
Text(NSLocalizedString("连接", comment: ""))
.font(.headline)
.frame(maxWidth: .infinity)
.frame(height: 48)
@@ -823,16 +816,16 @@ private struct ServerSettingsView: View {
var body: some View {
Form {
Section("鼠标控制") {
Picker("控制模式", selection: $session.mouseControlMode) {
Section(NSLocalizedString("鼠标控制", comment: "")) {
Picker(NSLocalizedString("控制模式", comment: ""), selection: $session.mouseControlMode) {
ForEach(MouseControlMode.allCases) { mode in
Text(mode.title).tag(mode)
}
}
.pickerStyle(.segmented)
}
Section("画面偏好") {
Picker("偏好模式", selection: $session.videoAdaptationPolicy) {
Section(NSLocalizedString("画面偏好", comment: "")) {
Picker(NSLocalizedString("偏好模式", comment: ""), selection: $session.videoAdaptationPolicy) {
ForEach(VideoAdaptationPolicy.allCases) { policy in
Text(policy.title).tag(policy)
}
@@ -840,18 +833,18 @@ private struct ServerSettingsView: View {
.pickerStyle(.segmented)
}
Section {
Toggle("连接建立后锁定横屏", isOn: $session.autoLandscape)
Toggle(NSLocalizedString("连接建立后锁定横屏", comment: ""), isOn: $session.autoLandscape)
} header: {
Text("屏幕方向")
Text(NSLocalizedString("屏幕方向", comment: ""))
} footer: {
Text("默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。")
Text(NSLocalizedString("默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。", comment: ""))
}
Section {
DisclosureGroup(isExpanded: $serverConfigurationExpanded) {
Toggle("使用自定义服务器", isOn: $usesCustomServer)
Toggle(NSLocalizedString("使用自定义服务器", comment: ""), isOn: $usesCustomServer)
if usesCustomServer {
LabeledContent("地址") {
TextField("填写服务器地址", text: $customServerHost)
LabeledContent(NSLocalizedString("地址", comment: "")) {
TextField(NSLocalizedString("填写服务器地址", comment: ""), text: $customServerHost)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.keyboardType(.URL)
@@ -859,21 +852,21 @@ private struct ServerSettingsView: View {
.focused($focusedServerField, equals: .host)
.submitLabel(.next)
.onSubmit { focusedServerField = .port }
.accessibilityLabel("服务器地址")
.accessibilityLabel(NSLocalizedString("服务器地址", comment: ""))
}
LabeledContent("端口") {
TextField("填写端口", text: $customServerPort)
LabeledContent(NSLocalizedString("端口", comment: "")) {
TextField(NSLocalizedString("填写端口", comment: ""), text: $customServerPort)
.keyboardType(.numberPad)
.multilineTextAlignment(.trailing)
.focused($focusedServerField, equals: .port)
.accessibilityLabel("服务器端口")
.accessibilityLabel(NSLocalizedString("服务器端口", comment: ""))
}
}
} label: {
HStack {
Text("服务器")
Text(NSLocalizedString("服务器", comment: ""))
Spacer()
Text(usesCustomServer ? "自定义" : "默认")
Text(usesCustomServer ? NSLocalizedString("自定义", comment: "") : NSLocalizedString("默认", comment: ""))
.foregroundStyle(.secondary)
}
}
@@ -886,9 +879,9 @@ private struct ServerSettingsView: View {
Text(warning)
.foregroundStyle(.orange)
}
Text(!session.hasNetworkConsent ? "完成隐私授权后登记本机身份" : session.localIdentity.isEmpty
? "正在获取本机 ID…"
: "本机 ID \(session.localIdentity)")
Text(!session.hasNetworkConsent ? NSLocalizedString("完成隐私授权后登记本机身份", comment: "") : session.localIdentity.isEmpty
? NSLocalizedString("正在获取本机 ID…", comment: "")
: String(format: NSLocalizedString("本机 ID %1$@", comment: ""), String(describing: session.localIdentity)))
.font(.caption2.monospacedDigit())
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .center)
@@ -901,20 +894,20 @@ private struct ServerSettingsView: View {
ThirdPartyLicensesView(session: session)
} label: {
HStack {
Text("关于")
Text(NSLocalizedString("关于", comment: ""))
Spacer()
if appUpdates.updateAvailable {
Text("新版本").foregroundStyle(.secondary)
Text(NSLocalizedString("新版本", comment: "")).foregroundStyle(.secondary)
UpdateDot()
}
}
}
.accessibilityLabel(appUpdates.updateAvailable ? "关于,有新版本" : "关于")
.accessibilityLabel(appUpdates.updateAvailable ? NSLocalizedString("关于,有新版本", comment: "") : NSLocalizedString("关于", comment: ""))
}
}
.readableContentWidth()
.background(Color(.systemGroupedBackground).ignoresSafeArea())
.navigationTitle("设置")
.navigationTitle(NSLocalizedString("设置", comment: ""))
.navigationBarTitleDisplayMode(.inline)
.scrollDismissesKeyboard(.interactively)
.background {
@@ -943,7 +936,7 @@ private struct ServerSettingsView: View {
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Spacer()
Button("完成") {
Button(NSLocalizedString("完成", comment: "")) {
focusedServerField = nil
}
}
@@ -965,11 +958,11 @@ private struct ServerSettingsView: View {
let host = customServerHost.trimmingCharacters(in: .whitespacesAndNewlines)
let port = customServerPort.trimmingCharacters(in: .whitespacesAndNewlines)
guard !host.isEmpty else {
serverConfigurationError = "请输入服务器地址,或关闭“使用自定义服务器”。"
serverConfigurationError = NSLocalizedString("请输入服务器地址,或关闭“使用自定义服务器”。", comment: "")
return
}
guard let portNumber = Int(port), (1...65535).contains(portNumber) else {
serverConfigurationError = "请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。"
serverConfigurationError = NSLocalizedString("请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。", comment: "")
return
}
serverConfigurationError = nil
+1 -1
View File
@@ -5,7 +5,7 @@
<key>CFBundleDisplayName</key>
<string>CrossDesk</string>
<key>CFBundleDevelopmentRegion</key>
<string>zh_CN</string>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -321,13 +321,13 @@ final class RemoteKeyboardInputBar: UIView {
heightConstraint.isActive = true
let computerButton = UIButton(type: .system)
computerButton.setTitle("电脑键盘", for: .normal)
computerButton.setTitle(NSLocalizedString("电脑键盘", comment: ""), for: .normal)
computerButton.titleLabel?.font = .systemFont(ofSize: 15, weight: .medium)
computerButton.accessibilityLabel = "切换到电脑键盘"
computerButton.accessibilityLabel = NSLocalizedString("切换到电脑键盘", comment: "")
computerButton.addTarget(self, action: #selector(switchToComputer), for: .touchUpInside)
let dismissButton = RemoteKeyboardDismissButton.makeButton()
dismissButton.accessibilityLabel = "收起系统键盘"
dismissButton.accessibilityLabel = NSLocalizedString("收起系统键盘", comment: "")
dismissButton.addTarget(self, action: #selector(hideKeyboard), for: .touchUpInside)
row.axis = .horizontal
@@ -64,8 +64,8 @@ private final class VirtualMouseKey: UIView {
isMultipleTouchEnabled = false
isAccessibilityElement = true
accessibilityTraits = kind == .wheel ? [.adjustable] : [.button]
accessibilityLabel = kind == .left ? "鼠标左键" : kind == .right ? "鼠标右键" : "鼠标滚轮"
accessibilityHint = kind == .wheel ? "上下滑动,可滑出按键范围" : "按住并拖动,可滑出按键范围,松开结束拖拽"
accessibilityLabel = kind == .left ? NSLocalizedString("鼠标左键", comment: "") : kind == .right ? NSLocalizedString("鼠标右键", comment: "") : NSLocalizedString("鼠标滚轮", comment: "")
accessibilityHint = kind == .wheel ? NSLocalizedString("上下滑动,可滑出按键范围", comment: "") : NSLocalizedString("按住并拖动,可滑出按键范围,松开结束拖拽", comment: "")
face.isUserInteractionEnabled = false
face.layer.mask = faceMask
addSubview(face)
+37 -35
View File
@@ -16,7 +16,7 @@ struct PrivacyConsentView: View {
.safeAreaInset(edge: .bottom, spacing: 0) {
consentActions
}
.navigationTitle("隐私政策")
.navigationTitle(NSLocalizedString("隐私政策", comment: ""))
.navigationBarTitleDisplayMode(.inline)
}
}
@@ -27,7 +27,7 @@ struct PrivacyConsentView: View {
VStack(spacing: 12) {
Toggle(isOn: $hasReadPolicy) {
Text("我已阅读并同意《隐私政策》")
Text(NSLocalizedString("我已阅读并同意《隐私政策》", comment: ""))
.font(.subheadline)
}
.toggleStyle(ConsentCheckboxStyle())
@@ -36,7 +36,7 @@ struct PrivacyConsentView: View {
guard hasReadPolicy else { return }
session.acceptNetworkConsent()
} label: {
Text("同意并继续")
Text(NSLocalizedString("同意并继续", comment: ""))
.font(.body.weight(.semibold))
.frame(maxWidth: .infinity, minHeight: 24)
}
@@ -48,7 +48,7 @@ struct PrivacyConsentView: View {
Button {
session.declineNetworkConsent()
} label: {
Text("暂不同意")
Text(NSLocalizedString("暂不同意", comment: ""))
.font(.body)
.frame(maxWidth: .infinity, minHeight: 44)
.contentShape(Rectangle())
@@ -81,8 +81,8 @@ private struct ConsentCheckboxStyle: ToggleStyle {
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.accessibilityLabel("我已阅读并同意隐私政策")
.accessibilityValue(configuration.isOn ? "已勾选" : "未勾选")
.accessibilityLabel(NSLocalizedString("我已阅读并同意隐私政策", comment: ""))
.accessibilityValue(configuration.isOn ? NSLocalizedString("已勾选", comment: "") : NSLocalizedString("未勾选", comment: ""))
}
}
@@ -97,13 +97,13 @@ struct PrivacySettingsSection: View {
PrivacyAuthorizationView(session: session)
} label: {
HStack {
Text("隐私与授权")
Text(NSLocalizedString("隐私与授权", comment: ""))
Spacer()
Text(session.hasNetworkConsent ? "已授权" : "未授权")
Text(session.hasNetworkConsent ? NSLocalizedString("已授权", comment: "") : NSLocalizedString("未授权", comment: ""))
.foregroundStyle(.secondary)
}
}
Toggle("保存远程画面预览", isOn: Binding(
Toggle(NSLocalizedString("保存远程画面预览", comment: ""), isOn: Binding(
get: { session.savesConnectionThumbnails },
set: { enabled in
if enabled { confirmThumbnails = true }
@@ -111,7 +111,7 @@ struct PrivacySettingsSection: View {
}
))
.disabled(!session.hasNetworkConsent || session.thumbnailCleanupInProgress)
Button(session.thumbnailCleanupInProgress ? "正在清除…" : "清除预览图", role: .destructive) {
Button(session.thumbnailCleanupInProgress ? NSLocalizedString("正在清除…", comment: "") : NSLocalizedString("清除预览图", comment: ""), role: .destructive) {
confirmClear = true
}
.disabled(session.thumbnailCleanupInProgress)
@@ -119,21 +119,21 @@ struct PrivacySettingsSection: View {
Text(error).font(.footnote).foregroundStyle(.secondary)
}
} header: {
Text("隐私")
Text(NSLocalizedString("隐私", comment: ""))
}
.alert("保存远程画面预览?", isPresented: $confirmThumbnails) {
Button("开启保存") { session.setSavesConnectionThumbnails(true) }
Button("取消", role: .cancel) { }
.alert(NSLocalizedString("保存远程画面预览?", comment: ""), isPresented: $confirmThumbnails) {
Button(NSLocalizedString("开启保存", comment: "")) { session.setSavesConnectionThumbnails(true) }
Button(NSLocalizedString("取消", comment: ""), role: .cancel) { }
} message: {
Text("预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。")
Text(NSLocalizedString("预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。", comment: ""))
}
.alert("清除预览图?", isPresented: $confirmClear) {
Button("清除", role: .destructive) { session.clearConnectionThumbnails() }
Button("取消", role: .cancel) { }
.alert(NSLocalizedString("清除预览图?", comment: ""), isPresented: $confirmClear) {
Button(NSLocalizedString("清除", comment: ""), role: .destructive) { session.clearConnectionThumbnails() }
Button(NSLocalizedString("取消", comment: ""), role: .cancel) { }
} message: {
Text(session.savesConnectionThumbnails
? "将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。"
: "将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。")
? NSLocalizedString("将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。", comment: "")
: NSLocalizedString("将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。", comment: ""))
}
}
}
@@ -145,33 +145,33 @@ private struct PrivacyAuthorizationView: View {
var body: some View {
Form {
Section {
LabeledContent("授权状态", value: session.hasNetworkConsent ? "已授权" : "未授权")
NavigationLink("隐私政策") { PrivacyPolicyView() }
LabeledContent(NSLocalizedString("授权状态", comment: ""), value: session.hasNetworkConsent ? NSLocalizedString("已授权", comment: "") : NSLocalizedString("未授权", comment: ""))
NavigationLink(NSLocalizedString("隐私政策", comment: "")) { PrivacyPolicyView() }
} footer: {
if !session.hasNetworkConsent {
Text("授权后可使用远程连接。")
Text(NSLocalizedString("授权后可使用远程连接。", comment: ""))
}
}
Section {
if session.hasNetworkConsent {
Button("撤回隐私授权", role: .destructive) {
Button(NSLocalizedString("撤回隐私授权", comment: ""), role: .destructive) {
confirmWithdrawal = true
}
} else {
Button("阅读并授权") { session.showPrivacyNotice() }
Button(NSLocalizedString("阅读并授权", comment: "")) { session.showPrivacyNotice() }
}
}
}
.readableContentWidth()
.background(Color(.systemGroupedBackground).ignoresSafeArea())
.navigationTitle("隐私与授权")
.navigationTitle(NSLocalizedString("隐私与授权", comment: ""))
.navigationBarTitleDisplayMode(.inline)
.alert("是否撤回隐私授权?", isPresented: $confirmWithdrawal) {
Button("撤回授权", role: .destructive) { session.revokeNetworkConsent() }
Button("取消", role: .cancel) { }
.alert(NSLocalizedString("是否撤回隐私授权?", comment: ""), isPresented: $confirmWithdrawal) {
Button(NSLocalizedString("撤回授权", comment: ""), role: .destructive) { session.revokeNetworkConsent() }
Button(NSLocalizedString("取消", comment: ""), role: .cancel) { }
} message: {
Text("将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。")
Text(NSLocalizedString("将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。", comment: ""))
}
}
}
@@ -182,15 +182,17 @@ private struct PrivacyPolicyDocument: View {
guard let url = Bundle.main.url(forResource: "PRIVACY", withExtension: "md"),
let text = try? String(contentsOf: url, encoding: .utf8),
!text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return nil }
// The app's interface is Chinese; show the complete Chinese translation.
// Keep the published policy intact; use its English version outside Chinese locales.
if let start = text.range(of: "## 中文\n"),
let end = text.range(of: "## English", range: start.upperBound..<text.endIndex) {
return String(text[start.upperBound..<end.lowerBound])
let chinese = Bundle.main.preferredLocalizations.first?.hasPrefix("zh") == true
return chinese ? String(text[start.upperBound..<end.lowerBound])
: String(text[end.upperBound...])
}
return text
}()
static var isAvailable: Bool { text != nil }
private static let paragraphs = (text ?? "隐私政策暂时无法读取,请拒绝联网并重新安装应用。")
private static let paragraphs = (text ?? NSLocalizedString("隐私政策暂时无法读取,请拒绝联网并重新安装应用。", comment: ""))
.components(separatedBy: "\n\n")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty && $0 != "---" }
@@ -203,7 +205,7 @@ private struct PrivacyPolicyDocument: View {
Text(paragraph.trimmingCharacters(in: CharacterSet(charactersIn: "# ")))
.font(.headline)
.padding(.top, 4)
} else if paragraph.hasPrefix("更新日期:") {
} else if paragraph.hasPrefix("更新日期:") || paragraph.hasPrefix("Last updated:") {
Text(paragraph)
.font(.caption)
.foregroundStyle(.secondary)
@@ -226,7 +228,7 @@ struct PrivacyPolicyView: View {
ScrollView {
PrivacyPolicyDocument().padding().readableContentWidth()
}
.navigationTitle("隐私政策")
.navigationTitle(NSLocalizedString("隐私政策", comment: ""))
.navigationBarTitleDisplayMode(.inline)
}
}
@@ -9,7 +9,7 @@ enum RemoteHostPlatform: String, Codable {
case .macos: return "macOS"
case .linux: return "Linux"
case .ios: return "iOS"
case .unknown: return "未知"
case .unknown: return NSLocalizedString("未知", comment: "")
}
}
}
@@ -29,8 +29,8 @@ struct RemoteNetworkSnapshot {
var connectionMode: String {
switch report?.mode {
case .direct: return "P2P 直连"
case .relay: return "TURN 中继"
case .direct: return NSLocalizedString("P2P 直连", comment: "")
case .relay: return NSLocalizedString("TURN 中继", comment: "")
default: return "—"
}
}
@@ -37,11 +37,11 @@ struct RemotePrivacyScreen {
return fresh(at: now) && s.state == 3 && s.overlayActive && !s.remotePaused
}
func buttonTitle(at now: TimeInterval) -> String {
if enabled(at: now) { return "隐私屏已开启" }
if enabled(at: now) { return NSLocalizedString("隐私屏已开启", comment: "") }
if fresh(at: now), let s = snapshot, s.supported && s.state == 1 && !active {
return "隐私屏已关闭"
return NSLocalizedString("隐私屏已关闭", comment: "")
}
return "隐私屏"
return NSLocalizedString("隐私屏", comment: "")
}
func feedback(at now: TimeInterval) -> String {
if !desktopAvailable { return "" }
@@ -87,26 +87,26 @@ struct RemotePrivacyScreen {
return requestID
}
mutating func failed(request: UInt32) {
if pending && requestID == request { pending = false; failure = "指令发送失败,请重试" }
if pending && requestID == request { pending = false; failure = NSLocalizedString("指令发送失败,请重试", comment: "") }
}
mutating func tick(at now: TimeInterval) {
if pending && now - requestedAt >= 15 {
pending = false; failure = "请求超时,尚未确认电脑的隐私屏状态"
pending = false; failure = NSLocalizedString("请求超时,尚未确认电脑的隐私屏状态", comment: "")
}
}
func status(at now: TimeInterval) -> String {
if !failure.isEmpty { return failure }
guard let s = snapshot else { return "等待电脑返回隐私屏状态;旧版电脑端可能不支持" }
if !fresh(at: now) { return "隐私屏状态已过期,等待电脑确认" }
if s.state == 2 { return "正在开启隐私屏…" }
if s.state == 4 { return "正在关闭隐私屏…" }
if pending { return "指令已发送,等待电脑确认…" }
if s.state == 5 { return "隐私屏异常" }
if !s.supported || s.state == 0 { return "隐私屏暂不可用" }
guard let s = snapshot else { return NSLocalizedString("等待电脑返回隐私屏状态;旧版电脑端可能不支持", comment: "") }
if !fresh(at: now) { return NSLocalizedString("隐私屏状态已过期,等待电脑确认", comment: "") }
if s.state == 2 { return NSLocalizedString("正在开启隐私屏…", comment: "") }
if s.state == 4 { return NSLocalizedString("正在关闭隐私屏…", comment: "") }
if pending { return NSLocalizedString("指令已发送,等待电脑确认…", comment: "") }
if s.state == 5 { return NSLocalizedString("隐私屏异常", comment: "") }
if !s.supported || s.state == 0 { return NSLocalizedString("隐私屏暂不可用", comment: "") }
if s.state == 3 && s.overlayActive && !s.remotePaused {
return "隐私屏已开启"
return NSLocalizedString("隐私屏已开启", comment: "")
}
return s.state == 1 && !active ? "隐私屏已关闭" : "隐私屏状态待确认"
return s.state == 1 && !active ? NSLocalizedString("隐私屏已关闭", comment: "") : NSLocalizedString("隐私屏状态待确认", comment: "")
}
func detail(at now: TimeInterval) -> String {
guard let s = snapshot, fresh(at: now), s.state == 0 || s.state == 5 || !s.supported else { return "" }
@@ -15,17 +15,17 @@ enum MouseControlMode: String, CaseIterable, Identifiable {
var title: String {
switch self {
case .absolute: return "绝对位置"
case .relative: return "相对位置"
case .absolute: return NSLocalizedString("绝对位置", comment: "")
case .relative: return NSLocalizedString("相对位置", comment: "")
}
}
var detail: String {
switch self {
case .absolute:
return "触摸位置直接对应远端屏幕位置,适合快速定位。"
return NSLocalizedString("触摸位置直接对应远端屏幕位置,适合快速定位。", comment: "")
case .relative:
return "像触控板一样滑动光标,点击时操作当前光标位置。"
return NSLocalizedString("像触控板一样滑动光标,点击时操作当前光标位置。", comment: "")
}
}
@@ -53,20 +53,20 @@ enum VideoAdaptationPolicy: String, CaseIterable, Identifiable {
var title: String {
switch self {
case .frameRatePriority: return "帧率优先"
case .qualityPriority: return "画质优先"
case .balanced: return "平衡"
case .frameRatePriority: return NSLocalizedString("帧率优先", comment: "")
case .qualityPriority: return NSLocalizedString("画质优先", comment: "")
case .balanced: return NSLocalizedString("平衡", comment: "")
}
}
var detail: String {
switch self {
case .frameRatePriority:
return "优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。"
return NSLocalizedString("优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。", comment: "")
case .qualityPriority:
return "始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。"
return NSLocalizedString("始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。", comment: "")
case .balanced:
return "兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。"
return NSLocalizedString("兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。", comment: "")
}
}
@@ -321,8 +321,8 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
private var privacyTimer: Timer?
@Published var remoteID = ""
@Published var password = ""
@Published private(set) var signalStatus = "等待隐私授权"
@Published private(set) var connectionStatus = "未连接"
@Published private(set) var signalStatus = NSLocalizedString("等待隐私授权", comment: "")
@Published private(set) var connectionStatus = NSLocalizedString("未连接", comment: "")
@Published private(set) var localIdentity = ""
@Published private(set) var identityStorageWarning: String?
@Published private(set) var isConnecting = false
@@ -337,7 +337,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
@Published private(set) var hasRemoteCursorState = false
private var networkStatistics = RemoteNetworkStatistics()
private var videoFrameID: UInt64 = 0
@Published private(set) var displays: [String] = ["显示器 1"]
@Published private(set) var displays: [String] = [NSLocalizedString("显示器 1", comment: "")]
@Published private(set) var displaySizes: [CGSize] = []
@Published var selectedDisplay = 0
@Published var audioEnabled = true
@@ -368,7 +368,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
private var remoteVersionCheckAttempted = false
private var remoteVersionCheckGeneration = UUID()
private var remoteVersionCheckTask: URLSessionDataTask?
private static let remoteUpdateNotice = "被控端版本过低,请升级到最新版本。"
private static let remoteUpdateNotice = NSLocalizedString("被控端版本过低,请升级到最新版本。", comment: "")
private var pendingRememberPassword = false
private var connectionRecorded = false
private var shouldCaptureThumbnail = false
@@ -380,7 +380,8 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
private var videoSettingsTimeoutTask: Task<Void, Never>?
private var pendingPresenceRemoteID: String?
private var presenceProbeGeneration: UInt64 = 0
private var signalConnected = false
@Published private(set) var signalConnected = false
@Published private(set) var signalHasError = false
private var presenceIsForeground = false
private var awaitingPresenceSnapshot = true
private var presenceUpdatedAt: [String: ContinuousClock.Instant] = [:]
@@ -425,7 +426,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
self.audioPlayer.setEnabled(playing && self.audioEnabled)
}
pictureInPicture.failed = { [weak self] in
self?.controlStatus = "画中画未能开启,请保持应用在前台后重试。"
self?.controlStatus = NSLocalizedString("画中画未能开启,请保持应用在前台后重试。", comment: "")
}
announcements.send = { [weak bridge] data, requestID in
bridge?.sendAnnouncementRequest(data, requestID: requestID)
@@ -498,7 +499,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
announcements.reset()
bridgeConfiguration = nil
localIdentity = ""
signalStatus = "等待隐私授权"
signalStatus = NSLocalizedString("等待隐私授权", comment: ""); signalHasError = false
savesConnectionThumbnails = false
clearConnectionThumbnails()
}
@@ -526,7 +527,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
self.thumbnailCleanupInProgress = false
self.thumbnailCleanupError = success
? nil
: "部分预览图未能清除,请重试。"
: NSLocalizedString("部分预览图未能清除,请重试。", comment: "")
}
}
@@ -542,14 +543,14 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
@discardableResult
func configureBridge() -> Bool {
guard hasNetworkConsent else {
signalStatus = "等待隐私授权"
signalStatus = NSLocalizedString("等待隐私授权", comment: ""); signalHasError = false
return false
}
let host = signalHost.trimmingCharacters(in: .whitespacesAndNewlines)
guard let signal = Int(signalPort),
(1...65535).contains(signal),
!host.isEmpty else {
signalStatus = "服务器配置无效"
signalStatus = NSLocalizedString("服务器配置无效", comment: ""); signalHasError = true
return false
}
let configuration = BridgeConfiguration(host: host, signalPort: signal)
@@ -558,12 +559,12 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
signalConnected = false
localIdentity = ""
announcements.reset()
signalStatus = "正在连接信令服务"
signalStatus = NSLocalizedString("正在连接信令服务", comment: ""); signalHasError = false
stopPresenceMonitoring()
invalidatePresence()
cancelPendingPresenceConnection()
} else if signalConnected {
signalStatus = "已连接服务器"
signalStatus = NSLocalizedString("已连接服务器", comment: ""); signalHasError = false
}
bridge.setVideoAdaptationPolicy(videoAdaptationPolicy.bridgeValue)
bridge.configure(withSignalHost: host,
@@ -585,7 +586,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
connectionFailureMessage = nil
let identifier = remoteID.trimmingCharacters(in: .whitespacesAndNewlines)
guard !identifier.isEmpty else {
connectionStatus = "请输入远程设备 ID"
connectionStatus = NSLocalizedString("请输入远程设备 ID", comment: "")
return
}
remoteID = identifier
@@ -616,7 +617,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
isConnected = false
sessionVisible = false
resetRemoteCursorState()
connectionStatus = "正在准备连接…"
connectionStatus = NSLocalizedString("正在准备连接…", comment: "")
bridge.connect(toRemoteID: identifier, password: password)
}
@@ -632,7 +633,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
isConnecting = true
isConnected = false
sessionVisible = false
connectionStatus = "正在确认设备状态…"
connectionStatus = NSLocalizedString("正在确认设备状态…", comment: "")
requestPresenceProbe(identifier)
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
@@ -656,7 +657,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
guard pendingPresenceRemoteID != nil else { return }
cancelPresenceProbe()
isConnecting = false
connectionStatus = "未连接"
connectionStatus = NSLocalizedString("未连接", comment: "")
}
private func requestPresenceProbe(_ identifier: String) {
@@ -671,7 +672,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
isConnecting = false
isConnected = false
sessionVisible = false
connectionStatus = "设备离线"
connectionStatus = NSLocalizedString("设备离线", comment: "")
deviceOfflineAlertVisible = true
}
@@ -720,10 +721,10 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
resetRemoteCursorState()
frameCount = 0
networkStatistics = RemoteNetworkStatistics()
displays = ["显示器 1"]
displays = [NSLocalizedString("显示器 1", comment: "")]
displaySizes = []
selectedDisplay = 0
connectionStatus = "未连接"
connectionStatus = NSLocalizedString("未连接", comment: "")
audioPlayer.endSession()
AppOrientation.endSession()
appUpdates.checkNow()
@@ -1006,9 +1007,9 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
var videoSettingsFeedback: String {
if !canChangeVideoSettings {
return "连接建立后可调整,需被控端支持。"
return NSLocalizedString("连接建立后可调整,需被控端支持。", comment: "")
}
if videoSettings.failed { return "调整失败,请重试。" }
if videoSettings.failed { return NSLocalizedString("调整失败,请重试。", comment: "") }
return ""
}
@@ -1077,20 +1078,20 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func sendClipboard() {
guard hasNetworkConsent, isConnected else { return }
guard let text = UIPasteboard.general.string, !text.isEmpty else {
clipboardStatus = "剪贴板中没有文本"
clipboardStatus = NSLocalizedString("剪贴板中没有文本", comment: "")
return
}
guard text.lengthOfBytes(using: .utf8) <= 128 * 1024 else {
clipboardStatus = "剪贴板文本超过 128 KiB"
clipboardStatus = NSLocalizedString("剪贴板文本超过 128 KiB", comment: "")
return
}
bridge.sendClipboardText(text)
clipboardStatus = "已发送本机剪贴板"
clipboardStatus = NSLocalizedString("已发送本机剪贴板", comment: "")
}
func sendFile(_ url: URL) {
guard hasNetworkConsent, isConnected else { return }
transferStatus = "正在发送 \(url.lastPathComponent)"
transferStatus = String(format: NSLocalizedString("正在发送 %1$@", comment: ""), String(describing: url.lastPathComponent))
transferProgress = 0
bridge.sendFile(at: url)
}
@@ -1129,15 +1130,15 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func sendSecureAttention() {
guard hasNetworkConsent, isConnected, remotePlatform == .windows else { return }
guard remoteServiceAvailable else {
controlStatus = "Ctrl+Alt+Del 需要远端启用 Windows 系统服务"
controlStatus = NSLocalizedString("Ctrl+Alt+Del 需要远端启用 Windows 系统服务", comment: "")
return
}
bridge.sendSecureAttentionSequence()
controlStatus = "正在发送 Ctrl+Alt+Del…"
controlStatus = NSLocalizedString("正在发送 Ctrl+Alt+Del…", comment: "")
}
func fileImportFailed() {
transferStatus = "无法读取所选文件,请重试"
transferStatus = NSLocalizedString("无法读取所选文件,请重试", comment: "")
transferProgress = 0
}
@@ -1146,14 +1147,14 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func sendSystemTextEdit(deleting count: Int, inserting text: String) -> Bool {
guard hasNetworkConsent, isConnected else { return false }
guard (0...16 * 1024).contains(count), text.utf8.count <= 16 * 1024, !text.unicodeScalars.contains(where: { $0.value == 0 }) else {
keyboardInputStatus = "单次输入最多 16 KiB,且不能包含空字符"
keyboardInputStatus = NSLocalizedString("单次输入最多 16 KiB,且不能包含空字符", comment: "")
return false
}
let isASCII = text.unicodeScalars.allSatisfy({
(0x20...0x7e).contains($0.value) || [9, 10, 13].contains($0.value)
})
guard supportsTextInput || isASCII else {
keyboardInputStatus = "被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴"
keyboardInputStatus = NSLocalizedString("被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴", comment: "")
return false
}
for _ in 0..<count { sendKeyStroke(0x08) }
@@ -1180,7 +1181,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func rtcBridge(_ bridge: CrossDeskRTCBridge, didChangeIdentityStorageError hasError: Bool) {
identityStorageWarning = hasError
? "本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。"
? NSLocalizedString("本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。", comment: "")
: nil
}
@@ -1191,15 +1192,15 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
announcements.setConnected(signalConnected)
invalidatePresence()
switch state.rawValue {
case 0: signalStatus = "正在连接信令服务"
case 1: signalStatus = "已连接服务器"
case 2: signalStatus = "信令连接失败"
case 3: signalStatus = "信令连接已关闭"
case 4: signalStatus = "信令服务重连中"
case 5: signalStatus = "信令服务器已关闭连接"
case 6: signalStatus = "TLS 证书校验失败"
case 7: signalStatus = "无法访问本机登录凭据,请解锁后重试"
default: signalStatus = "未知信令状态"
case 0: signalStatus = NSLocalizedString("正在连接信令服务", comment: ""); signalHasError = false
case 1: signalStatus = NSLocalizedString("已连接服务器", comment: ""); signalHasError = false
case 2: signalStatus = NSLocalizedString("信令连接失败", comment: ""); signalHasError = true
case 3: signalStatus = NSLocalizedString("信令连接已关闭", comment: ""); signalHasError = true
case 4: signalStatus = NSLocalizedString("信令服务重连中", comment: ""); signalHasError = false
case 5: signalStatus = NSLocalizedString("信令服务器已关闭连接", comment: ""); signalHasError = true
case 6: signalStatus = NSLocalizedString("TLS 证书校验失败", comment: ""); signalHasError = true
case 7: signalStatus = NSLocalizedString("无法访问本机登录凭据,请解锁后重试", comment: ""); signalHasError = true
default: signalStatus = NSLocalizedString("未知信令状态", comment: ""); signalHasError = true
}
if signalConnected {
startPresenceMonitoring()
@@ -1226,9 +1227,9 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
}
isConnecting = [0, 2].contains(state.rawValue)
switch state.rawValue {
case 0: connectionStatus = "正在建立远程连接"
case 0: connectionStatus = NSLocalizedString("正在建立远程连接", comment: "")
case 1:
connectionStatus = "已连接"
connectionStatus = NSLocalizedString("已连接", comment: "")
startPrivacyMonitoring()
sessionVisible = true
pictureInPicture.beginSession()
@@ -1238,27 +1239,27 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
audioPlayer.setEnabled(audioEnabled)
bridge.setAudioEnabled(audioEnabled)
AppOrientation.beginSession(autoLandscape: autoLandscape)
case 2: connectionStatus = "正在收集 ICE 候选"
case 3: finishConnection(withError: "网络连接已中断,请重新连接。")
case 2: connectionStatus = NSLocalizedString("正在收集 ICE 候选", comment: "")
case 3: finishConnection(withError: NSLocalizedString("网络连接已中断,请重新连接。", comment: ""))
case 4:
finishConnection(withError: "P2P/TURN 建链失败,请检查网络后重试。")
finishConnection(withError: NSLocalizedString("P2P/TURN 建链失败,请检查网络后重试。", comment: ""))
case 5:
finishConnection(withError: "远程连接已结束。")
finishConnection(withError: NSLocalizedString("远程连接已结束。", comment: ""))
case 6:
finishConnection(withError: "访问密码错误,请重新输入。")
finishConnection(withError: NSLocalizedString("访问密码错误,请重新输入。", comment: ""))
case 7:
if !remoteID.isEmpty {
recentConnectionPresence[remoteID] = false
presenceUpdatedAt.removeValue(forKey: remoteID)
}
finishConnection(withError: "远程设备 ID 不存在,请检查设备 ID。")
finishConnection(withError: NSLocalizedString("远程设备 ID 不存在,请检查设备 ID。", comment: ""))
case 8:
if !remoteID.isEmpty {
recentConnectionPresence[remoteID] = false
presenceUpdatedAt.removeValue(forKey: remoteID)
}
finishConnection(withError: "远程设备当前不可用,请稍后重试。")
default: connectionStatus = "未知连接状态"
finishConnection(withError: NSLocalizedString("远程设备当前不可用,请稍后重试。", comment: ""))
default: connectionStatus = NSLocalizedString("未知连接状态", comment: "")
}
}
@@ -1383,7 +1384,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
updateActiveConnectionHostInfo(hostName)
let names = remoteDisplayNames.enumerated().map { index, displayName in
let name = displayName.trimmingCharacters(in: .whitespacesAndNewlines)
return name.isEmpty ? "显示器 \(index + 1)" : name
return name.isEmpty ? String(format: NSLocalizedString("显示器 %1$@", comment: ""), String(describing: index + 1)) : name
}
if !names.isEmpty {
displays = names
@@ -1437,7 +1438,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func rtcBridge(_ bridge: CrossDeskRTCBridge, didReceiveClipboardText text: String) {
guard hasNetworkConsent, isConnected else { return }
UIPasteboard.general.string = text
clipboardStatus = "已接收远端剪贴板"
clipboardStatus = NSLocalizedString("已接收远端剪贴板", comment: "")
}
func rtcBridge(_ bridge: CrossDeskRTCBridge, didReceiveServiceAvailable available: Bool,
@@ -1449,7 +1450,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
func rtcBridge(_ bridge: CrossDeskRTCBridge, didSendSecureAttention success: Bool) {
guard hasNetworkConsent, isConnected else { return }
controlStatus = success ? "已发送 Ctrl+Alt+Del" : "指令发送失败,请重试"
controlStatus = success ? NSLocalizedString("已发送 Ctrl+Alt+Del", comment: "") : NSLocalizedString("指令发送失败,请重试", comment: "")
}
func rtcBridge(_ bridge: CrossDeskRTCBridge,
@@ -1458,19 +1459,19 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
sending: Bool) {
guard hasNetworkConsent, isConnected else { return }
if progress < 0 {
transferStatus = "\(fileName) 传输失败"
transferStatus = String(format: NSLocalizedString("%1$@ 传输失败", comment: ""), String(describing: fileName))
transferProgress = 0
return
}
transferProgress = progress
let percent = Int((progress * 100).rounded())
transferStatus = "\(sending ? "发送" : "接收") \(fileName) · \(percent)%"
transferStatus = "\(NSLocalizedString(sending ? "发送" : "接收", comment: "")) \(fileName) · \(percent)%"
}
func rtcBridge(_ bridge: CrossDeskRTCBridge, didReceiveFileAt fileURL: URL) {
guard hasNetworkConsent, isConnected else { return }
receivedFileURL = fileURL
transferStatus = "已接收 \(fileURL.lastPathComponent)"
transferStatus = String(format: NSLocalizedString("已接收 %1$@", comment: ""), String(describing: fileURL.lastPathComponent))
transferProgress = 1
}
@@ -1518,8 +1519,8 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
var videoStatus: String {
if frameCount > 0 {
return "视频 \(Int(frameSize.width))×\(Int(frameSize.height)) · \(frameCount) 帧"
return String(format: NSLocalizedString("视频 %1$@×%2$@ · %3$@ 帧", comment: ""), String(describing: Int(frameSize.width)), String(describing: Int(frameSize.height)), String(describing: frameCount))
}
return isConnected ? "正在取回远程画面…" : "等待连接"
return isConnected ? NSLocalizedString("正在取回远程画面…", comment: "") : NSLocalizedString("等待连接", comment: "")
}
}
@@ -189,11 +189,11 @@ struct RemoteSessionView: View {
.zIndex(50)
}
}
.alert("升级提示", isPresented: Binding(
.alert(NSLocalizedString("升级提示", comment: ""), isPresented: Binding(
get: { !session.privacyScreen.unlockNotice && session.remoteUpdateMessage != nil },
set: { if !$0 && !session.privacyScreen.unlockNotice { session.dismissRemoteUpdate() } }
)) {
Button("知道了", role: .cancel) { session.dismissRemoteUpdate() }
Button(NSLocalizedString("知道了", comment: ""), role: .cancel) { session.dismissRemoteUpdate() }
} message: {
Text(session.remoteUpdateMessage ?? "")
}
@@ -282,18 +282,18 @@ struct RemoteSessionView: View {
Color.black.opacity(0.28).ignoresSafeArea().contentShape(Rectangle()).onTapGesture { }
VStack(spacing: 0) {
VStack(spacing: 12) {
Text("隐私屏已被解锁").font(.headline)
Text("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。")
Text(NSLocalizedString("隐私屏已被解锁", comment: "")).font(.headline)
Text(NSLocalizedString("被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。", comment: ""))
.font(.subheadline).foregroundStyle(.secondary)
let feedback = session.privacyScreen.feedback(at: session.privacyClock)
if !feedback.isEmpty { Text(feedback).font(.caption).foregroundStyle(.secondary) }
}.padding(24)
Divider()
HStack(spacing: 0) {
Button("确认") { session.privacyScreen.unlockNotice = false }
Button(NSLocalizedString("确认", comment: "")) { session.privacyScreen.unlockNotice = false }
.frame(maxWidth: .infinity, minHeight: 48)
Divider().frame(height: 48)
Button("锁定") { session.lockPrivacyScreen() }
Button(NSLocalizedString("锁定", comment: "")) { session.lockPrivacyScreen() }
.fontWeight(.semibold).frame(maxWidth: .infinity, minHeight: 48)
.disabled(!session.privacyScreen.canLock(at: session.privacyClock))
}
@@ -320,11 +320,11 @@ struct RemoteSessionView: View {
VStack(spacing: 0) {
VStack(spacing: 4) {
Text("断开远程连接?")
Text(NSLocalizedString("断开远程连接?", comment: ""))
.font(.headline)
.foregroundStyle(Color.black.opacity(0.9))
Text("断开后将返回首页")
Text(NSLocalizedString("断开后将返回首页", comment: ""))
.font(.subheadline)
.foregroundStyle(Color.black.opacity(0.55))
}
@@ -337,7 +337,7 @@ struct RemoteSessionView: View {
disconnectConfirmationVisible = false
session.disconnect()
} label: {
Text("断开连接")
Text(NSLocalizedString("断开连接", comment: ""))
.font(.body.weight(.semibold))
.foregroundStyle(Color.red)
.frame(maxWidth: .infinity)
@@ -351,7 +351,7 @@ struct RemoteSessionView: View {
disconnectConfirmationVisible = false
}
} label: {
Text("取消")
Text(NSLocalizedString("取消", comment: ""))
.font(.body.weight(.semibold))
.foregroundStyle(Color.blue)
.frame(maxWidth: .infinity)
@@ -765,8 +765,8 @@ private struct RemoteComputerKey {
if let title { return title.replacingOccurrences(of: "\n", with: " ") }
switch symbol {
case "shift": return "Shift"
case "delete.left": return "退格"
default: return "键盘操作"
case "delete.left": return NSLocalizedString("退格", comment: "")
default: return NSLocalizedString("键盘操作", comment: "")
}
}
@@ -842,27 +842,27 @@ private struct RemoteComputerKeyboard: View {
.init("Ctrl", 0x11, isModifier: true),
.init("Win", 0x5B, isModifier: true),
.init("Alt", 0x12, isModifier: true),
.init("空格", 0x20, width: 2.6),
.init("回车", 0x0D, width: 1.4, style: .special)
.init(NSLocalizedString("空格", comment: ""), 0x20, width: 2.6),
.init(NSLocalizedString("回车", comment: ""), 0x0D, width: 1.4, style: .special)
]
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 4) {
Button("系统键盘") {
Button(NSLocalizedString("系统键盘", comment: "")) {
feedback.keyPressed()
releaseModifiers()
switchKeyboard()
}
.font(.system(size: 15, weight: .medium))
.frame(width: 80, height: 44)
.accessibilityLabel("切换到系统键盘")
.accessibilityLabel(NSLocalizedString("切换到系统键盘", comment: ""))
if verticalSizeClass == .compact {
pageTabs
} else {
Spacer(minLength: 0)
}
RemoteKeyboardDismissButton(accessibilityLabel: "收起电脑键盘") {
RemoteKeyboardDismissButton(accessibilityLabel: NSLocalizedString("收起电脑键盘", comment: "")) {
feedback.keyPressed()
releaseModifiers()
dismiss()
@@ -943,14 +943,14 @@ private struct RemoteComputerKeyboard: View {
feedback.keyPressed()
page = item
} label: {
Text(item.rawValue)
Text(NSLocalizedString(item.rawValue, comment: ""))
.font(.system(size: 14, weight: page == item ? .semibold : .regular))
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(page == item ? RemoteKeyboardPalette.specialKey : .clear,
in: RoundedRectangle(cornerRadius: 6))
.contentShape(Rectangle())
}
.accessibilityLabel("\(item.rawValue)键盘页")
.accessibilityLabel(String(format: NSLocalizedString("%1$@键盘页", comment: ""), NSLocalizedString(item.rawValue, comment: "")))
.accessibilityAddTraits(page == item ? .isSelected : [])
}
}
@@ -1066,7 +1066,7 @@ private struct CrossDeskStatusOrb: View {
lineWidth: isExpanded ? 2 : 1)
}
.compositingGroup()
.accessibilityLabel(isExpanded ? "收起远程控制菜单" : "展开远程控制菜单")
.accessibilityLabel(isExpanded ? NSLocalizedString("收起远程控制菜单", comment: "") : NSLocalizedString("展开远程控制菜单", comment: ""))
}
}
@@ -1194,8 +1194,8 @@ private struct FloatingSessionMenu: View {
.frame(width: 28, height: 32)
}
.buttonStyle(.plain)
.accessibilityLabel("返回控制栏")
Text(page == .videoSettings ? "画面设置" : "网络状态")
.accessibilityLabel(NSLocalizedString("返回控制栏", comment: ""))
Text(page == .videoSettings ? NSLocalizedString("画面设置", comment: "") : NSLocalizedString("网络状态", comment: ""))
.font(.subheadline.weight(.semibold))
} else {
Circle()
@@ -1212,7 +1212,7 @@ private struct FloatingSessionMenu: View {
.foregroundStyle(.secondary)
}
.buttonStyle(.plain)
.accessibilityLabel("关闭控制栏")
.accessibilityLabel(NSLocalizedString("关闭控制栏", comment: ""))
}
if page == .videoSettings, !session.videoSettingsFeedback.isEmpty {
@@ -1232,12 +1232,12 @@ private struct FloatingSessionMenu: View {
} else {
VStack(spacing: 9) {
LazyVGrid(columns: columns, spacing: 7) {
FloatingControlButton(title: "键盘", symbol: "keyboard",
FloatingControlButton(title: NSLocalizedString("键盘", comment: ""), symbol: "keyboard",
action: showKeyboard)
FloatingControlButton(title: virtualMouseVisible ? "收起鼠标" : "虚拟鼠标",
FloatingControlButton(title: virtualMouseVisible ? NSLocalizedString("收起鼠标", comment: "") : NSLocalizedString("虚拟鼠标", comment: ""),
symbol: "computermouse", action: toggleMouse)
FloatingControlButton(
title: session.mouseControlMode == .relative ? "相对鼠标" : "绝对鼠标",
title: session.mouseControlMode == .relative ? NSLocalizedString("相对鼠标", comment: "") : NSLocalizedString("绝对鼠标", comment: ""),
symbol: "computermouse",
action: {
session.mouseControlMode = session.mouseControlMode == .relative
@@ -1259,22 +1259,22 @@ private struct FloatingSessionMenu: View {
}
}
} label: {
FloatingControlLabel(title: "显示器", symbol: "display")
FloatingControlLabel(title: NSLocalizedString("显示器", comment: ""), symbol: "display")
}
.buttonStyle(.plain)
FloatingControlButton(title: "画面设置", symbol: "slider.horizontal.3") {
FloatingControlButton(title: NSLocalizedString("画面设置", comment: ""), symbol: "slider.horizontal.3") {
page = .videoSettings
}
FloatingControlButton(
title: session.audioEnabled ? "声音" : "静音",
title: session.audioEnabled ? NSLocalizedString("声音", comment: "") : NSLocalizedString("静音", comment: ""),
symbol: session.audioEnabled ? "speaker.wave.2" : "speaker.slash",
action: session.toggleAudio
)
FloatingControlButton(title: "发送文件",
FloatingControlButton(title: NSLocalizedString("发送文件", comment: ""),
symbol: "folder.badge.plus",
action: chooseFile)
FloatingControlButton(title: "网络状态", symbol: "chart.bar.xaxis") {
FloatingControlButton(title: NSLocalizedString("网络状态", comment: ""), symbol: "chart.bar.xaxis") {
page = .networkStats
}
if session.remotePlatform == .windows {
@@ -1282,7 +1282,7 @@ private struct FloatingSessionMenu: View {
symbol: "lock.trianglebadge.exclamationmark",
action: session.sendSecureAttention)
}
FloatingControlButton(title: "剪贴板", symbol: "doc.on.clipboard",
FloatingControlButton(title: NSLocalizedString("剪贴板", comment: ""), symbol: "doc.on.clipboard",
action: session.sendClipboard)
if session.privacyScreen.desktopAvailable {
FloatingControlButton(
@@ -1293,9 +1293,9 @@ private struct FloatingSessionMenu: View {
)
.disabled(!session.isConnected || !session.privacyScreen.canToggle(at: session.privacyClock))
.opacity(session.isConnected && session.privacyScreen.canToggle(at: session.privacyClock) ? 1 : 0.45)
.accessibilityLabel("隐私屏")
.accessibilityLabel(NSLocalizedString("隐私屏", comment: ""))
.accessibilityValue(session.privacyScreen.status(at: session.privacyClock))
.accessibilityHint(session.privacyScreen.active ? "关闭隐私屏" : "开启隐私屏")
.accessibilityHint(session.privacyScreen.active ? NSLocalizedString("关闭隐私屏", comment: "") : NSLocalizedString("开启隐私屏", comment: ""))
}
}
@@ -1335,7 +1335,7 @@ private struct FloatingSessionMenu: View {
private var disconnectButton: some View {
Button(role: .destructive, action: disconnect) {
Text("断开连接")
Text(NSLocalizedString("断开连接", comment: ""))
.font(.subheadline.weight(.semibold))
.frame(maxWidth: .infinity)
.frame(height: 34)
@@ -1393,12 +1393,12 @@ private struct FloatingNetworkStatistics: View {
var body: some View {
VStack(spacing: 10) {
VStack(spacing: 0) {
trafficRow("", inbound: "接收", outbound: "发送", loss: "丢包率")
trafficRow("", inbound: NSLocalizedString("接收", comment: ""), outbound: NSLocalizedString("发送", comment: ""), loss: NSLocalizedString("丢包率", comment: ""))
.foregroundStyle(.secondary)
trafficRow("视频", statistics: snapshot.report?.video)
trafficRow("音频", statistics: snapshot.report?.audio)
trafficRow("数据", statistics: snapshot.report?.data)
trafficRow("合计", statistics: snapshot.report?.total)
trafficRow(NSLocalizedString("视频", comment: ""), statistics: snapshot.report?.video)
trafficRow(NSLocalizedString("音频", comment: ""), statistics: snapshot.report?.audio)
trafficRow(NSLocalizedString("数据", comment: ""), statistics: snapshot.report?.data)
trafficRow(NSLocalizedString("合计", comment: ""), statistics: snapshot.report?.total)
.fontWeight(.semibold)
.background(Color.blue.opacity(0.07), in: RoundedRectangle(cornerRadius: 5))
}
@@ -1407,17 +1407,17 @@ private struct FloatingNetworkStatistics: View {
Divider()
VStack(spacing: 7) {
detail("帧率", value: "\(snapshot.framesPerSecond) FPS")
detail("分辨率", value: resolution)
detail("画面延时", value: RemoteNetworkSnapshot.latency(snapshot.videoLatencyMilliseconds))
detail("连接延时(RTT)", value: RemoteNetworkSnapshot.latency(snapshot.rttMilliseconds))
detail("连接方式", value: snapshot.connectionMode)
detail(NSLocalizedString("帧率", comment: ""), value: "\(snapshot.framesPerSecond) FPS")
detail(NSLocalizedString("分辨率", comment: ""), value: resolution)
detail(NSLocalizedString("画面延时", comment: ""), value: RemoteNetworkSnapshot.latency(snapshot.videoLatencyMilliseconds))
detail(NSLocalizedString("连接延时(RTT)", comment: ""), value: RemoteNetworkSnapshot.latency(snapshot.rttMilliseconds))
detail(NSLocalizedString("连接方式", comment: ""), value: snapshot.connectionMode)
HStack {
Text("媒体加密")
Text(NSLocalizedString("媒体加密", comment: ""))
.foregroundStyle(.secondary)
Spacer(minLength: 8)
if let report = snapshot.report {
Label(report.srtpActive ? "SRTP 已启用" : "未启用",
Label(report.srtpActive ? NSLocalizedString("SRTP 已启用", comment: "") : NSLocalizedString("未启用", comment: ""),
systemImage: report.srtpActive ? "lock.shield" : "lock.open")
.foregroundStyle(report.srtpActive ? Color.green : Color.secondary)
} else {
@@ -1425,8 +1425,8 @@ private struct FloatingNetworkStatistics: View {
}
}
.accessibilityElement(children: .combine)
codec("被控端编码器", value: snapshot.remoteVideoEncoder)
codec("控制端解码器", value: snapshot.localVideoDecoder)
codec(NSLocalizedString("被控端编码器", comment: ""), value: snapshot.remoteVideoEncoder)
codec(NSLocalizedString("控制端解码器", comment: ""), value: snapshot.localVideoDecoder)
}
.font(.caption.monospacedDigit())
}
@@ -1460,13 +1460,13 @@ private struct FloatingNetworkStatistics: View {
inbound: RemoteNetworkSnapshot.bitrate(statistics?.inboundBitrate),
outbound: RemoteNetworkSnapshot.bitrate(statistics?.outboundBitrate),
loss: RemoteNetworkSnapshot.lossRate(statistics?.lossRate))
.accessibilityLabel("\(title),接收 \(RemoteNetworkSnapshot.bitrate(statistics?.inboundBitrate)),发送 \(RemoteNetworkSnapshot.bitrate(statistics?.outboundBitrate)),丢包率 \(RemoteNetworkSnapshot.lossRate(statistics?.lossRate))")
.accessibilityLabel(String(format: NSLocalizedString("%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@", comment: ""), String(describing: title), String(describing: RemoteNetworkSnapshot.bitrate(statistics?.inboundBitrate)), String(describing: RemoteNetworkSnapshot.bitrate(statistics?.outboundBitrate)), String(describing: RemoteNetworkSnapshot.lossRate(statistics?.lossRate))))
}
private func trafficRow(_ title: String, inbound: String,
outbound: String, loss: String) -> some View {
HStack(spacing: 4) {
Text(title).frame(width: 28, alignment: .leading)
Text(title).frame(width: 48, alignment: .leading)
Text(inbound).frame(maxWidth: .infinity, alignment: .trailing)
Text(outbound).frame(maxWidth: .infinity, alignment: .trailing)
Text(loss).frame(width: 46, alignment: .trailing)
@@ -1484,17 +1484,17 @@ private struct FloatingVideoSettings: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
settingRow("画面质量", options: ["低", "中", "高"],
settingRow(NSLocalizedString("画面质量", comment: ""), options: [NSLocalizedString("低", comment: ""), NSLocalizedString("中", comment: ""), NSLocalizedString("高", comment: "")],
values: [0, 1, 2], selection: Binding(
get: { session.videoSettings.selection.quality },
set: { session.updateVideoSettings(quality: $0) }
))
settingRow("画面采集帧率", options: ["30 fps", "60 fps"],
settingRow(NSLocalizedString("画面采集帧率", comment: ""), options: ["30 fps", "60 fps"],
values: [30, 60], selection: Binding(
get: { session.videoSettings.selection.frameRate },
set: { session.updateVideoSettings(frameRate: $0) }
))
settingRow("画面偏好", options: VideoAdaptationPolicy.allCases.map(\.title),
settingRow(NSLocalizedString("画面偏好", comment: ""), options: VideoAdaptationPolicy.allCases.map(\.title),
values: VideoAdaptationPolicy.allCases.map { $0.bridgeValue.rawValue },
selection: Binding(
get: { session.videoSettings.selection.preference },
@@ -1546,7 +1546,8 @@ private struct FloatingControlLabel: View {
.font(.system(size: 16, weight: .semibold))
Text(title)
.font(.system(size: 9.5, weight: .medium))
.lineLimit(1)
.lineLimit(2)
.multilineTextAlignment(.center)
.minimumScaleFactor(0.72)
}
.foregroundStyle(.primary)
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk nutzt das lokale Netzwerk, um native P2P-Fernsteuerungsverbindungen zu erkennen und aufzubauen.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "%1$@-Übertragung fehlgeschlagen";
"%1$@ 台设备" = "Geräte: %1$@";
"%1$@ 条未读" = "%1$@ ungelesene Elemente";
"%1$@键盘页" = "%1$@ Tastaturseite";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, Empfangen von %2$@, Senden von %3$@, Paketverlustrate %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk nutzt das lokale Netzwerk, um native P2P-Fernsteuerungsverbindungen zu erkennen und aufzubauen.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del erfordert eine Remote-Aktivierung der Windows-Systemdienste";
"GNU GPL 第 3 版" = "GNU GPL Version 3";
"OpenFEC 告知" = "OpenFEC-Hinweis";
"OpenFEC 许可与源码" = "OpenFEC Lizenz und Quelle";
"P2P 直连" = "P2P-Direktverbindung";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Verbindungsaufbau fehlgeschlagen, bitte überprüfen Sie das Netzwerk und versuchen Sie es erneut.";
"SRTP 已启用" = "SRTP enabled";
"TLS 证书校验失败" = "Die Überprüfung des TLS-Zertifikats ist fehlgeschlagen";
"TURN 中继" = "TURN-Relais";
"上下滑动,可滑出按键范围" = "Nach oben und unten schieben, um aus dem Tastenbereich herauszurutschen";
"丢包率" = "Verlustrate";
"中" = "Mittel";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Priorisiert die Aufrechterhaltung eines flüssigen Bildes und reduziert die Auflösung stärker, wenn die Bandbreite oder der Codierungsdruck hoch ist.";
"低" = "Niedrig";
"使用自定义服务器" = "Verwendung eines benutzerdefinierten Servers";
"保存密码" = "Passwort speichern";
"保存远程画面预览" = "Remote-Bildschirmvorschauen speichern";
"保存远程画面预览?" = "Remote-Bildschirmvorschau speichern?";
"信令服务器已关闭连接" = "Der Signalisierungsserver hat die Verbindung geschlossen";
"信令服务重连中" = "Signalisierungsdienst stellt erneut Verbindung her";
"信令连接失败" = "Signalverbindung fehlgeschlagen";
"信令连接已关闭" = "Signalverbindung geschlossen";
"偏好模式" = "Präferenzmodus";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Bewegen Sie den Cursor wie ein Trackpad und bedienen Sie die aktuelle Cursorposition, wenn Sie darauf klicken.";
"公告加载失败,请重试。" = "Das Laden der Ankündigung ist fehlgeschlagen. Bitte versuchen Sie es erneut.";
"公告详情" = "Einzelheiten zur Ankündigung";
"共 %1$@ 条 · %2$@ 条未读" = "Insgesamt %1$@ Artikel ·%2$@ ungelesene Artikel";
"关于" = "Ungefähr";
"关于 CrossDesk" = "Über CrossDesk";
"关于,有新版本" = "Ungefähr, es gibt eine neue Version";
"关闭" = "Schließen";
"关闭控制栏" = "Bedienbügel schließen";
"关闭隐私屏" = "Privatsphäre-Bildschirm deaktivieren";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Gleicht Klarheit und Glätte aus und verringert die Auflösung bei konstantem Druck nur allmählich.";
"分辨率" = "Auflösung";
"切换到电脑键盘" = "Zur Computertastatur wechseln";
"切换到系统键盘" = "Zur Systemtastatur wechseln";
"删除" = "Löschen";
"删除公告" = "Ansage löschen";
"删除记录" = "Datensatz löschen";
"删除这条公告?" = "Diese Ankündigung löschen?";
"刷新公告" = "Ankündigung aktualisieren";
"剪贴板" = "Zwischenablage";
"剪贴板中没有文本" = "Kein Text in der Zwischenablage";
"剪贴板文本超过 128 KiB" = "Der Text in der Zwischenablage ist größer als 128 KiB";
"加载中…" = "Laden…";
"加载更多" = "Mehr laden";
"升级提示" = "Update empfohlen";
"单次输入最多 16 KiB,且不能包含空字符" = "Eine einzelne Eingabe kann bis zu 16 KiB groß sein und darf keine Nullzeichen enthalten";
"发送" = "Senden";
"发送文件" = "Datei senden";
"取消" = "Abbrechen";
"取消连接" = "Verbindung abbrechen";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Jede Komponente unterliegt der aufgeführten Open-Source-Lizenz und die gewährten Rechte unterliegen nicht den allgemeinen Nutzungsbedingungen dieser Anwendung. Für die Anzeige des Quellcodes ist eine Internetverbindung erforderlich.";
"合计" = "Insgesamt";
"同意并继续" = "Zustimmen und fortfahren";
"回车" = "Treten Sie ein";
"在线" = "Online";
"地址" = "Adresse";
"填写服务器地址" = "Geben Sie die Serveradresse ein";
"填写端口" = "Füllen Sie den Port aus";
"声音" = "Ton";
"失败" = "Fehlgeschlagen";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Behalten Sie immer eine höhere Auflösung bei und reduzieren Sie die Bildrate, um die Bildschärfe zu gewährleisten, wenn die Bandbreite nicht ausreicht.";
"媒体加密" = "Medienverschlüsselung";
"字母数字" = "Buchstaben & Zahlen";
"完成" = "Fertig";
"完成隐私授权后可检查更新" = "Sie können nach Abschluss der Datenschutzautorisierung nach Updates suchen";
"完成隐私授权后登记本机身份" = "Registrieren Sie die lokale Identität, nachdem Sie die Datenschutzautorisierung abgeschlossen haben";
"密码将安全保存在本机 Keychain 中" = "Passwörter werden sicher im lokalen Schlüsselbund gespeichert";
"对端 ID" = "Remote-ID";
"对端 ID %1$@" = "Peer-ID %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Stoppt die Remote-Verbindung, schließt die Bildschirmvorschau, speichert sie und löscht die vorhandene Vorschau. Verbindungsdatensätze und empfangene Dateien bleiben erhalten und vorhandene Serverdaten werden nicht gelöscht. Sie können sich jederzeit erneut autorisieren.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Alle auf diesem Computer gespeicherten Remote-Vorschauen werden gelöscht, während Verbindungsdatensätze, Passwörter und empfangene Dateien erhalten bleiben.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Alle auf diesem Computer gespeicherten Remote-Vorschauen werden gelöscht, während Verbindungsdatensätze, Passwörter und empfangene Dateien erhalten bleiben. Bei späteren Verbindungen werden weiterhin neue Vorschauen gespeichert.";
"屏幕方向" = "Bildschirmausrichtung";
"展开远程控制菜单" = "Fernbedienungsmenü erweitern";
"已保存密码" = "Passwort gespeichert";
"已勾选" = "Geprüft";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del gesendet";
"已发送本机剪贴板" = "Lokale Zwischenablage gesendet";
"已授权" = "Zugegeben";
"已接收 %1$@" = "%1$@ erhalten";
"已接收远端剪贴板" = "Remote-Zwischenablage empfangen";
"已读" = "Lesen";
"已连接" = "Verbunden";
"已连接服务器" = "Mit Server verbunden";
"帧率" = "Bildrate";
"帧率优先" = "Flüssig";
"平衡" = "Balance";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "Die Lizenzdatei in der Anwendung fehlt oder ist beschädigt. Bitte installieren Sie die Anwendung neu.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "Die Entwicklungsversion enthält Änderungen, die noch nicht veröffentlicht wurden. Der folgende Link ist der Quellcode der Basisversion.";
"开启保存" = "Speichern aktivieren";
"开启隐私屏" = "Privatsphärenschutz aktivieren";
"开源组件" = "Open-Source-Komponenten";
"开源软件权利说明" = "Beschreibung der Open-Source-Softwarerechte";
"当前已是最新版本" = "Dies ist derzeit die neueste Version";
"成功连接后,这里会显示远端桌面的缩略图。" = "Nach erfolgreicher Verbindung wird hier eine Miniaturansicht des Remote-Desktops angezeigt.";
"我已阅读并同意《隐私政策》" = "Ich habe die Datenschutzerklärung gelesen und bin damit einverstanden";
"我已阅读并同意隐私政策" = "Ich habe die Datenschutzbestimmungen gelesen und stimme ihnen zu";
"打开隐私政策" = "Datenschutzerklärung öffnen";
"指令发送失败,请重试" = "Das Senden des Befehls ist fehlgeschlagen. Bitte versuchen Sie es erneut";
"指令已发送,等待电脑确认…" = "Der Befehl wurde gesendet und wartet auf die Bestätigung des Computers ...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Drücken und ziehen Sie, um aus dem Tastenbereich zu gleiten, und lassen Sie los, um das Ziehen zu beenden";
"授权后可使用远程连接。" = "Remote-Verbindungen sind nach Autorisierung verfügbar.";
"授权状态" = "Autorisierungsstatus";
"接收" = "Empfangen";
"控制模式" = "Steuermodus";
"控制端解码器" = "Lokaler Decoder";
"撤回授权" = "Autorisierung entziehen";
"撤回隐私授权" = "Datenschutzberechtigung entziehen";
"收起电脑键盘" = "PC-Tastatur ausblenden";
"收起系统键盘" = "Systemtastatur einklappen";
"收起远程控制菜单" = "Fernbedienungsmenü einklappen";
"收起鼠标" = "Maus ausblenden";
"数据" = "Daten";
"断开后将返回首页" = "Kehrt nach dem Trennen der Verbindung zur Startseite zurück";
"断开远程连接?" = "Remote-Verbindung trennen?";
"断开连接" = "Trennen";
"新版本" = "Neue Version";
"新版本可用:v%1$@" = "Neue Version verfügbar: v%1$@";
"无效" = "Ungültig";
"无未读公告" = "Keine ungelesenen Ankündigungen";
"无法" = "Unfähig";
"无法保存本地阅读状态,请重试。" = "Lesestatus kann nicht lokal gespeichert werden. Bitte versuchen Sie es erneut.";
"无法删除这条公告,请重试。" = "Diese Ankündigung kann nicht gelöscht werden. Bitte versuchen Sie es erneut.";
"无法访问本机登录凭据,请解锁后重试" = "Der Zugriff auf die lokalen Anmeldedaten ist nicht möglich. Bitte entsperren Sie die Sperre und versuchen Sie es erneut";
"无法读取开源许可" = "Open-Source-Lizenz kann nicht gelesen werden";
"无法读取所选文件,请重试" = "Die ausgewählte Datei konnte nicht gelesen werden. Bitte versuchen Sie es erneut";
"是否撤回隐私授权?" = "Datenschutzberechtigung entziehen?";
"显示器" = "Bildschirm";
"显示器 %1$@" = "Monitor %1$@";
"显示器 1" = "Monitor 1";
"显示密码" = "Passwort anzeigen";
"暂不同意" = "Jetzt nicht";
"暂无公告" = "Keine Ankündigungen";
"最近连接" = "Letzte Verbindungen";
"服务器" = "Server";
"服务器地址" = "Serveradresse";
"服务器端口" = "Server-Port";
"服务器配置无效" = "Ungültige Serverkonfiguration";
"未勾选" = "Nicht überprüft";
"未启用" = "Nicht aktiviert";
"未授权" = "Nicht autorisiert";
"未知" = "Unbekannt";
"未知信令状态" = "Unbekannter Signalisierungsstatus";
"未知连接状态" = "Unbekannter Verbindungsstatus";
"未读" = "Ungelesen";
"未连接" = "Nicht verbunden";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Diese Anwendung verwendet OpenFEC zur Vorwärtsfehlerkorrektur unterCeCILL-C1.0, was eingeschränkte Garantien und Haftungsbeschränkungen bietet. Einzelheiten finden Sie in der vollständigen Lizenz.";
"本机 ID %1$@" = "Lokale ID %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "Die Anmeldeinformationen für diesen Computer sind vorübergehend unsicher. Bitte entsperren Sie Ihr Gerät und klicken Sie in den Einstellungen auf „Übernehmen“, um es erneut zu versuchen.";
"本版本源码与构建说明" = "Quellcode und Build-Anweisungen für diese Version";
"本版本源码信息暂不可用。" = "Die Quellrevision ist für diesen Build nicht verfügbar.";
"构建说明" = "Bauanleitung";
"构建配方与项目补丁" = "Erstellen Sie Rezepte und Projekt-Patches";
"查看源码" = "Quellcode anzeigen";
"检查失败,请稍后重试" = "Prüfung fehlgeschlagen. Bitte versuchen Sie es später noch einmal.";
"检查更新" = "Suchen Sie nach Updates";
"正在关闭隐私屏…" = "Sichtschutz wird geschlossen…";
"正在准备连接…" = "Verbindung wird vorbereitet...";
"正在发送 %1$@" = "Senden von %1$@";
"正在发送 Ctrl+Alt+Del…" = "Senden von Ctrl+Alt+Del…";
"正在取回远程画面…" = "Remote-Bildschirm abrufen...";
"正在建立远程连接" = "Remote-Verbindung aufbauen";
"正在开启隐私屏…" = "Privatsphärenschutz einschalten...";
"正在收集 ICE 候选" = "Sammeln von ICE-Kandidaten";
"正在检查更新…" = "Suche nach Updates…";
"正在清除…" = "Löschen...";
"正在确认设备状态…" = "Gerätestatus bestätigen...";
"正在获取本机 ID…" = "Lokale ID erhalten...";
"正在连接" = "Verbinden";
"正在连接信令服务" = "Verbindung zum Signalisierungsdienst herstellen";
"清除" = "Klar";
"清除预览图" = "Vorschaubilder löschen";
"清除预览图?" = "Vorschaubilder löschen?";
"源码与构建说明" = "Quellcode und Build-Anweisungen";
"源码版本" = "Quellcode-Version";
"版本" = "Version";
"版权与许可声明" = "Urheberrechts- und Lizenzerklärung";
"电脑键盘" = "Computertastatur";
"画中画未能开启,请保持应用在前台后重试。" = "Bild-in-Bild konnte nicht geöffnet werden. Bitte lassen Sie die Anwendung im Vordergrund und versuchen Sie es erneut.";
"画质优先" = "Qualität";
"画面偏好" = "Videopräferenz";
"画面延时" = "Videolatenz";
"画面设置" = "Videoeinstellungen";
"画面质量" = "Videoqualität";
"画面采集帧率" = "Bildrate der Videoaufnahme";
"相对位置" = "Touchpad";
"相对鼠标" = "Relative Maus";
"知道了" = "Verstanden";
"确定" = "Okay";
"确认" = "Bestätigen";
"离线" = "Offline";
"空格" = "Leertaste";
"端口" = "Hafen";
"符号功能" = "Symbole & Tasten";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Warten Sie, bis der Computer in den Datenschutzstatus zurückkehrt. Ältere Computer unterstützen dies möglicherweise nicht.";
"等待连接" = "Warten auf Verbindung";
"等待隐私授权" = "Datenschutzzustimmung ausstehend";
"系统键盘" = "Systemtastatur";
"绝对位置" = "Direkte Berührung";
"绝对鼠标" = "Absolute Maus";
"网络状态" = "Netzwerk";
"网络连接已中断,请重新连接。" = "Die Netzwerkverbindung wurde unterbrochen. Bitte stellen Sie die Verbindung wieder her.";
"自定义" = "Benutzerdefiniert";
"虚拟鼠标" = "Virtuelle Maus";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "Das gesteuerte Terminal unterstützt keine direkte Eingabe von Chinesisch. Bitte aktualisieren Sie das von macOS gesteuerte Terminal. Sie können den Text auch zunächst kopieren, über die Zwischenablage des Fernbedienungsmenüs senden und dann einfügen";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "Der Remote-Datenschutzbildschirm wurde über die lokale Verknüpfung entsperrt. Sperren Sie es erneut, um die Privatsphäre wiederherzustellen und das Entsperren lokaler Verknüpfungen zu deaktivieren.";
"被控端平台:%1$@" = "Kontrollierte Terminalplattform:%1$@";
"被控端版本过低,请升级到最新版本。" = "Die kontrollierte Version ist zu niedrig, bitte aktualisieren Sie auf die neueste Version.";
"被控端编码器" = "Remote-Encoder";
"视频" = "Video";
"视频 %1$@×%2$@ · %3$@ 帧" = "Video %1$@×%2$@·%3$@-Frames";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "Die Berührungsposition entspricht direkt der Remote-Bildschirmposition, die für eine schnelle Positionierung geeignet ist.";
"许可" = "Lizenz";
"许可与版权声明" = "Lizenzen und Urheberrechtshinweise";
"许可与版权声明可离线查阅。" = "Die Lizenz- und Urheberrechtshinweise sind offline verfügbar.";
"设备离线" = "Gerät offline";
"设置" = "Einstellungen";
"设置,有新版本" = "Einstellungen, es gibt eine neue Version";
"访问密码" = "Zugangspasswort";
"访问密码错误,请重新输入。" = "Das Zugangspasswort ist falsch, bitte geben Sie es erneut ein.";
"请求超时,尚未确认电脑的隐私屏状态" = "Die Anfrage ist abgelaufen und der Datenschutzstatus des Computers wurde nicht bestätigt.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Bitte geben Sie einen Port im Bereich 1–65535 ein oder deaktivieren Sie „Benutzerdefinierten Server verwenden“.";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Bitte geben Sie die Serveradresse ein oder deaktivieren Sie „Benutzerdefinierten Server verwenden“.";
"请输入远程设备 ID" = "Bitte geben Sie die Remote-Geräte-ID ein";
"调整失败,请重试。" = "Einstellungen konnten nicht angewendet werden. Versuchen Sie es erneut.";
"软件许可" = "Softwarelizenz";
"返回控制栏" = "Zurück zur Steuerleiste";
"还没有连接记录" = "Noch kein Verbindungsdatensatz";
"这条公告已更新或删除,请返回公告列表查看。" = "Diese Ankündigung wurde aktualisiert oder gelöscht. Bitte kehren Sie zur Ankündigungsliste zurück, um sie anzuzeigen.";
"远程桌面" = "Remotedesktop";
"远程设备 ID 不存在,请检查设备 ID。" = "Remote-Geräte-ID existiert nicht, bitte überprüfen Sie die Geräte-ID.";
"远程设备当前不可用,请稍后重试。" = "Das Remote-Gerät ist derzeit nicht verfügbar. Bitte versuchen Sie es später erneut.";
"远程连接已结束。" = "Remote-Verbindung beendet.";
"连接" = "Verbinden";
"连接延时(RTT)" = "Verbindungsverzögerung (RTT)";
"连接建立后可调整,需被控端支持。" = "Verfügbar bei Verbindung mit einem unterstützten Remote-Gerät.";
"连接建立后锁定横屏" = "Querformatbildschirm sperren, nachdem die Verbindung hergestellt wurde";
"连接提示" = "Verbindungsaufforderung";
"连接方式" = "Verbindungsmethode";
"连接远程桌面" = "Mit Remote-Desktop verbinden";
"退格" = "Rücktaste";
"通知公告" = "Mitteilungen";
"部分预览图未能清除,请重试。" = "Einige Vorschauen konnten nicht gelöscht werden. Bitte versuchen Sie es erneut.";
"重试" = "Versuchen Sie es erneut";
"重试保存阅读状态" = "Versuchen Sie erneut, den Lesestatus zu speichern";
"锁定" = "Sperren";
"键盘" = "Tastatur";
"键盘操作" = "Tastaturbedienung";
"阅读并授权" = "Lesen und autorisieren";
"隐私" = "Datenschutz";
"隐私与授权" = "Datenschutz und Autorisierung";
"隐私屏" = "Sichtschutz";
"隐私屏已关闭" = "Sichtschutz aus";
"隐私屏已开启" = "Sichtschutz an";
"隐私屏已被解锁" = "Datenschutzbildschirm entsperrt";
"隐私屏异常" = "Anomalie des Sichtschutzbildschirms";
"隐私屏暂不可用" = "Der Datenschutzbildschirm ist vorübergehend nicht verfügbar";
"隐私屏状态已过期,等待电脑确认" = "Der Status des Datenschutzbildschirms ist abgelaufen und wartet auf Computerbestätigung";
"隐私屏状态待确认" = "Status des Datenschutzbildschirms muss bestätigt werden";
"隐私政策" = "Datenschutzrichtlinie";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "Die Datenschutzerklärung kann vorübergehend nicht gelesen werden. Bitte verweigern Sie die Verbindung zum Internet und installieren Sie die Anwendung neu.";
"隐藏密码" = "Passwort verbergen";
"静音" = "Stumm";
"音频" = "Audio";
"项目主页" = "Projektwebsite";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "Vorschauen können persönliche oder geschäftliche Informationen vom Remote-Computer enthalten. Nach dem Einschalten speichert jede Verbindung automatisch ein Bild auf dem Gerät und zeigt es in den letzten Verbindungen an. Sie können jederzeit schließen und löschen.";
"高" = "Hoch";
"默认" = "Standard";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Standardmäßig deaktiviert. Beim Einschalten wechselt das iPhone in die Querformatausrichtung und bleibt beim Anschließen im Querformat. Im ausgeschalteten Zustand folgt es der Ausrichtung des Geräts. iPads sind nicht betroffen.";
"鼠标右键" = "Rechte Maustaste";
"鼠标左键" = "Linke Maustaste";
"鼠标控制" = "Maussteuerung";
"鼠标滚轮" = "Mausrad";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk uses the local network to discover and establish native P2P remote control connections.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "%1$@ transmission failed";
"%1$@ 台设备" = "Devices: %1$@";
"%1$@ 条未读" = "%1$@ unread items";
"%1$@键盘页" = "%1$@ keyboard page";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, receiving %2$@, sending %3$@, packet loss rate %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk uses the local network to discover and establish native P2P remote control connections.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del requires remote activation of Windows system services";
"GNU GPL 第 3 版" = "GNU GPL version 3";
"OpenFEC 告知" = "OpenFEC notice";
"OpenFEC 许可与源码" = "OpenFEC license and source";
"P2P 直连" = "P2P direct connection";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Link establishment failed, please check the network and try again.";
"SRTP 已启用" = "SRTP enabled";
"TLS 证书校验失败" = "TLS certificate verification failed";
"TURN 中继" = "TURN relay";
"上下滑动,可滑出按键范围" = "Slide up and down to slide out of the key range";
"丢包率" = "Loss Rate";
"中" = "Medium";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Prioritizes keeping the picture smooth, and will reduce resolution more aggressively when bandwidth or encoding pressure is high.";
"低" = "Low";
"使用自定义服务器" = "Using a custom server";
"保存密码" = "Save password";
"保存远程画面预览" = "Save remote screen previews";
"保存远程画面预览?" = "Save remote screen preview?";
"信令服务器已关闭连接" = "The signaling server has closed the connection";
"信令服务重连中" = "Signaling service reconnecting";
"信令连接失败" = "Signaling connection failed";
"信令连接已关闭" = "Signaling connection closed";
"偏好模式" = "Preference mode";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Slide the cursor like a trackpad and operate the current cursor position when clicked.";
"公告加载失败,请重试。" = "Announcement loading failed, please try again.";
"公告详情" = "Announcement details";
"共 %1$@ 条 · %2$@ 条未读" = "Total %1$@ items ·%2$@ unread items";
"关于" = "About";
"关于 CrossDesk" = "About CrossDesk";
"关于,有新版本" = "About, there is a new version";
"关闭" = "Close";
"关闭控制栏" = "Close control bar";
"关闭隐私屏" = "Disable privacy screen";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Balances clarity and smoothness, only gradually reducing resolution under constant pressure.";
"分辨率" = "Resolution";
"切换到电脑键盘" = "Switch to computer keyboard";
"切换到系统键盘" = "Switch to system keyboard";
"删除" = "Delete";
"删除公告" = "Delete announcement";
"删除记录" = "Delete record";
"删除这条公告?" = "Delete this announcement?";
"刷新公告" = "Refresh announcement";
"剪贴板" = "Clipboard";
"剪贴板中没有文本" = "No text in clipboard";
"剪贴板文本超过 128 KiB" = "Clipboard text exceeds 128 KiB";
"加载中…" = "Loading…";
"加载更多" = "Load more";
"升级提示" = "Update recommended";
"单次输入最多 16 KiB,且不能包含空字符" = "A single input can be up to 16 KiB and cannot contain null characters";
"发送" = "Send";
"发送文件" = "Send file";
"取消" = "Cancel";
"取消连接" = "Cancel connection";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Each component is subject to the listed open source license, and the rights granted are not subject to the general terms of use of this application. Viewing source code requires an internet connection.";
"合计" = "Total";
"同意并继续" = "Agree and continue";
"回车" = "Enter";
"在线" = "Online";
"地址" = "Address";
"填写服务器地址" = "Fill in the server address";
"填写端口" = "Fill in the port";
"声音" = "Sound";
"失败" = "Failed";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Always maintain a higher resolution and reduce the frame rate to ensure picture clarity when bandwidth is insufficient.";
"媒体加密" = "Media encryption";
"字母数字" = "Letters & numbers";
"完成" = "Done";
"完成隐私授权后可检查更新" = "You can check for updates after completing privacy authorization";
"完成隐私授权后登记本机身份" = "Register the local identity after completing privacy authorization";
"密码将安全保存在本机 Keychain 中" = "Passwords will be safely stored in the local Keychain";
"对端 ID" = "Remote ID";
"对端 ID %1$@" = "Peer ID %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Will stop the remote connection, close the screen preview save and clear the existing preview. Connection records and received files will be retained, and existing server data will not be deleted. You can reauthorize at any time.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "All remote previews saved on this machine will be deleted, while connection records, passwords and received files will be retained.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "All remote previews saved on this machine will be deleted, while connection records, passwords and received files will be retained. Subsequent connections will still save new previews.";
"屏幕方向" = "Screen orientation";
"展开远程控制菜单" = "Expand remote control menu";
"已保存密码" = "Password saved";
"已勾选" = "Checked";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del sent";
"已发送本机剪贴板" = "Local clipboard sent";
"已授权" = "Granted";
"已接收 %1$@" = "Received %1$@";
"已接收远端剪贴板" = "Remote clipboard received";
"已读" = "Read";
"已连接" = "Connected";
"已连接服务器" = "Connected to server";
"帧率" = "Frame rate";
"帧率优先" = "Smooth";
"平衡" = "Balanced";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "The license file in the application is missing or damaged, please reinstall the application.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "The development version contains modifications that have not yet been released. The following link is the source code of the basic version.";
"开启保存" = "Enable saving";
"开启隐私屏" = "Enable privacy screen";
"开源组件" = "Open-source components";
"开源软件权利说明" = "Open source software rights description";
"当前已是最新版本" = "This is currently the latest version";
"成功连接后,这里会显示远端桌面的缩略图。" = "After a successful connection, a thumbnail of the remote desktop will be displayed here.";
"我已阅读并同意《隐私政策》" = "I have read and agreed to the Privacy Policy";
"我已阅读并同意隐私政策" = "I have read and agree to the privacy policy";
"打开隐私政策" = "Open privacy policy";
"指令发送失败,请重试" = "Command sending failed, please try again";
"指令已发送,等待电脑确认…" = "The command has been sent, waiting for computer confirmation...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Press and drag to slide out of the key range, release to end dragging";
"授权后可使用远程连接。" = "Remote connections are available after authorization.";
"授权状态" = "Authorization status";
"接收" = "Receive";
"控制模式" = "Control mode";
"控制端解码器" = "Local decoder";
"撤回授权" = "Withdraw authorization";
"撤回隐私授权" = "Withdraw privacy authorization";
"收起电脑键盘" = "Hide computer keyboard";
"收起系统键盘" = "Collapse system keyboard";
"收起远程控制菜单" = "Collapse remote control menu";
"收起鼠标" = "Hide mouse";
"数据" = "Data";
"断开后将返回首页" = "Will return to the homepage after disconnecting";
"断开远程连接?" = "Disconnect remote connection?";
"断开连接" = "Disconnect";
"新版本" = "New version";
"新版本可用:v%1$@" = "New version available: v%1$@";
"无效" = "Invalid";
"无未读公告" = "No unread announcements";
"无法" = "Unable to";
"无法保存本地阅读状态,请重试。" = "Unable to save reading status locally. Please try again.";
"无法删除这条公告,请重试。" = "Unable to delete this announcement. Please try again.";
"无法访问本机登录凭据,请解锁后重试" = "Unable to access local login credentials, please unlock and try again";
"无法读取开源许可" = "Unable to read open source license";
"无法读取所选文件,请重试" = "Unable to read selected file, please try again";
"是否撤回隐私授权?" = "Withdraw privacy authorization?";
"显示器" = "Display";
"显示器 %1$@" = "Monitor %1$@";
"显示器 1" = "Monitor 1";
"显示密码" = "Show password";
"暂不同意" = "Not now";
"暂无公告" = "No announcements";
"最近连接" = "Recent connections";
"服务器" = "Server";
"服务器地址" = "Server Address";
"服务器端口" = "Server port";
"服务器配置无效" = "Invalid server configuration";
"未勾选" = "Not checked";
"未启用" = "Not enabled";
"未授权" = "Unauthorized";
"未知" = "Unknown";
"未知信令状态" = "Unknown signaling status";
"未知连接状态" = "Unknown connection status";
"未读" = "Unread";
"未连接" = "Not connected";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "This application uses OpenFEC for forward error correction under CeCILL-C 1.0, which provides limited warranties and limits liability. See the full license for details.";
"本机 ID %1$@" = "Local ID %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "The login credentials for this machine are temporarily insecure. Please unlock your device and click \"Apply\" in settings to try again.";
"本版本源码与构建说明" = "Source code and build instructions for this version";
"本版本源码信息暂不可用。" = "Source revision is unavailable for this build.";
"构建说明" = "Build instructions";
"构建配方与项目补丁" = "Build recipes and project patches";
"查看源码" = "View source code";
"检查失败,请稍后重试" = "Check failed. Please try again later.";
"检查更新" = "Check for updates";
"正在关闭隐私屏…" = "Closing privacy screen…";
"正在准备连接…" = "Preparing to connect...";
"正在发送 %1$@" = "Sending %1$@";
"正在发送 Ctrl+Alt+Del…" = "Sending Ctrl+Alt+Del…";
"正在取回远程画面…" = "Retrieving remote screen...";
"正在建立远程连接" = "Establishing remote connection";
"正在开启隐私屏…" = "Turning on privacy screen...";
"正在收集 ICE 候选" = "Collecting ICE candidates";
"正在检查更新…" = "Checking for updates…";
"正在清除…" = "Clearing...";
"正在确认设备状态…" = "Confirming device status...";
"正在获取本机 ID…" = "Obtaining local ID...";
"正在连接" = "Connecting";
"正在连接信令服务" = "Connecting to signaling service";
"清除" = "Clear";
"清除预览图" = "Clear previews";
"清除预览图?" = "Clear previews?";
"源码与构建说明" = "Source code and build instructions";
"源码版本" = "Source code version";
"版本" = "Version";
"版权与许可声明" = "Copyright and License Statement";
"电脑键盘" = "Computer keyboard";
"画中画未能开启,请保持应用在前台后重试。" = "Picture-in-picture failed to open, please keep the application in the foreground and try again.";
"画质优先" = "Quality";
"画面偏好" = "Video Preference";
"画面延时" = "Video latency";
"画面设置" = "Video settings";
"画面质量" = "Video Quality";
"画面采集帧率" = "Video Capture Frame Rate";
"相对位置" = "Trackpad";
"相对鼠标" = "Relative mouse";
"知道了" = "Got it";
"确定" = "OK";
"确认" = "Confirm";
"离线" = "Offline";
"空格" = "Space";
"端口" = "Port";
"符号功能" = "Symbols & keys";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Wait for the computer to return to the privacy screen state; older computers may not support it.";
"等待连接" = "Waiting for connection";
"等待隐私授权" = "Waiting for privacy authorization";
"系统键盘" = "System keyboard";
"绝对位置" = "Direct touch";
"绝对鼠标" = "Absolute Mouse";
"网络状态" = "Network";
"网络连接已中断,请重新连接。" = "The network connection has been interrupted, please reconnect.";
"自定义" = "Custom";
"虚拟鼠标" = "Virtual mouse";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "The controlled terminal does not support direct input of Chinese, please update the macOS controlled terminal; you can also copy the text first, send it through the clipboard of the remote control menu and then paste it";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "The remote privacy screen was unlocked using the local shortcut. Lock it again to restore privacy and disable local shortcut unlocking.";
"被控端平台:%1$@" = "Controlled terminal platform:%1$@";
"被控端版本过低,请升级到最新版本。" = "The controlled version is too low, please upgrade to the latest version.";
"被控端编码器" = "Remote encoder";
"视频" = "Video";
"视频 %1$@×%2$@ · %3$@ 帧" = "Video %1$@×%2$@·%3$@ frames";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "The touch position directly corresponds to the remote screen position, which is suitable for quick positioning.";
"许可" = "License";
"许可与版权声明" = "Licenses and copyright notices";
"许可与版权声明可离线查阅。" = "The license and copyright notice are available offline.";
"设备离线" = "Device Offline";
"设置" = "Settings";
"设置,有新版本" = "Settings, there is a new version";
"访问密码" = "Access password";
"访问密码错误,请重新输入。" = "The access password is wrong, please re-enter it.";
"请求超时,尚未确认电脑的隐私屏状态" = "The request timed out and the computer's privacy screen status has not been confirmed.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Please enter a port in the range 1–65535, or turn off \"Use custom server\".";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Please enter the server address, or turn off \"Use a custom server\".";
"请输入远程设备 ID" = "Please enter the remote device ID";
"调整失败,请重试。" = "Could not apply settings. Try again.";
"软件许可" = "Software license";
"返回控制栏" = "Return to control bar";
"还没有连接记录" = "No connection record yet";
"这条公告已更新或删除,请返回公告列表查看。" = "This announcement has been updated or deleted, please return to the announcement list to view it.";
"远程桌面" = "Remote Desktop";
"远程设备 ID 不存在,请检查设备 ID。" = "Remote device ID does not exist, please check device ID.";
"远程设备当前不可用,请稍后重试。" = "The remote device is currently unavailable, please try again later.";
"远程连接已结束。" = "Remote connection ended.";
"连接" = "Connect";
"连接延时(RTT)" = "Connection delay (RTT)";
"连接建立后可调整,需被控端支持。" = "Available when connected to a supported remote device.";
"连接建立后锁定横屏" = "Lock landscape screen after connection is established";
"连接提示" = "Connection prompt";
"连接方式" = "Connection method";
"连接远程桌面" = "Connect to remote desktop";
"退格" = "Backspace";
"通知公告" = "Announcements";
"部分预览图未能清除,请重试。" = "Some previews could not be cleared, please try again.";
"重试" = "Try again";
"重试保存阅读状态" = "Retry saving reading status";
"锁定" = "Lock";
"键盘" = "Keyboard";
"键盘操作" = "Keyboard operation";
"阅读并授权" = "Read and authorize";
"隐私" = "Privacy";
"隐私与授权" = "Privacy and Authorization";
"隐私屏" = "Privacy screen";
"隐私屏已关闭" = "Privacy screen off";
"隐私屏已开启" = "Privacy screen on";
"隐私屏已被解锁" = "Privacy screen unlocked";
"隐私屏异常" = "Privacy screen abnormality";
"隐私屏暂不可用" = "Privacy screen is temporarily unavailable";
"隐私屏状态已过期,等待电脑确认" = "Privacy screen status has expired, waiting for computer confirmation";
"隐私屏状态待确认" = "Privacy screen status to be confirmed";
"隐私政策" = "Privacy Policy";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "The privacy policy cannot be read temporarily. Please refuse to connect to the Internet and reinstall the application.";
"隐藏密码" = "Hide password";
"静音" = "Mute";
"音频" = "Audio";
"项目主页" = "Project website";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "Previews may contain personal or work information from the remote computer. After turning it on, each connection will automatically save a picture on the machine and display it in recent connections. You can close and clear at any time.";
"高" = "High";
"默认" = "Default";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Off by default. When turned on, the iPhone goes into and stays in landscape orientation when connected; when turned off, it follows the device orientation. iPads are not affected.";
"鼠标右键" = "Right mouse button";
"鼠标左键" = "Left mouse button";
"鼠标控制" = "Mouse control";
"鼠标滚轮" = "Mouse wheel";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk utiliza la red local para descubrir y establecer conexiones nativas de control remoto P2P.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "Error en la transmisión %1$@";
"%1$@ 台设备" = "Dispositivos: %1$@";
"%1$@ 条未读" = "%1$@ elementos no leídos";
"%1$@键盘页" = "Página del teclado %1$@";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, recepción de %2$@, envío de %3$@, tasa de pérdida de paquetes %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk utiliza la red local para descubrir y establecer conexiones nativas de control remoto P2P.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del requiere activación remota de los servicios del sistema Windows";
"GNU GPL 第 3 版" = "GNU GPL versión 3";
"OpenFEC 告知" = "Aviso OpenFEC";
"OpenFEC 许可与源码" = "Licencia y fuente OpenFEC";
"P2P 直连" = "Conexión directa P2P";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Error al establecer el enlace, verifique la red e inténtelo nuevamente.";
"SRTP 已启用" = "SRTP habilitado";
"TLS 证书校验失败" = "Error en la verificación del certificado TLS";
"TURN 中继" = "Relé de GIRO";
"上下滑动,可滑出按键范围" = "Deslice hacia arriba y hacia abajo para salir del rango de teclas";
"丢包率" = "Tasa de pérdidas";
"中" = "Media";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Da prioridad a mantener la imagen fluida y reducirá la resolución de manera más agresiva cuando el ancho de banda o la presión de codificación sean altos.";
"低" = "Baja";
"使用自定义服务器" = "Usando un servidor personalizado";
"保存密码" = "Guardar contraseña";
"保存远程画面预览" = "Guardar vistas previas de pantalla remotas";
"保存远程画面预览?" = "¿Guardar vista previa de pantalla remota?";
"信令服务器已关闭连接" = "El servidor de señalización ha cerrado la conexión.";
"信令服务重连中" = "Reconexión del servicio de señalización";
"信令连接失败" = "Error en la conexión de señalización";
"信令连接已关闭" = "Conexión de señalización cerrada";
"偏好模式" = "Modo de preferencia";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Deslice el cursor como un trackpad y opere la posición actual del cursor cuando se haga clic.";
"公告加载失败,请重试。" = "Error al cargar el anuncio, inténtelo de nuevo.";
"公告详情" = "Detalles del anuncio";
"共 %1$@ 条 · %2$@ 条未读" = "Total de elementos %1$@· Elementos no leídos %2$@";
"关于" = "Acerca de";
"关于 CrossDesk" = "Acerca de CrossDesk";
"关于,有新版本" = "Acerca de, hay una nueva versión.";
"关闭" = "Cerrar";
"关闭控制栏" = "Cerrar barra de control";
"关闭隐私屏" = "Desactivar la pantalla de privacidad";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Equilibra la claridad y la suavidad, reduciendo solo gradualmente la resolución bajo presión constante.";
"分辨率" = "Resolución";
"切换到电脑键盘" = "Cambiar al teclado de la computadora";
"切换到系统键盘" = "Cambiar al teclado del sistema";
"删除" = "Eliminar";
"删除公告" = "Eliminar anuncio";
"删除记录" = "Eliminar registro";
"删除这条公告?" = "¿Eliminar este anuncio?";
"刷新公告" = "Actualizar anuncio";
"剪贴板" = "Portapapeles";
"剪贴板中没有文本" = "No hay texto en el portapapeles";
"剪贴板文本超过 128 KiB" = "El texto del portapapeles supera los 128 KiB";
"加载中…" = "Cargando…";
"加载更多" = "Cargar más";
"升级提示" = "Actualización recomendada";
"单次输入最多 16 KiB,且不能包含空字符" = "Una sola entrada puede tener hasta 16 KiB y no puede contener caracteres nulos";
"发送" = "Enviar";
"发送文件" = "Enviar archivo";
"取消" = "Cancelar";
"取消连接" = "Cancelar conexión";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Cada componente está sujeto a la licencia de código abierto enumerada y los derechos otorgados no están sujetos a los términos generales de uso de esta aplicación. Para ver el código fuente se requiere una conexión a Internet.";
"合计" = "Total";
"同意并继续" = "Aceptar y continuar";
"回车" = "Entrar";
"在线" = "En línea";
"地址" = "Dirección";
"填写服务器地址" = "Complete la dirección del servidor";
"填写端口" = "Complete el puerto";
"声音" = "Sonido";
"失败" = "Fallido";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Mantenga siempre una resolución más alta y reduzca la velocidad de fotogramas para garantizar la claridad de la imagen cuando el ancho de banda sea insuficiente.";
"媒体加密" = "Cifrado de medios";
"字母数字" = "Letras y números";
"完成" = "Hecho";
"完成隐私授权后可检查更新" = "Puede buscar actualizaciones después de completar la autorización de privacidad";
"完成隐私授权后登记本机身份" = "Registre la identidad local después de completar la autorización de privacidad";
"密码将安全保存在本机 Keychain 中" = "Las contraseñas se almacenarán de forma segura en el llavero local.";
"对端 ID" = "Identificación remota";
"对端 ID %1$@" = "ID de igual %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Detendrá la conexión remota, cerrará la vista previa de la pantalla y borrará la vista previa existente. Los registros de conexión y los archivos recibidos se conservarán y los datos existentes del servidor no se eliminarán. Puede volver a autorizar en cualquier momento.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Se eliminarán todas las vistas previas remotas guardadas en esta máquina, mientras que se conservarán los registros de conexión, las contraseñas y los archivos recibidos.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Se eliminarán todas las vistas previas remotas guardadas en esta máquina, mientras que se conservarán los registros de conexión, las contraseñas y los archivos recibidos. Las conexiones posteriores seguirán guardando nuevas vistas previas.";
"屏幕方向" = "Orientación de la pantalla";
"展开远程控制菜单" = "Ampliar el menú del control remoto";
"已保存密码" = "Contraseña guardada";
"已勾选" = "Comprobado";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del enviado";
"已发送本机剪贴板" = "Portapapeles local enviado";
"已授权" = "Concedido";
"已接收 %1$@" = "Recibido %1$@";
"已接收远端剪贴板" = "Portapapeles remoto recibido";
"已读" = "Leer";
"已连接" = "Conectado";
"已连接服务器" = "Conectado al servidor";
"帧率" = "Velocidad de fotogramas";
"帧率优先" = "Fluidez";
"平衡" = "Equilibrado";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "El archivo de licencia de la aplicación falta o está dañado; reinstale la aplicación.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "La versión de desarrollo contiene modificaciones que aún no han sido publicadas. El siguiente enlace es el código fuente de la versión básica.";
"开启保存" = "Activar guardado";
"开启隐私屏" = "Habilitar pantalla de privacidad";
"开源组件" = "Componentes de código abierto";
"开源软件权利说明" = "Descripción de los derechos del software de código abierto";
"当前已是最新版本" = "Esta es actualmente la última versión.";
"成功连接后,这里会显示远端桌面的缩略图。" = "Después de una conexión exitosa, aquí se mostrará una miniatura del escritorio remoto.";
"我已阅读并同意《隐私政策》" = "He leído y acepto la Política de Privacidad";
"我已阅读并同意隐私政策" = "He leído y acepto la política de privacidad";
"打开隐私政策" = "Abrir política de privacidad";
"指令发送失败,请重试" = "Error al enviar el comando, inténtelo de nuevo";
"指令已发送,等待电脑确认…" = "El comando ha sido enviado, esperando confirmación por computadora...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Presione y arrastre para deslizarse fuera del rango de teclas, suelte para finalizar el arrastre";
"授权后可使用远程连接。" = "Las conexiones remotas están disponibles después de la autorización.";
"授权状态" = "Estado de autorización";
"接收" = "Recibir";
"控制模式" = "Modo de control";
"控制端解码器" = "Decodificador local";
"撤回授权" = "Retirar autorización";
"撤回隐私授权" = "Retirar la autorización de privacidad";
"收起电脑键盘" = "Ocultar teclado del PC";
"收起系统键盘" = "Contraer teclado del sistema";
"收起远程控制菜单" = "Contraer menú de control remoto";
"收起鼠标" = "Ocultar ratón";
"数据" = "Datos";
"断开后将返回首页" = "Volverá a la página de inicio después de desconectarse";
"断开远程连接?" = "¿Desconectar la conexión remota?";
"断开连接" = "Desconectar";
"新版本" = "Nueva versión";
"新版本可用:v%1$@" = "Nueva versión disponible: v%1$@";
"无效" = "Inválido";
"无未读公告" = "No hay anuncios no leídos";
"无法" = "No puedo";
"无法保存本地阅读状态,请重试。" = "No se puede guardar el estado de lectura localmente. Por favor inténtalo de nuevo.";
"无法删除这条公告,请重试。" = "No se puede eliminar este anuncio. Por favor inténtalo de nuevo.";
"无法访问本机登录凭据,请解锁后重试" = "No se puede acceder a las credenciales de inicio de sesión locales, desbloquee e intente nuevamente";
"无法读取开源许可" = "No se puede leer la licencia de código abierto";
"无法读取所选文件,请重试" = "No se puede leer el archivo seleccionado, inténtelo de nuevo";
"是否撤回隐私授权?" = "¿Retirar la autorización de privacidad?";
"显示器" = "Pantalla";
"显示器 %1$@" = "Monitorizar %1$@";
"显示器 1" = "Monitor 1";
"显示密码" = "Mostrar contraseña";
"暂不同意" = "Ahora no";
"暂无公告" = "Sin anuncios";
"最近连接" = "Conexiones recientes";
"服务器" = "Servidor";
"服务器地址" = "Dirección del servidor";
"服务器端口" = "Puerto del servidor";
"服务器配置无效" = "Configuración del servidor no válida";
"未勾选" = "No marcado";
"未启用" = "No habilitado";
"未授权" = "No autorizado";
"未知" = "Desconocido";
"未知信令状态" = "Estado de señalización desconocido";
"未知连接状态" = "Estado de conexión desconocido";
"未读" = "No leído";
"未连接" = "No conectado";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Esta aplicación utiliza OpenFEC para la corrección de errores directos bajoCeCILL-C1.0, que proporciona garantías limitadas y limita la responsabilidad. Consulte la licencia completa para obtener más detalles.";
"本机 ID %1$@" = "ID local %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "Las credenciales de inicio de sesión para esta máquina son temporalmente inseguras. Desbloquee su dispositivo y haga clic en \"Aplicar\" en la configuración para volver a intentarlo.";
"本版本源码与构建说明" = "Código fuente e instrucciones de compilación para esta versión";
"本版本源码信息暂不可用。" = "La revisión fuente no está disponible para esta compilación.";
"构建说明" = "Instrucciones de construcción";
"构建配方与项目补丁" = "Crear recetas y parches de proyectos";
"查看源码" = "Ver código fuente";
"检查失败,请稍后重试" = "La verificación falló. Inténtelo de nuevo más tarde.";
"检查更新" = "Buscar actualizaciones";
"正在关闭隐私屏…" = "Cerrando la pantalla de privacidad...";
"正在准备连接…" = "Preparándose para conectarse...";
"正在发送 %1$@" = "Envío de %1$@";
"正在发送 Ctrl+Alt+Del…" = "Enviando Ctrl+Alt+Del…";
"正在取回远程画面…" = "Recuperando la pantalla remota...";
"正在建立远程连接" = "Establecer conexión remota";
"正在开启隐私屏…" = "Activando la pantalla de privacidad...";
"正在收集 ICE 候选" = "Recopilación de candidatos de ICE";
"正在检查更新…" = "Buscando actualizaciones...";
"正在清除…" = "Borrando...";
"正在确认设备状态…" = "Confirmando el estado del dispositivo...";
"正在获取本机 ID…" = "Obtención de identificación local...";
"正在连接" = "Conexión";
"正在连接信令服务" = "Conexión al servicio de señalización";
"清除" = "Borrar";
"清除预览图" = "Borrar vistas previas";
"清除预览图?" = "¿Borrar vistas previas?";
"源码与构建说明" = "Código fuente e instrucciones de compilación";
"源码版本" = "Versión del código fuente";
"版本" = "Versión";
"版权与许可声明" = "Declaración de derechos de autor y licencia";
"电脑键盘" = "Teclado de computadora";
"画中画未能开启,请保持应用在前台后重试。" = "No se pudo abrir la imagen en imagen. Mantenga la aplicación en primer plano e inténtelo nuevamente.";
"画质优先" = "Calidad";
"画面偏好" = "Preferencia de vídeo";
"画面延时" = "Latencia de vídeo";
"画面设置" = "Configuración de vídeo";
"画面质量" = "Calidad de vídeo";
"画面采集帧率" = "Velocidad de fotogramas de captura de vídeo";
"相对位置" = "Panel táctil";
"相对鼠标" = "Ratón relativo";
"知道了" = "Entendido";
"确定" = "Aceptar";
"确认" = "Confirmar";
"离线" = "Sin conexión";
"空格" = "Espacio";
"端口" = "Puerto";
"符号功能" = "Símbolos y teclas";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Espere a que la computadora vuelva al estado de pantalla de privacidad; Es posible que las computadoras más antiguas no lo admitan.";
"等待连接" = "Esperando conexión";
"等待隐私授权" = "Esperando autorización de privacidad";
"系统键盘" = "Teclado del sistema";
"绝对位置" = "Toque directo";
"绝对鼠标" = "Ratón absoluto";
"网络状态" = "Red";
"网络连接已中断,请重新连接。" = "La conexión de red ha sido interrumpida, vuelva a conectarse.";
"自定义" = "Personalizado";
"虚拟鼠标" = "Ratón virtual";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "El terminal controlado no admite la entrada directa de chino; actualice el terminal controlado por macOS; También puedes copiar el texto primero, enviarlo a través del portapapeles del menú del control remoto y luego pegarlo.";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "La pantalla de privacidad remota se desbloqueó usando el acceso directo local. Bloquéelo nuevamente para restaurar la privacidad y deshabilitar el desbloqueo de accesos directos locales.";
"被控端平台:%1$@" = "Plataforma terminal controlada:%1$@";
"被控端版本过低,请升级到最新版本。" = "La versión controlada es demasiado baja; actualice a la última versión.";
"被控端编码器" = "Codificador remoto";
"视频" = "Vídeo";
"视频 %1$@×%2$@ · %3$@ 帧" = "Vídeo %1$@×%2$@· Cuadros %3$@";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "La posición táctil corresponde directamente a la posición de la pantalla remota, que es adecuada para un posicionamiento rápido.";
"许可" = "Licencia";
"许可与版权声明" = "Licencias y avisos de derechos de autor";
"许可与版权声明可离线查阅。" = "La licencia y el aviso de derechos de autor están disponibles sin conexión.";
"设备离线" = "Dispositivo sin conexión";
"设置" = "Ajustes";
"设置,有新版本" = "Configuración, hay una nueva versión.";
"访问密码" = "Contraseña de acceso";
"访问密码错误,请重新输入。" = "La contraseña de acceso es incorrecta, por favor vuelva a ingresarla.";
"请求超时,尚未确认电脑的隐私屏状态" = "Se agotó el tiempo de espera de la solicitud y no se ha confirmado el estado de la pantalla de privacidad de la computadora.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Ingrese un puerto en el rango 1–65535 o desactive \"Usar servidor personalizado\".";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Ingrese la dirección del servidor o desactive \"Usar un servidor personalizado\".";
"请输入远程设备 ID" = "Ingrese el ID del dispositivo remoto";
"调整失败,请重试。" = "No se pudieron aplicar las configuraciones. Intentar otra vez.";
"软件许可" = "Licencia de software";
"返回控制栏" = "Volver a la barra de control";
"还没有连接记录" = "Aún no hay registro de conexión";
"这条公告已更新或删除,请返回公告列表查看。" = "Este anuncio ha sido actualizado o eliminado, regrese a la lista de anuncios para verlo.";
"远程桌面" = "Escritorio remoto";
"远程设备 ID 不存在,请检查设备 ID。" = "La ID del dispositivo remoto no existe; verifique la ID del dispositivo.";
"远程设备当前不可用,请稍后重试。" = "El dispositivo remoto no está disponible actualmente; inténtelo nuevamente más tarde.";
"远程连接已结束。" = "La conexión remota finalizó.";
"连接" = "Conectar";
"连接延时(RTT)" = "Retardo de conexión (RTT)";
"连接建立后可调整,需被控端支持。" = "Disponible cuando se conecta a un dispositivo remoto compatible.";
"连接建立后锁定横屏" = "Bloquear la pantalla horizontal después de establecer la conexión";
"连接提示" = "Aviso de conexión";
"连接方式" = "Método de conexión";
"连接远程桌面" = "Conectarse al escritorio remoto";
"退格" = "Retroceso";
"通知公告" = "Anuncios";
"部分预览图未能清除,请重试。" = "Algunas vistas previas no se pudieron borrar, inténtalo de nuevo.";
"重试" = "Inténtalo de nuevo";
"重试保存阅读状态" = "Reintentar guardar el estado de lectura";
"锁定" = "Bloquear";
"键盘" = "Teclado";
"键盘操作" = "Operación del teclado";
"阅读并授权" = "Leer y autorizar";
"隐私" = "Privacidad";
"隐私与授权" = "Privacidad y autorización";
"隐私屏" = "Pantalla de privacidad";
"隐私屏已关闭" = "Privacidad desactivada";
"隐私屏已开启" = "Privacidad activada";
"隐私屏已被解锁" = "Pantalla de privacidad desbloqueada";
"隐私屏异常" = "Anormalidad en la pantalla de privacidad";
"隐私屏暂不可用" = "La pantalla de privacidad no está disponible temporalmente";
"隐私屏状态已过期,等待电脑确认" = "El estado de la pantalla de privacidad ha caducado, esperando confirmación por computadora";
"隐私屏状态待确认" = "Estado de la pantalla de privacidad por confirmar";
"隐私政策" = "Política de privacidad";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "La política de privacidad no se puede leer temporalmente. Rechace conectarse a Internet y reinstale la aplicación.";
"隐藏密码" = "Ocultar contraseña";
"静音" = "Silencio";
"音频" = "Audio";
"项目主页" = "Sitio web del proyecto";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "Las vistas previas pueden contener información personal o laboral de la computadora remota. Después de encenderlo, cada conexión guardará automáticamente una imagen en la máquina y la mostrará en las conexiones recientes. Puedes cerrar y borrar en cualquier momento.";
"高" = "Alta";
"默认" = "Predeterminado";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Desactivado de forma predeterminada. Cuando se enciende, el iPhone entra y permanece en orientación horizontal cuando está conectado; cuando está apagado, sigue la orientación del dispositivo. Los iPad no se ven afectados.";
"鼠标右键" = "Botón derecho del ratón";
"鼠标左键" = "Botón izquierdo del ratón";
"鼠标控制" = "Control del ratón";
"鼠标滚轮" = "Rueda del ratón";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk utilise le réseau local pour découvrir et établir des connexions de contrôle à distance P2P natives.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "Échec de la transmission %1$@";
"%1$@ 台设备" = "Appareils : %1$@";
"%1$@ 条未读" = "Éléments non lus %1$@";
"%1$@键盘页" = "Page du clavier %1$@";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, réception de %2$@, envoi de %3$@, taux de perte de paquets %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk utilise le réseau local pour découvrir et établir des connexions de contrôle à distance P2P natives.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del nécessite l'activation à distance des services système Windows";
"GNU GPL 第 3 版" = "GNU GPL version 3";
"OpenFEC 告知" = "Avis OpenFEC";
"OpenFEC 许可与源码" = "Licence et source OpenFEC";
"P2P 直连" = "Connexion directe P2P";
"P2P/TURN 建链失败,请检查网络后重试。" = "Échec de l'établissement du lien P2P/TURN, veuillez vérifier le réseau et réessayer.";
"SRTP 已启用" = "SRTP activé";
"TLS 证书校验失败" = "La vérification du certificat TLS a échoué";
"TURN 中继" = "Relais TOUR";
"上下滑动,可滑出按键范围" = "Faites glisser vers le haut et vers le bas pour sortir de la plage de touches";
"丢包率" = "Taux de perte";
"中" = "Moyenne";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Donne la priorité au maintien de la fluidité de l'image et réduira la résolution de manière plus agressive lorsque la bande passante ou la pression d'encodage est élevée.";
"低" = "Faible";
"使用自定义服务器" = "Utilisation d'un serveur personnalisé";
"保存密码" = "Enregistrer le mot de passe";
"保存远程画面预览" = "Enregistrer les aperçus de l'écran distant";
"保存远程画面预览?" = "Enregistrer l'aperçu de l'écran distant ?";
"信令服务器已关闭连接" = "Le serveur de signalisation a fermé la connexion";
"信令服务重连中" = "Reconnexion du service de signalisation";
"信令连接失败" = "Échec de la connexion de signalisation";
"信令连接已关闭" = "Connexion de signalisation fermée";
"偏好模式" = "Mode préférence";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Faites glisser le curseur comme un trackpad et actionnez la position actuelle du curseur lorsque vous cliquez dessus.";
"公告加载失败,请重试。" = "Le chargement de l'annonce a échoué, veuillez réessayer.";
"公告详情" = "Détails de l'annonce";
"共 %1$@ 条 · %2$@ 条未读" = "Total des éléments %1$@· Éléments non lus %2$@";
"关于" = "À propos";
"关于 CrossDesk" = "À propos de CrossDesk";
"关于,有新版本" = "À propos, il existe une nouvelle version";
"关闭" = "Fermer";
"关闭控制栏" = "Fermer la barre de contrôle";
"关闭隐私屏" = "Désactiver l'écran de confidentialité";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Équilibre la clarté et la douceur, en réduisant seulement progressivement la résolution sous une pression constante.";
"分辨率" = "Résolution";
"切换到电脑键盘" = "Passer au clavier de l'ordinateur";
"切换到系统键盘" = "Passer au clavier système";
"删除" = "Supprimer";
"删除公告" = "Supprimer l'annonce";
"删除记录" = "Supprimer l'enregistrement";
"删除这条公告?" = "Supprimer cette annonce ?";
"刷新公告" = "Actualiser l'annonce";
"剪贴板" = "Presse-papiers";
"剪贴板中没有文本" = "Aucun texte dans le presse-papiers";
"剪贴板文本超过 128 KiB" = "Le texte du presse-papiers dépasse 128 Ko";
"加载中…" = "Chargement…";
"加载更多" = "Charger plus";
"升级提示" = "Mise à jour conseillée";
"单次输入最多 16 KiB,且不能包含空字符" = "Une seule entrée peut atteindre 16 Ko et ne peut pas contenir de caractères nuls";
"发送" = "Envoi";
"发送文件" = "Envoyer le fichier";
"取消" = "Annuler";
"取消连接" = "Annuler la connexion";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Chaque composant est soumis à la licence open source répertoriée, et les droits accordés ne sont pas soumis aux conditions générales d'utilisation de cette application. La visualisation du code source nécessite une connexion Internet.";
"合计" = "Total";
"同意并继续" = "Accepter et continuer";
"回车" = "Entrez";
"在线" = "En ligne";
"地址" = "Adresse";
"填写服务器地址" = "Remplissez l'adresse du serveur";
"填写端口" = "Remplissez le port";
"声音" = "Son";
"失败" = "Échec";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Maintenez toujours une résolution plus élevée et réduisez la fréquence d'images pour garantir la clarté de l'image lorsque la bande passante est insuffisante.";
"媒体加密" = "Cryptage multimédia";
"字母数字" = "Lettres et chiffres";
"完成" = "Terminé";
"完成隐私授权后可检查更新" = "Vous pouvez vérifier les mises à jour après avoir terminé l'autorisation de confidentialité";
"完成隐私授权后登记本机身份" = "Enregistrez l'identité locale après avoir terminé l'autorisation de confidentialité";
"密码将安全保存在本机 Keychain 中" = "Les mots de passe seront stockés en toute sécurité dans le trousseau local";
"对端 ID" = "ID à distance";
"对端 ID %1$@" = "ID homologue %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Arrêtera la connexion à distance, fermera la sauvegarde de l'aperçu de l'écran et effacera l'aperçu existant. Les enregistrements de connexion et les fichiers reçus seront conservés et les données du serveur existantes ne seront pas supprimées. Vous pouvez réautoriser à tout moment.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Tous les aperçus à distance enregistrés sur cette machine seront supprimés, tandis que les enregistrements de connexion, les mots de passe et les fichiers reçus seront conservés.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Tous les aperçus à distance enregistrés sur cette machine seront supprimés, tandis que les enregistrements de connexion, les mots de passe et les fichiers reçus seront conservés. Les connexions ultérieures enregistreront toujours les nouveaux aperçus.";
"屏幕方向" = "Orientation de l'écran";
"展开远程控制菜单" = "Développer le menu de la télécommande";
"已保存密码" = "Mot de passe enregistré";
"已勾选" = "Vérifié";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del envoyé";
"已发送本机剪贴板" = "Presse-papiers local envoyé";
"已授权" = "Accordé";
"已接收 %1$@" = "Reçu %1$@";
"已接收远端剪贴板" = "Presse-papiers distant reçu";
"已读" = "Lire";
"已连接" = "Connecté";
"已连接服务器" = "Connecté au serveur";
"帧率" = "Fréquence d'images";
"帧率优先" = "Fluidité";
"平衡" = "Équilibré";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "Le fichier de licence dans l'application est manquant ou endommagé, veuillez réinstaller l'application.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "La version de développement contient des modifications qui n'ont pas encore été publiées. Le lien suivant est le code source de la version de base.";
"开启保存" = "Activer l’enregistrement";
"开启隐私屏" = "Activer l'écran de confidentialité";
"开源组件" = "Composants open source";
"开源软件权利说明" = "Description des droits sur les logiciels open source";
"当前已是最新版本" = "Il s'agit actuellement de la dernière version";
"成功连接后,这里会显示远端桌面的缩略图。" = "Après une connexion réussie, une vignette du bureau distant s'affichera ici.";
"我已阅读并同意《隐私政策》" = "J'ai lu et accepté la politique de confidentialité";
"我已阅读并同意隐私政策" = "J'ai lu et j'accepte la politique de confidentialité";
"打开隐私政策" = "Ouvrir la politique de confidentialité";
"指令发送失败,请重试" = "L'envoi de la commande a échoué, veuillez réessayer";
"指令已发送,等待电脑确认…" = "La commande a été envoyée, en attente de confirmation informatique...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Appuyez et faites glisser pour sortir de la plage de touches, relâchez pour terminer le glissement";
"授权后可使用远程连接。" = "Les connexions à distance sont disponibles après autorisation.";
"授权状态" = "Statut d'autorisation";
"接收" = "Réception";
"控制模式" = "Mode de contrôle";
"控制端解码器" = "Décodeur local";
"撤回授权" = "Retirer l'autorisation";
"撤回隐私授权" = "Retirer l'autorisation de confidentialité";
"收起电脑键盘" = "Masquer le clavier du PC";
"收起系统键盘" = "Réduire le clavier système";
"收起远程控制菜单" = "Réduire le menu de la télécommande";
"收起鼠标" = "Masquer la souris";
"数据" = "Données";
"断开后将返回首页" = "Reviendra à la page d'accueil après la déconnexion";
"断开远程连接?" = "Déconnecter la connexion à distance ?";
"断开连接" = "Déconnecter";
"新版本" = "Nouvelle version";
"新版本可用:v%1$@" = "Nouvelle version disponible : v%1$@";
"无效" = "Invalide";
"无未读公告" = "Aucune annonce non lue";
"无法" = "Impossible de";
"无法保存本地阅读状态,请重试。" = "Impossible d'enregistrer l'état de lecture localement. Veuillez réessayer.";
"无法删除这条公告,请重试。" = "Impossible de supprimer cette annonce. Veuillez réessayer.";
"无法访问本机登录凭据,请解锁后重试" = "Impossible d'accéder aux informations de connexion locales, veuillez déverrouiller et réessayer";
"无法读取开源许可" = "Impossible de lire la licence open source";
"无法读取所选文件,请重试" = "Impossible de lire le fichier sélectionné, veuillez réessayer";
"是否撤回隐私授权?" = "Retirer l'autorisation de confidentialité ?";
"显示器" = "Écran";
"显示器 %1$@" = "Moniteur %1$@";
"显示器 1" = "Moniteur 1";
"显示密码" = "Afficher le mot de passe";
"暂不同意" = "Pas maintenant";
"暂无公告" = "Aucune annonce";
"最近连接" = "Connexions récentes";
"服务器" = "Serveur";
"服务器地址" = "Adresse du serveur";
"服务器端口" = "Port du serveur";
"服务器配置无效" = "Configuration du serveur invalide";
"未勾选" = "Non coché";
"未启用" = "Non activé";
"未授权" = "Non autorisé";
"未知" = "Inconnu";
"未知信令状态" = "État de signalisation inconnu";
"未知连接状态" = "État de connexion inconnu";
"未读" = "Non lu";
"未连接" = "Non connecté";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Cette application utilise OpenFEC pour la correction d'erreur directe sousCeCILL-C1.0, qui offre des garanties limitées et limite la responsabilité. Consultez la licence complète pour plus de détails.";
"本机 ID %1$@" = "ID local %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "Les informations de connexion pour cette machine ne sont temporairement pas sécurisées. Veuillez déverrouiller votre appareil et cliquer sur « Appliquer » dans les paramètres pour réessayer.";
"本版本源码与构建说明" = "Code source et instructions de construction pour cette version";
"本版本源码信息暂不可用。" = "La révision source n'est pas disponible pour cette version.";
"构建说明" = "Instructions de construction";
"构建配方与项目补丁" = "Créer des recettes et des correctifs de projet";
"查看源码" = "Afficher le code source";
"检查失败,请稍后重试" = "La vérification a échoué. Veuillez réessayer plus tard.";
"检查更新" = "Rechercher des mises à jour";
"正在关闭隐私屏…" = "Fermeture de l'écran de confidentialité…";
"正在准备连接…" = "Préparation de la connexion...";
"正在发送 %1$@" = "Envoi de %1$@";
"正在发送 Ctrl+Alt+Del…" = "Envoi de Ctrl+Alt+Del…";
"正在取回远程画面…" = "Récupération de l'écran distant...";
"正在建立远程连接" = "Établissement d'une connexion à distance";
"正在开启隐私屏…" = "Activation de l'écran de confidentialité...";
"正在收集 ICE 候选" = "Collecte des candidats ICE";
"正在检查更新…" = "Vérification des mises à jour…";
"正在清除…" = "Effacement...";
"正在确认设备状态…" = "Confirmation de l'état de l'appareil...";
"正在获取本机 ID…" = "Obtention d'une pièce d'identité locale...";
"正在连接" = "Connexion";
"正在连接信令服务" = "Connexion au service de signalisation";
"清除" = "Effacer";
"清除预览图" = "Supprimer les aperçus";
"清除预览图?" = "Supprimer les aperçus ?";
"源码与构建说明" = "Code source et instructions de construction";
"源码版本" = "Version du code source";
"版本" = "Version";
"版权与许可声明" = "Déclaration de droits d'auteur et de licence";
"电脑键盘" = "Clavier d'ordinateur";
"画中画未能开启,请保持应用在前台后重试。" = "L'image dans l'image n'a pas pu s'ouvrir. Veuillez conserver l'application au premier plan et réessayer.";
"画质优先" = "Qualité";
"画面偏好" = "Préférence vidéo";
"画面延时" = "Latence vidéo";
"画面设置" = "Paramètres vidéo";
"画面质量" = "Qualité vidéo";
"画面采集帧率" = "Fréquence d'images de capture vidéo";
"相对位置" = "Pavé tactile";
"相对鼠标" = "Souris relative";
"知道了" = "Compris";
"确定" = "D'accord";
"确认" = "Confirmer";
"离线" = "Hors ligne";
"空格" = "Espace";
"端口" = "Port";
"符号功能" = "Symboles et touches";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Attendez que l'ordinateur revienne à l'état de l'écran de confidentialité ; les ordinateurs plus anciens peuvent ne pas le prendre en charge.";
"等待连接" = "En attente de connexion";
"等待隐私授权" = "En attente d'une autorisation de confidentialité";
"系统键盘" = "Clavier système";
"绝对位置" = "Toucher direct";
"绝对鼠标" = "Souris absolue";
"网络状态" = "Réseau";
"网络连接已中断,请重新连接。" = "La connexion réseau a été interrompue, veuillez vous reconnecter.";
"自定义" = "Personnalisé";
"虚拟鼠标" = "Souris virtuelle";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "Le terminal contrôlé ne prend pas en charge la saisie directe du chinois, veuillez mettre à jour le terminal contrôlé par macOS ; vous pouvez également d'abord copier le texte, l'envoyer via le presse-papiers du menu de la télécommande, puis le coller";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "L'écran de confidentialité à distance a été déverrouillé à l'aide du raccourci local. Verrouillez-le à nouveau pour restaurer la confidentialité et désactiver le déverrouillage des raccourcis locaux.";
"被控端平台:%1$@" = "Plateforme terminale contrôlée :%1$@";
"被控端版本过低,请升级到最新版本。" = "La version contrôlée est trop basse, veuillez mettre à niveau vers la dernière version.";
"被控端编码器" = "Encodeur déporté";
"视频" = "Vidéo";
"视频 %1$@×%2$@ · %3$@ 帧" = "Vidéo %1$@×%2$@· Images %3$@";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "La position tactile correspond directement à la position de l'écran distant, ce qui convient à un positionnement rapide.";
"许可" = "Licence";
"许可与版权声明" = "Licences et mentions de droits d'auteur";
"许可与版权声明可离线查阅。" = "La licence et la notice de copyright sont disponibles hors ligne.";
"设备离线" = "Appareil hors ligne";
"设置" = "Réglages";
"设置,有新版本" = "Paramètres, il existe une nouvelle version";
"访问密码" = "Mot de passe d'accès";
"访问密码错误,请重新输入。" = "Le mot de passe d'accès est erroné, veuillez le saisir à nouveau.";
"请求超时,尚未确认电脑的隐私屏状态" = "Le délai de demande a expiré et l'état de l'écran de confidentialité de l'ordinateur n'a pas été confirmé.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Veuillez saisir un port compris entre 1 et 65 535 ou désactivez « Utiliser un serveur personnalisé ».";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Veuillez saisir l'adresse du serveur ou désactiver « Utiliser un serveur personnalisé ».";
"请输入远程设备 ID" = "Veuillez saisir l'ID de l'appareil distant";
"调整失败,请重试。" = "Impossible d'appliquer les paramètres. Essayer à nouveau.";
"软件许可" = "Licence du logiciel";
"返回控制栏" = "Retour à la barre de contrôle";
"还没有连接记录" = "Aucun enregistrement de connexion pour l'instant";
"这条公告已更新或删除,请返回公告列表查看。" = "Cette annonce a été mise à jour ou supprimée, veuillez revenir à la liste des annonces pour la consulter.";
"远程桌面" = "Bureau à distance";
"远程设备 ID 不存在,请检查设备 ID。" = "L'ID de l'appareil distant n'existe pas, veuillez vérifier l'ID de l'appareil.";
"远程设备当前不可用,请稍后重试。" = "L'appareil distant est actuellement indisponible, veuillez réessayer plus tard.";
"远程连接已结束。" = "Connexion à distance terminée.";
"连接" = "Connecter";
"连接延时(RTT)" = "Délai de connexion (RTT)";
"连接建立后可调整,需被控端支持。" = "Disponible lors de la connexion à un appareil distant pris en charge.";
"连接建立后锁定横屏" = "Verrouiller l'écran paysage une fois la connexion établie";
"连接提示" = "Invite de connexion";
"连接方式" = "Méthode de connexion";
"连接远程桌面" = "Connectez-vous au bureau à distance";
"退格" = "Retour arrière";
"通知公告" = "Annonces";
"部分预览图未能清除,请重试。" = "Certains aperçus n'ont pas pu être effacés, veuillez réessayer.";
"重试" = "Réessayez";
"重试保存阅读状态" = "Réessayez d'enregistrer l'état de lecture";
"锁定" = "Verrouillage";
"键盘" = "Clavier";
"键盘操作" = "Fonctionnement du clavier";
"阅读并授权" = "Lire et autoriser";
"隐私" = "Confidentialité";
"隐私与授权" = "Confidentialité et autorisation";
"隐私屏" = "Écran de confidentialité";
"隐私屏已关闭" = "Confidentialité inactive";
"隐私屏已开启" = "Confidentialité active";
"隐私屏已被解锁" = "Écran de confidentialité déverrouillé";
"隐私屏异常" = "Anomalie de l'écran de confidentialité";
"隐私屏暂不可用" = "L'écran de confidentialité est temporairement indisponible";
"隐私屏状态已过期,等待电脑确认" = "L'état de l'écran de confidentialité a expiré, en attente de confirmation par l'ordinateur";
"隐私屏状态待确认" = "Statut de l'écran de confidentialité à confirmer";
"隐私政策" = "Politique de confidentialité";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "La politique de confidentialité ne peut pas être lue temporairement. Veuillez refuser de vous connecter à Internet et réinstaller l'application.";
"隐藏密码" = "Masquer le mot de passe";
"静音" = "Muet";
"音频" = "Audio";
"项目主页" = "Site Web du projet";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "Les aperçus peuvent contenir des informations personnelles ou professionnelles provenant de l'ordinateur distant. Après l'avoir allumé, chaque connexion enregistrera automatiquement une image sur la machine et l'affichera dans les connexions récentes. Vous pouvez fermer et effacer à tout moment.";
"高" = "Élevée";
"默认" = "Par défaut";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Désactivé par défaut. Lorsqu'il est allumé, l'iPhone passe et reste en orientation paysage lorsqu'il est connecté ; lorsqu'il est éteint, il suit l'orientation de l'appareil. Les iPad ne sont pas concernés.";
"鼠标右键" = "Bouton droit de la souris";
"鼠标左键" = "Bouton gauche de la souris";
"鼠标控制" = "Contrôle de la souris";
"鼠标滚轮" = "Molette de la souris";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDeskは、ローカル ネットワークを使用してネイティブ P2P リモート コントロール接続を検出し、確立します。";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "%1$@送信失敗";
"%1$@ 台设备" = "デバイス数: %1$@";
"%1$@ 条未读" = "%1$@の未読アイテム";
"%1$@键盘页" = "%1$@キーボードページ";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@、受信%2$@、送信%3$@、パケットロス率%4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDeskは、ローカル ネットワークを使用してネイティブ P2P リモート コントロール接続を検出し、確立します。";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Delでは Windows システム サービスのリモート アクティベーションが必要です";
"GNU GPL 第 3 版" = "GNU GPL バージョン 3";
"OpenFEC 告知" = "OpenFEC通知";
"OpenFEC 许可与源码" = "OpenFECライセンスとソース";
"P2P 直连" = "P2P直接接続";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURNリンクの確立に失敗しました。ネットワークを確認して再試行してください。";
"SRTP 已启用" = "SRT有効";
"TLS 证书校验失败" = "TLS証明書の検証に失敗しました";
"TURN 中继" = "ターンリレー";
"上下滑动,可滑出按键范围" = "上下にスライドしてキー範囲外にスライドします";
"丢包率" = "損失率";
"中" = "中";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "画像を滑らかに保つことを優先し、帯域幅やエンコード圧力が高い場合には、より積極的に解像度を下げます。";
"低" = "低";
"使用自定义服务器" = "カスタムサーバーの使用";
"保存密码" = "パスワードの保存";
"保存远程画面预览" = "リモート画面のプレビューを保存する";
"保存远程画面预览?" = "リモート画面のプレビューを保存しますか?";
"信令服务器已关闭连接" = "シグナリングサーバーが接続を閉じました";
"信令服务重连中" = "シグナリング サービスの再接続";
"信令连接失败" = "シグナリング接続に失敗しました";
"信令连接已关闭" = "信号接続が閉じられています";
"偏好模式" = "プリファレンスモード";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "トラックパッドのようにカーソルをスライドさせ、クリックすると現在のカーソル位置を操作します。";
"公告加载失败,请重试。" = "お知らせを読み込めませんでした。再試行してください。";
"公告详情" = "発表内容";
"共 %1$@ 条 · %2$@ 条未读" = "合計%1$@個のアイテム ·%2$@個の未読アイテム";
"关于" = "概要";
"关于 CrossDesk" = "CrossDesk について";
"关于,有新版本" = "について、新しいバージョンがあります";
"关闭" = "閉じる";
"关闭控制栏" = "コントロールバーを閉じる";
"关闭隐私屏" = "プライバシー画面を無効にする";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "明瞭さと滑らかさのバランスを保ち、一定の圧力下で解像度を徐々に低下させるだけです。";
"分辨率" = "解像度";
"切换到电脑键盘" = "コンピューターのキーボードに切り替える";
"切换到系统键盘" = "システムキーボードに切り替える";
"删除" = "削除";
"删除公告" = "お知らせの削除";
"删除记录" = "レコードの削除";
"删除这条公告?" = "このお知らせを削除しますか?";
"刷新公告" = "リフレッシュのお知らせ";
"剪贴板" = "クリップボード";
"剪贴板中没有文本" = "クリップボードにテキストがありません";
"剪贴板文本超过 128 KiB" = "クリップボードのテキストが 128 KiB を超えています";
"加载中…" = "読み込み中…";
"加载更多" = "さらに読み込む";
"升级提示" = "更新のご案内";
"单次输入最多 16 KiB,且不能包含空字符" = "1 つの入力は最大 16 KiB であり、null 文字を含めることはできません";
"发送" = "送信";
"发送文件" = "ファイル送信";
"取消" = "キャンセル";
"取消连接" = "接続をキャンセルする";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "各コンポーネントは、リストされているオープン ソース ライセンスの対象となり、付与された権利は、このアプリケーションの一般的な使用条件の対象ではありません。ソース コードを表示するには、インターネット接続が必要です。";
"合计" = "合計";
"同意并继续" = "同意して続行";
"回车" = "入る";
"在线" = "オンライン";
"地址" = "住所";
"填写服务器地址" = "サーバーアドレスを入力します";
"填写端口" = "ポートを記入します";
"声音" = "サウンド";
"失败" = "失敗しました";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "帯域幅が不十分な場合は、常に高い解像度を維持し、フレーム レートを下げて画像の鮮明さを確保します。";
"媒体加密" = "メディアの暗号化";
"字母数字" = "英数字";
"完成" = "完了";
"完成隐私授权后可检查更新" = "プライバシー認証を完了すると、アップデートを確認できます";
"完成隐私授权后登记本机身份" = "プライバシー認証完了後、ローカル ID を登録します";
"密码将安全保存在本机 Keychain 中" = "パスワードはローカルのキーチェーンに安全に保存されます";
"对端 ID" = "リモートID";
"对端 ID %1$@" = "ピアID%1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "リモート接続を停止し、画面プレビューの保存を閉じて、既存のプレビューをクリアします。接続記録と受信したファイルは保持され、既存のサーバー データは削除されません。いつでも再認証できます。";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "本機に保存されているリモートプレビューはすべて削除されますが、接続記録、パスワード、受信したファイルは保持されます。";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "本機に保存されているリモート プレビューはすべて削除されますが、接続記録、パスワード、受信したファイルは保持されます。後続の接続でも新しいプレビューが保存されます。";
"屏幕方向" = "画面の向き";
"展开远程控制菜单" = "リモコンメニューを展開する";
"已保存密码" = "パスワード保存済み";
"已勾选" = "チェックあり";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Delを送信しました";
"已发送本机剪贴板" = "ローカルのクリップボードを送信しました";
"已授权" = "認められる";
"已接收 %1$@" = "%1$@を受け取りました";
"已接收远端剪贴板" = "リモートクリップボードを受信しました";
"已读" = "読む";
"已连接" = "接続されました";
"已连接服务器" = "サーバーに接続しました";
"帧率" = "フレームレート";
"帧率优先" = "滑らかさ";
"平衡" = "バランス";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "アプリケーション内のライセンス ファイルが見つからないか破損しています。アプリケーションを再インストールしてください。";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "開発バージョンには、まだリリースされていない修正が含まれています。以下のリンクは基本バージョンのソースコードです。";
"开启保存" = "保存を有効にする";
"开启隐私屏" = "プライバシー画面を有効にする";
"开源组件" = "オープンソースコンポーネント";
"开源软件权利说明" = "オープンソース ソフトウェアの権利の説明";
"当前已是最新版本" = "これが現在の最新バージョンです";
"成功连接后,这里会显示远端桌面的缩略图。" = "接続に成功すると、ここにリモート デスクトップのサムネイルが表示されます。";
"我已阅读并同意《隐私政策》" = "プライバシーポリシーを読んで同意します";
"我已阅读并同意隐私政策" = "プライバシーポリシーを読んで同意します";
"打开隐私政策" = "オープンなプライバシーポリシー";
"指令发送失败,请重试" = "コマンドの送信に失敗しました。もう一度お試しください。";
"指令已发送,等待电脑确认…" = "コマンドが送信され、コンピューターの確認を待っています...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "押してドラッグするとキー範囲からスライドし、放すとドラッグが終了します";
"授权后可使用远程连接。" = "認証後、リモート接続が可能になります。";
"授权状态" = "認可ステータス";
"接收" = "受信";
"控制模式" = "制御モード";
"控制端解码器" = "ローカルデコーダ";
"撤回授权" = "認可の撤回";
"撤回隐私授权" = "プライバシー認証の撤回";
"收起电脑键盘" = "PC キーボードを隠す";
"收起系统键盘" = "システムキーボードを折りたたむ";
"收起远程控制菜单" = "リモコンメニューを折りたたむ";
"收起鼠标" = "マウスを隠す";
"数据" = "データ";
"断开后将返回首页" = "切断後ホームページに戻ります";
"断开远程连接?" = "リモート接続を切断しますか?";
"断开连接" = "切断";
"新版本" = "新しいバージョン";
"新版本可用:v%1$@" = "利用可能な新しいバージョン: v%1$@";
"无效" = "無効";
"无未读公告" = "未読のお知らせはありません";
"无法" = "できません";
"无法保存本地阅读状态,请重试。" = "読み取りステータスをローカルに保存できません。もう一度試してください。";
"无法删除这条公告,请重试。" = "このお知らせは削除できません。もう一度試してください。";
"无法访问本机登录凭据,请解锁后重试" = "ローカル ログイン資格情報にアクセスできません。ロックを解除して再試行してください。";
"无法读取开源许可" = "オープンソースライセンスを読み取れません";
"无法读取所选文件,请重试" = "選択したファイルを読み取れません。もう一度お試しください。";
"是否撤回隐私授权?" = "プライバシー認証を取り消しますか?";
"显示器" = "ディスプレイ";
"显示器 %1$@" = "モニター%1$@";
"显示器 1" = "モニター1";
"显示密码" = "パスワードを表示";
"暂不同意" = "今は同意しない";
"暂无公告" = "アナウンスはありません";
"最近连接" = "最近の接続";
"服务器" = "サーバー";
"服务器地址" = "サーバーアドレス";
"服务器端口" = "サーバーポート";
"服务器配置无效" = "無効なサーバー構成";
"未勾选" = "チェックなし";
"未启用" = "有効ではありません";
"未授权" = "無許可";
"未知" = "不明";
"未知信令状态" = "不明な信号ステータス";
"未知连接状态" = "不明な接続ステータス";
"未读" = "未読";
"未连接" = "接続されていません";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "このアプリケーションは、限定保証を提供し、責任を制限するCeCILL-C1.0 に基づく前方誤り訂正にOpenFECを使用します。詳細については、完全なライセンスを参照してください。";
"本机 ID %1$@" = "ローカルID%1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "このマシンのログイン資格情報は一時的に安全ではありません。デバイスのロックを解除し、設定で「適用」をクリックして再試行してください。";
"本版本源码与构建说明" = "このバージョンのソース コードとビルド手順";
"本版本源码信息暂不可用。" = "このビルドではソース リビジョンが利用できません。";
"构建说明" = "組み立て説明書";
"构建配方与项目补丁" = "ビルドレシピとプロジェクトパッチ";
"查看源码" = "ソースコードを表示する";
"检查失败,请稍后重试" = "チェックに失敗しました。後でもう一度試してください。";
"检查更新" = "アップデートを確認する";
"正在关闭隐私屏…" = "プライバシー画面を閉じています…";
"正在准备连接…" = "接続準備中...";
"正在发送 %1$@" = "%1$@の送信";
"正在发送 Ctrl+Alt+Del…" = "Ctrl+Alt+Delを送信中 …";
"正在取回远程画面…" = "リモート画面を取得中...";
"正在建立远程连接" = "リモート接続の確立";
"正在开启隐私屏…" = "プライバシー画面を有効化中…";
"正在收集 ICE 候选" = "ICE 候補を収集中";
"正在检查更新…" = "アップデートを確認しています…";
"正在清除…" = "クリア中...";
"正在确认设备状态…" = "機器の状態を確認しています...";
"正在获取本机 ID…" = "ローカルIDを取得中...";
"正在连接" = "接続する";
"正在连接信令服务" = "シグナリングサービスへの接続";
"清除" = "クリア";
"清除预览图" = "プレビューを削除";
"清除预览图?" = "プレビューを削除しますか?";
"源码与构建说明" = "ソースコードとビルド手順";
"源码版本" = "ソースコードのバージョン";
"版本" = "バージョン";
"版权与许可声明" = "著作権およびライセンスに関する声明";
"电脑键盘" = "コンピューターのキーボード";
"画中画未能开启,请保持应用在前台后重试。" = "ピクチャーインピクチャーを開けませんでした。アプリケーションをフォアグラウンドに保ち、もう一度お試しください。";
"画质优先" = "画質";
"画面偏好" = "ビデオ設定";
"画面延时" = "映像遅延";
"画面设置" = "ビデオ設定";
"画面质量" = "ビデオ品質";
"画面采集帧率" = "ビデオキャプチャフレームレート";
"相对位置" = "トラックパッド";
"相对鼠标" = "相対マウス";
"知道了" = "分かりました";
"确定" = "OK";
"确认" = "確認する";
"离线" = "オフライン";
"空格" = "スペース";
"端口" = "港";
"符号功能" = "記号・機能";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "コンピューターがプライバシー画面の状態に戻るまで待ちます。古いコンピューターではサポートされていない可能性があります。";
"等待连接" = "接続待ち";
"等待隐私授权" = "プライバシー認証待ち";
"系统键盘" = "システムキーボード";
"绝对位置" = "直接タッチ";
"绝对鼠标" = "アブソリュートマウス";
"网络状态" = "ネットワーク";
"网络连接已中断,请重新连接。" = "ネットワーク接続が切断されました。再接続してください。";
"自定义" = "カスタム";
"虚拟鼠标" = "仮想マウス";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "制御端末は中国語の直接入力をサポートしていません。macOS 制御端末を更新してください。最初にテキストをコピーし、リモート コントロール メニューのクリップボード経由で送信してから貼り付けることもできます。";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "ローカル ショートカットを使用してリモート プライバシー画面のロックが解除されました。再度ロックするとプライバシーが回復され、ローカル ショートカットのロック解除が無効になります。";
"被控端平台:%1$@" = "制御端末プラットフォーム:%1$@";
"被控端版本过低,请升级到最新版本。" = "管理されているバージョンが低すぎるため、最新バージョンにアップグレードしてください。";
"被控端编码器" = "リモートエンコーダー";
"视频" = "ビデオ";
"视频 %1$@×%2$@ · %3$@ 帧" = "ビデオ%1$@×%2$@・%3$@フレーム";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "タッチ位置はリモート画面位置に直接対応するため、素早い位置決めに適しています。";
"许可" = "ライセンス";
"许可与版权声明" = "ライセンスと著作権表示";
"许可与版权声明可离线查阅。" = "ライセンスと著作権表示はオフラインでも利用できます。";
"设备离线" = "デバイスがオフライン";
"设置" = "設定";
"设置,有新版本" = "設定、新しいバージョンがあります";
"访问密码" = "アクセスパスワード";
"访问密码错误,请重新输入。" = "アクセスパスワードが間違っていますので、再度入力してください。";
"请求超时,尚未确认电脑的隐私屏状态" = "リクエストがタイムアウトし、コンピュータのプライバシー画面の状態が確認できませんでした。";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "1 ~ 65535 の範囲でポートを入力するか、「カスタム サーバーを使用する」をオフにしてください。";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "サーバーアドレスを入力するか、「カスタムサーバーを使用する」をオフにしてください。";
"请输入远程设备 ID" = "リモートデバイスIDを入力してください";
"调整失败,请重试。" = "設定を適用できませんでした。もう一度やり直してください。";
"软件许可" = "ソフトウェアライセンス";
"返回控制栏" = "コントロールバーに戻る";
"还没有连接记录" = "まだ接続実績がありません";
"这条公告已更新或删除,请返回公告列表查看。" = "このお知らせは更新または削除されました。表示するにはお知らせリストに戻ってください。";
"远程桌面" = "リモートデスクトップ";
"远程设备 ID 不存在,请检查设备 ID。" = "リモートデバイス ID が存在しません。デバイス ID を確認してください。";
"远程设备当前不可用,请稍后重试。" = "リモート デバイスは現在利用できません。後でもう一度試してください。";
"远程连接已结束。" = "リモート接続が終了しました。";
"连接" = "接続";
"连接延时(RTT)" = "接続遅延 (RTT)";
"连接建立后可调整,需被控端支持。" = "サポートされているリモート デバイスに接続されている場合に使用できます。";
"连接建立后锁定横屏" = "接続完了後、横画面をロックする";
"连接提示" = "接続プロンプト";
"连接方式" = "接続方法";
"连接远程桌面" = "リモートデスクトップに接続する";
"退格" = "バックスペース";
"通知公告" = "お知らせ";
"部分预览图未能清除,请重试。" = "一部のプレビューをクリアできませんでした。もう一度お試しください。";
"重试" = "もう一度お試しください";
"重试保存阅读状态" = "読み取りステータスの保存をリトライする";
"锁定" = "ロック";
"键盘" = "キーボード";
"键盘操作" = "キーボード操作";
"阅读并授权" = "読んで承認する";
"隐私" = "プライバシー";
"隐私与授权" = "プライバシーと認証";
"隐私屏" = "プライバシー画面";
"隐私屏已关闭" = "プライバシー画面:オフ";
"隐私屏已开启" = "プライバシー画面:オン";
"隐私屏已被解锁" = "プライバシー画面のロックが解除されました";
"隐私屏异常" = "プライバシー画面異常";
"隐私屏暂不可用" = "プライバシー画面が一時的に利用できなくなります";
"隐私屏状态已过期,等待电脑确认" = "プライバシー画面のステータスが期限切れになり、コンピュータの確認を待っています";
"隐私屏状态待确认" = "プライバシー画面のステータスを確認する";
"隐私政策" = "プライバシーポリシー";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "一時的にプライバシーポリシーが閲覧できなくなります。インターネットへの接続を拒否し、アプリケーションを再インストールしてください。";
"隐藏密码" = "パスワードを隠す";
"静音" = "ミュート";
"音频" = "オーディオ";
"项目主页" = "プロジェクトWebサイト";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "プレビューには、リモート コンピューターからの個人情報または仕事情報が含まれる場合があります。オンにすると、接続するたびにマシンに写真が自動的に保存され、最近の接続で表示されます。いつでも閉じてクリアできます。";
"高" = "高";
"默认" = "デフォルト";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "デフォルトではオフです。オンにすると、iPhone は接続時に横向きになり、横向きのままになります。オフにすると、デバイスの向きに従います。 iPad は影響を受けません。";
"鼠标右键" = "マウスの右ボタン";
"鼠标左键" = "マウスの左ボタン";
"鼠标控制" = "マウスコントロール";
"鼠标滚轮" = "マウスホイール";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk는 로컬 네트워크를 사용하여 기본 P2P 원격 제어 연결을 검색하고 설정합니다.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "%1$@전송 실패";
"%1$@ 台设备" = "기기: %1$@";
"%1$@ 条未读" = "%1$@읽지 않은 항목";
"%1$@键盘页" = "%1$@키보드 페이지";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, 수신%2$@, 송신%3$@, 패킷 손실률%4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk는 로컬 네트워크를 사용하여 기본 P2P 원격 제어 연결을 검색하고 설정합니다.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del에는 Windows 시스템 서비스의 원격 활성화가 필요합니다.";
"GNU GPL 第 3 版" = "GNU GPL 버전 3";
"OpenFEC 告知" = "OpenFEC공지";
"OpenFEC 许可与源码" = "OpenFEC라이선스 및 소스";
"P2P 直连" = "P2P 직접 연결";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN링크 설정에 실패했습니다. 네트워크를 확인하고 다시 시도하십시오.";
"SRTP 已启用" = "SRTP 활성화됨";
"TLS 证书校验失败" = "TLS인증서 확인 실패";
"TURN 中继" = "TURN 릴레이";
"上下滑动,可滑出按键范围" = "키 범위에서 벗어나려면 위아래로 슬라이드하세요.";
"丢包率" = "손실률";
"中" = "중간";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "영상을 부드럽게 유지하는 데 우선 순위를 두며 대역폭이나 인코딩 압력이 높을 때 해상도를 더욱 적극적으로 줄입니다.";
"低" = "낮음";
"使用自定义服务器" = "커스텀 서버 이용하기";
"保存密码" = "비밀번호 저장";
"保存远程画面预览" = "원격 화면 미리보기 저장";
"保存远程画面预览?" = "원격 화면 미리보기를 저장하시겠습니까?";
"信令服务器已关闭连接" = "시그널링 서버가 연결을 종료했습니다.";
"信令服务重连中" = "시그널링 서비스 재접속";
"信令连接失败" = "시그널링 연결 실패";
"信令连接已关闭" = "신호 연결이 닫혔습니다.";
"偏好模式" = "선호 모드";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "커서를 트랙패드처럼 밀어서 클릭하면 현재 커서 위치를 조작합니다.";
"公告加载失败,请重试。" = "공지사항 로딩에 실패했습니다. 다시 시도해주세요.";
"公告详情" = "공지사항";
"共 %1$@ 条 · %2$@ 条未读" = "총%1$@항목 ·%2$@읽지 않은 항목";
"关于" = "소개";
"关于 CrossDesk" = "CrossDesk 정보";
"关于,有新版本" = "새 버전이 있습니다.";
"关闭" = "닫기";
"关闭控制栏" = "컨트롤 바 닫기";
"关闭隐私屏" = "개인 정보 보호 화면 비활성화";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "선명도와 부드러움의 균형을 유지하며 일정한 압력을 가하면 해상도가 점차 감소합니다.";
"分辨率" = "해상도";
"切换到电脑键盘" = "컴퓨터 키보드로 전환";
"切换到系统键盘" = "시스템 키보드로 전환";
"删除" = "삭제";
"删除公告" = "공지사항 삭제";
"删除记录" = "기록 삭제";
"删除这条公告?" = "이 공지를 삭제하시겠습니까?";
"刷新公告" = "새로고침 안내";
"剪贴板" = "클립보드";
"剪贴板中没有文本" = "클립보드에 텍스트가 없습니다.";
"剪贴板文本超过 128 KiB" = "클립보드 텍스트가 128KiB를 초과합니다.";
"加载中…" = "로드 중…";
"加载更多" = "더 로드하기";
"升级提示" = "업데이트 안내";
"单次输入最多 16 KiB,且不能包含空字符" = "단일 입력은 최대 16KiB일 수 있으며 null 문자를 포함할 수 없습니다.";
"发送" = "송신";
"发送文件" = "파일 보내기";
"取消" = "취소";
"取消连接" = "연결 취소";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "각 구성 요소에는 나열된 오픈 소스 라이선스가 적용되며 부여된 권리에는 이 애플리케이션의 일반 사용 약관이 적용되지 않습니다. 소스 코드를 보려면 인터넷 연결이 필요합니다.";
"合计" = "합계";
"同意并继续" = "동의하고 계속하기";
"回车" = "들어가다";
"在线" = "온라인";
"地址" = "주소";
"填写服务器地址" = "서버 주소를 입력하세요";
"填写端口" = "포트를 채워주세요";
"声音" = "소리";
"失败" = "실패";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "대역폭이 부족할 때 영상 선명도를 보장하려면 항상 더 높은 해상도를 유지하고 프레임 속도를 낮추십시오.";
"媒体加密" = "미디어 암호화";
"字母数字" = "문자·숫자";
"完成" = "완료";
"完成隐私授权后可检查更新" = "개인정보 동의 완료 후 업데이트 내용을 확인할 수 있습니다.";
"完成隐私授权后登记本机身份" = "개인정보 인증 완료 후 로컬 신원 등록";
"密码将安全保存在本机 Keychain 中" = "비밀번호는 로컬 키체인에 안전하게 저장됩니다.";
"对端 ID" = "원격 ID";
"对端 ID %1$@" = "피어 ID%1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "원격 연결을 중지하고 화면 미리보기 저장을 닫은 후 기존 미리보기를 지웁니다. 접속 기록 및 수신 파일은 그대로 유지되며, 기존 서버 데이터는 삭제되지 않습니다. 언제든지 재승인할 수 있습니다.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "이 기기에 저장된 원격 미리보기는 모두 삭제되지만 연결 기록, 비밀번호 및 수신된 파일은 유지됩니다.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "이 기기에 저장된 원격 미리보기는 모두 삭제되지만 연결 기록, 비밀번호 및 수신된 파일은 유지됩니다. 후속 연결에서는 계속해서 새로운 미리보기가 저장됩니다.";
"屏幕方向" = "화면 방향";
"展开远程控制菜单" = "원격제어 메뉴 확장";
"已保存密码" = "비밀번호 저장됨";
"已勾选" = "확인됨";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del가 전송되었습니다.";
"已发送本机剪贴板" = "로컬 클립보드 전송됨";
"已授权" = "승인됨";
"已接收 %1$@" = "%1$@수신됨";
"已接收远端剪贴板" = "원격 클립보드 수신됨";
"已读" = "읽기";
"已连接" = "연결됨";
"已连接服务器" = "서버에 연결됨";
"帧率" = "프레임 속도";
"帧率优先" = "부드러움";
"平衡" = "균형";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "애플리케이션의 라이센스 파일이 없거나 손상되었습니다. 애플리케이션을 다시 설치하십시오.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "개발 버전에는 아직 출시되지 않은 수정 사항이 포함되어 있습니다. 다음 링크는 기본 버전의 소스 코드입니다.";
"开启保存" = "저장 사용";
"开启隐私屏" = "개인 정보 보호 화면 활성화";
"开源组件" = "오픈 소스 구성 요소";
"开源软件权利说明" = "오픈소스 소프트웨어 권리 설명";
"当前已是最新版本" = "현재 최신 버전입니다.";
"成功连接后,这里会显示远端桌面的缩略图。" = "성공적으로 연결되면 원격 데스크톱의 썸네일이 여기에 표시됩니다.";
"我已阅读并同意《隐私政策》" = "개인정보 보호정책을 읽었으며 이에 동의합니다.";
"我已阅读并同意隐私政策" = "개인정보취급방침을 읽었으며 이에 동의합니다.";
"打开隐私政策" = "공개 개인 정보 보호 정책";
"指令发送失败,请重试" = "명령 전송에 실패했습니다. 다시 시도해 주세요.";
"指令已发送,等待电脑确认…" = "명령이 전송되었으며 컴퓨터 확인을 기다리고 있습니다...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "누르고 드래그하여 키 범위 밖으로 슬라이드하고, 드래그를 종료하려면 놓습니다.";
"授权后可使用远程连接。" = "인증 후 원격접속이 가능합니다.";
"授权状态" = "인증상태";
"接收" = "수신";
"控制模式" = "제어 모드";
"控制端解码器" = "로컬 디코더";
"撤回授权" = "철회 승인";
"撤回隐私授权" = "개인정보 승인 철회";
"收起电脑键盘" = "PC 키보드 숨기기";
"收起系统键盘" = "시스템 키보드 축소";
"收起远程控制菜单" = "리모컨 메뉴 접기";
"收起鼠标" = "마우스 숨기기";
"数据" = "데이터";
"断开后将返回首页" = "연결을 끊으면 홈페이지로 돌아갑니다.";
"断开远程连接?" = "원격 연결을 끊으시겠습니까?";
"断开连接" = "연결 해제";
"新版本" = "새 버전";
"新版本可用:v%1$@" = "새 버전 사용 가능: v%1$@";
"无效" = "유효하지 않음";
"无未读公告" = "읽지 않은 공지사항 없음";
"无法" = "할 수 없다";
"无法保存本地阅读状态,请重试。" = "읽기 상태를 로컬에 저장할 수 없습니다. 다시 시도해 주세요.";
"无法删除这条公告,请重试。" = "이 공지를 삭제할 수 없습니다. 다시 시도해 주세요.";
"无法访问本机登录凭据,请解锁后重试" = "로컬 로그인 자격 증명에 액세스할 수 없습니다. 잠금을 해제하고 다시 시도하십시오.";
"无法读取开源许可" = "오픈소스 라이선스를 읽을 수 없습니다.";
"无法读取所选文件,请重试" = "선택한 파일을 읽을 수 없습니다. 다시 시도해 주세요.";
"是否撤回隐私授权?" = "개인정보 보호 승인을 철회하시겠습니까?";
"显示器" = "디스플레이";
"显示器 %1$@" = "모니터%1$@";
"显示器 1" = "모니터 1";
"显示密码" = "비밀번호 표시";
"暂不同意" = "나중에";
"暂无公告" = "공지 없음";
"最近连接" = "최근 연결";
"服务器" = "서버";
"服务器地址" = "서버 주소";
"服务器端口" = "서버 포트";
"服务器配置无效" = "잘못된 서버 구성";
"未勾选" = "체크하지 않음";
"未启用" = "활성화되지 않음";
"未授权" = "무단";
"未知" = "알 수 없음";
"未知信令状态" = "알 수 없는 신호 상태";
"未知连接状态" = "알 수 없는 연결 상태";
"未读" = "읽지 않음";
"未连接" = "연결되지 않음";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "이 애플리케이션은 제한된 보증과 책임 제한을 제공하는CeCILL-C1.0에 따라 순방향 오류 수정을 위해OpenFEC를 사용합니다. 자세한 내용은 전체 라이센스를 참조하세요.";
"本机 ID %1$@" = "로컬 ID%1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "이 시스템의 로그인 자격 증명은 일시적으로 안전하지 않습니다. 기기를 잠금 해제하고 설정에서 '적용'을 클릭하여 다시 시도하세요.";
"本版本源码与构建说明" = "이 버전의 소스 코드 및 빌드 지침";
"本版本源码信息暂不可用。" = "이 빌드에는 소스 개정을 사용할 수 없습니다.";
"构建说明" = "빌드 지침";
"构建配方与项目补丁" = "레시피 및 프로젝트 패치 빌드";
"查看源码" = "소스코드 보기";
"检查失败,请稍后重试" = "확인에 실패했습니다. 나중에 다시 시도해 주세요.";
"检查更新" = "업데이트 확인";
"正在关闭隐私屏…" = "개인정보 보호 화면을 닫는 중…";
"正在准备连接…" = "연결 준비 중...";
"正在发送 %1$@" = "%1$@보내기";
"正在发送 Ctrl+Alt+Del…" = "Ctrl+Alt+Del를 보내는 중…";
"正在取回远程画面…" = "원격 화면을 검색하는 중...";
"正在建立远程连接" = "원격 연결 설정";
"正在开启隐私屏…" = "개인정보 보호 화면을 켜는 중...";
"正在收集 ICE 候选" = "ICE후보 수집";
"正在检查更新…" = "업데이트 확인 중…";
"正在清除…" = "지우는 중...";
"正在确认设备状态…" = "기기 상태를 확인하는 중...";
"正在获取本机 ID…" = "로컬 ID를 얻는 중...";
"正在连接" = "연결 중";
"正在连接信令服务" = "시그널링 서비스 연결";
"清除" = "클리어";
"清除预览图" = "미리보기 삭제";
"清除预览图?" = "미리보기를 삭제할까요?";
"源码与构建说明" = "소스 코드 및 빌드 지침";
"源码版本" = "소스코드 버전";
"版本" = "버전";
"版权与许可声明" = "저작권 및 라이선스 정책";
"电脑键盘" = "컴퓨터 키보드";
"画中画未能开启,请保持应用在前台后重试。" = "PIP(Picture-in-Picture)를 열지 못했습니다. 애플리케이션을 포그라운드에 두고 다시 시도하십시오.";
"画质优先" = "화질";
"画面偏好" = "영상 선호도";
"画面延时" = "영상 지연";
"画面设置" = "영상 설정";
"画面质量" = "비디오 품질";
"画面采集帧率" = "비디오 캡처 프레임 속도";
"相对位置" = "트랙패드";
"相对鼠标" = "상대 마우스";
"知道了" = "알겠습니다";
"确定" = "알았어";
"确认" = "확인";
"离线" = "오프라인";
"空格" = "스페이스";
"端口" = "포트";
"符号功能" = "기호·기능";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "컴퓨터가 개인 정보 보호 화면 상태로 돌아갈 때까지 기다립니다. 오래된 컴퓨터에서는 이를 지원하지 않을 수 있습니다.";
"等待连接" = "연결 대기 중";
"等待隐私授权" = "개인정보 인증 대기 중";
"系统键盘" = "시스템 키보드";
"绝对位置" = "직접 터치";
"绝对鼠标" = "앱솔루트 마우스";
"网络状态" = "네트워크";
"网络连接已中断,请重新连接。" = "네트워크 연결이 중단되었습니다. 다시 연결해 주세요.";
"自定义" = "커스텀";
"虚拟鼠标" = "가상마우스";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "제어 터미널은 중국어 직접 입력을 지원하지 않습니다. macOS 제어 터미널을 업데이트하세요. 텍스트를 먼저 복사한 후 리모컨 메뉴의 클립보드를 통해 전송한 후 붙여넣을 수도 있습니다.";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "로컬 단축키를 사용하여 원격 개인정보 보호 화면을 잠금 해제했습니다. 개인정보 보호를 복원하고 로컬 바로가기 잠금 해제를 비활성화하려면 다시 잠그세요.";
"被控端平台:%1$@" = "제어 터미널 플랫폼:%1$@";
"被控端版本过低,请升级到最新版本。" = "제어 버전이 너무 낮습니다. 최신 버전으로 업그레이드하세요.";
"被控端编码器" = "리모트 엔코더";
"视频" = "영상";
"视频 %1$@×%2$@ · %3$@ 帧" = "비디오%1$@×%2$@·%3$@프레임";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "터치 위치는 원격 화면 위치와 직접적으로 일치하므로 빠른 위치 지정에 적합합니다.";
"许可" = "라이센스";
"许可与版权声明" = "라이센스 및 저작권 고지";
"许可与版权声明可离线查阅。" = "라이센스 및 저작권 고지는 오프라인에서 확인할 수 있습니다.";
"设备离线" = "장치 오프라인";
"设置" = "설정";
"设置,有新版本" = "설정, 새 버전이 있습니다";
"访问密码" = "접속 비밀번호";
"访问密码错误,请重新输入。" = "접속비밀번호가 잘못되었습니다. 다시 입력해 주세요.";
"请求超时,尚未确认电脑的隐私屏状态" = "요청 시간이 초과되어 컴퓨터의 개인 정보 보호 화면 상태가 확인되지 않았습니다.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "1~65535 범위의 포트를 입력하거나 \"사용자 정의 서버 사용\"을 꺼주세요.";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "서버 주소를 입력하시거나, \"커스텀 서버 사용\"을 꺼주세요.";
"请输入远程设备 ID" = "원격장치 ID를 입력해주세요.";
"调整失败,请重试。" = "설정을 적용할 수 없습니다. 다시 시도해 보세요.";
"软件许可" = "소프트웨어 라이센스";
"返回控制栏" = "컨트롤 바로 돌아가기";
"还没有连接记录" = "아직 접속기록이 없습니다";
"这条公告已更新或删除,请返回公告列表查看。" = "본 공지사항은 업데이트 또는 삭제되었습니다. 공지사항 목록으로 돌아가서 확인하시기 바랍니다.";
"远程桌面" = "원격 데스크톱";
"远程设备 ID 不存在,请检查设备 ID。" = "원격 장치 ID가 존재하지 않습니다. 장치 ID를 확인하십시오.";
"远程设备当前不可用,请稍后重试。" = "현재 원격 장치를 사용할 수 없습니다. 나중에 다시 시도하십시오.";
"远程连接已结束。" = "원격 연결이 종료되었습니다.";
"连接" = "연결";
"连接延时(RTT)" = "연결 지연(RTT)";
"连接建立后可调整,需被控端支持。" = "지원되는 원격 장치에 연결된 경우 사용할 수 있습니다.";
"连接建立后锁定横屏" = "연결 완료 후 가로 화면 잠금";
"连接提示" = "연결 프롬프트";
"连接方式" = "연결 방법";
"连接远程桌面" = "원격 데스크톱에 연결";
"退格" = "백스페이스";
"通知公告" = "공지사항";
"部分预览图未能清除,请重试。" = "일부 미리보기를 삭제할 수 없습니다. 다시 시도해 주세요.";
"重试" = "다시 시도해 보세요";
"重试保存阅读状态" = "읽은 상태 저장 다시 시도";
"锁定" = "잠금";
"键盘" = "키보드";
"键盘操作" = "키보드 조작";
"阅读并授权" = "읽기 및 승인";
"隐私" = "개인 정보 보호";
"隐私与授权" = "개인 정보 보호 및 승인";
"隐私屏" = "개인정보 보호 화면";
"隐私屏已关闭" = "화면 보호 꺼짐";
"隐私屏已开启" = "화면 보호 켜짐";
"隐私屏已被解锁" = "개인정보 보호 화면 잠금 해제됨";
"隐私屏异常" = "개인정보 화면 이상";
"隐私屏暂不可用" = "개인 정보 보호 화면을 일시적으로 사용할 수 없습니다.";
"隐私屏状态已过期,等待电脑确认" = "개인 정보 보호 화면 상태가 만료되어 컴퓨터 확인을 기다리는 중입니다.";
"隐私屏状态待确认" = "개인정보 보호 화면 상태 확인 예정";
"隐私政策" = "개인정보 보호정책";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "개인정보취급방침은 일시적으로 열람할 수 없습니다. 인터넷 연결을 거부하고 애플리케이션을 다시 설치하십시오.";
"隐藏密码" = "비밀번호 숨기기";
"静音" = "음소거";
"音频" = "오디오";
"项目主页" = "프로젝트 홈페이지";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "미리보기에는 원격 컴퓨터의 개인 정보나 업무 정보가 포함될 수 있습니다. 전원을 켜면 각 연결이 자동으로 기기에 사진을 저장하고 최근 연결에 표시됩니다. 언제든지 닫고 지울 수 있습니다.";
"高" = "높음";
"默认" = "기본값";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "기본적으로 꺼져 있습니다. iPhone을 켜면 연결 시 iPhone이 가로 방향으로 전환되어 유지됩니다. 꺼지면 장치 방향을 따릅니다. iPad는 영향을 받지 않습니다.";
"鼠标右键" = "마우스 오른쪽 버튼";
"鼠标左键" = "마우스 왼쪽 버튼";
"鼠标控制" = "마우스 제어";
"鼠标滚轮" = "마우스 휠";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk usa a rede local para descobrir e estabelecer conexões de controle remoto P2P nativas.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "Falha na transmissão %1$@";
"%1$@ 台设备" = "Dispositivos: %1$@";
"%1$@ 条未读" = "Itens não lidos %1$@";
"%1$@键盘页" = "Página do teclado %1$@";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, recebendo %2$@, enviando %3$@, taxa de perda de pacotes %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk usa a rede local para descobrir e estabelecer conexões de controle remoto P2P nativas.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del requer ativação remota dos serviços do sistema Windows";
"GNU GPL 第 3 版" = "GNU GPL versão 3";
"OpenFEC 告知" = "Aviso OpenFEC";
"OpenFEC 许可与源码" = "Licença e fonte OpenFEC";
"P2P 直连" = "Conexão direta P2P";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Falha no estabelecimento do link, verifique a rede e tente novamente.";
"SRTP 已启用" = "SRT habilitado";
"TLS 证书校验失败" = "Falha na verificação do certificado TLS";
"TURN 中继" = "GIRAR relé";
"上下滑动,可滑出按键范围" = "Deslize para cima e para baixo para sair do intervalo de teclas";
"丢包率" = "Taxa de perda";
"中" = "Média";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Prioriza manter a imagem suave e reduzirá a resolução de forma mais agressiva quando a largura de banda ou a pressão de codificação forem altas.";
"低" = "Baixa";
"使用自定义服务器" = "Usando um servidor personalizado";
"保存密码" = "Salvar senha";
"保存远程画面预览" = "Salvar visualizações de tela remotas";
"保存远程画面预览?" = "Salvar visualização remota da tela?";
"信令服务器已关闭连接" = "O servidor de sinalização fechou a conexão";
"信令服务重连中" = "Reconexão do serviço de sinalização";
"信令连接失败" = "Falha na conexão de sinalização";
"信令连接已关闭" = "Conexão de sinalização fechada";
"偏好模式" = "Modo de preferência";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Deslize o cursor como um trackpad e opere a posição atual do cursor quando clicado.";
"公告加载失败,请重试。" = "Falha ao carregar o anúncio. Tente novamente.";
"公告详情" = "Detalhes do anúncio";
"共 %1$@ 条 · %2$@ 条未读" = "Total de itens %1$@· Itens não lidos %2$@";
"关于" = "Sobre";
"关于 CrossDesk" = "Sobre o CrossDesk";
"关于,有新版本" = "Sobre, há uma nova versão";
"关闭" = "Fechar";
"关闭控制栏" = "Fechar barra de controle";
"关闭隐私屏" = "Desativar tela de privacidade";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Equilibra clareza e suavidade, reduzindo apenas gradualmente a resolução sob pressão constante.";
"分辨率" = "Resolução";
"切换到电脑键盘" = "Mudar para o teclado do computador";
"切换到系统键盘" = "Mudar para o teclado do sistema";
"删除" = "Excluir";
"删除公告" = "Excluir anúncio";
"删除记录" = "Excluir registro";
"删除这条公告?" = "Excluir este anúncio?";
"刷新公告" = "Anúncio de atualização";
"剪贴板" = "Área de transferência";
"剪贴板中没有文本" = "Nenhum texto na área de transferência";
"剪贴板文本超过 128 KiB" = "O texto da área de transferência excede 128 KiB";
"加载中…" = "Carregando…";
"加载更多" = "Carregue mais";
"升级提示" = "Atualização recomendada";
"单次输入最多 16 KiB,且不能包含空字符" = "Uma única entrada pode ter até 16 KiB e não pode conter caracteres nulos";
"发送" = "Enviar";
"发送文件" = "Enviar arquivo";
"取消" = "Cancelar";
"取消连接" = "Cancelar conexão";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Cada componente está sujeito à licença de código aberto listada e os direitos concedidos não estão sujeitos aos termos gerais de uso deste aplicativo. A visualização do código-fonte requer uma conexão com a Internet.";
"合计" = "Total";
"同意并继续" = "Concorde e continue";
"回车" = "Entrar";
"在线" = "On-line";
"地址" = "Endereço";
"填写服务器地址" = "Preencha o endereço do servidor";
"填写端口" = "Preencha a porta";
"声音" = "Som";
"失败" = "Falhou";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Sempre mantenha uma resolução mais alta e reduza a taxa de quadros para garantir a clareza da imagem quando a largura de banda for insuficiente.";
"媒体加密" = "Criptografia de mídia";
"字母数字" = "Letras e números";
"完成" = "Feito";
"完成隐私授权后可检查更新" = "Você pode verificar se há atualizações após concluir a autorização de privacidade";
"完成隐私授权后登记本机身份" = "Registre a identidade local após concluir a autorização de privacidade";
"密码将安全保存在本机 Keychain 中" = "As senhas serão armazenadas com segurança no chaveiro local";
"对端 ID" = "ID remoto";
"对端 ID %1$@" = "ID de par %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Interromperá a conexão remota, fechará o salvamento da visualização da tela e limpará a visualização existente. Os registros de conexão e os arquivos recebidos serão retidos e os dados existentes do servidor não serão excluídos. Você pode reautorizar a qualquer momento.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Todas as visualizações remotas salvas nesta máquina serão excluídas, enquanto os registros de conexão, senhas e arquivos recebidos serão mantidos.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Todas as visualizações remotas salvas nesta máquina serão excluídas, enquanto os registros de conexão, senhas e arquivos recebidos serão mantidos. As conexões subsequentes ainda salvarão novas visualizações.";
"屏幕方向" = "Orientação da tela";
"展开远程控制菜单" = "Expandir menu de controle remoto";
"已保存密码" = "Senha salva";
"已勾选" = "Verificado";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del enviado";
"已发送本机剪贴板" = "Área de transferência local enviada";
"已授权" = "Concedido";
"已接收 %1$@" = "%1$@ recebido";
"已接收远端剪贴板" = "Área de transferência remota recebida";
"已读" = "Leia";
"已连接" = "Conectado";
"已连接服务器" = "Conectado ao servidor";
"帧率" = "Taxa de quadros";
"帧率优先" = "Fluidez";
"平衡" = "Equilibrado";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "O arquivo de licença do aplicativo está ausente ou danificado. Reinstale o aplicativo.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "A versão de desenvolvimento contém modificações que ainda não foram lançadas. O link a seguir é o código fonte da versão básica.";
"开启保存" = "Ativar salvamento";
"开启隐私屏" = "Habilitar tela de privacidade";
"开源组件" = "Componentes de código aberto";
"开源软件权利说明" = "Descrição dos direitos de software de código aberto";
"当前已是最新版本" = "Esta é atualmente a versão mais recente";
"成功连接后,这里会显示远端桌面的缩略图。" = "Após uma conexão bem-sucedida, uma miniatura da área de trabalho remota será exibida aqui.";
"我已阅读并同意《隐私政策》" = "Li e concordo com a Política de Privacidade";
"我已阅读并同意隐私政策" = "Li e concordo com a política de privacidade";
"打开隐私政策" = "Política de privacidade aberta";
"指令发送失败,请重试" = "Falha no envio do comando, tente novamente";
"指令已发送,等待电脑确认…" = "O comando foi enviado, aguardando confirmação do computador...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Pressione e arraste para deslizar para fora do intervalo de teclas, solte para encerrar o arrasto";
"授权后可使用远程连接。" = "Conexões remotas estão disponíveis após autorização.";
"授权状态" = "Status de autorização";
"接收" = "Receber";
"控制模式" = "Modo de controle";
"控制端解码器" = "Decodificador local";
"撤回授权" = "Retirar autorização";
"撤回隐私授权" = "Retirar autorização de privacidade";
"收起电脑键盘" = "Ocultar teclado do PC";
"收起系统键盘" = "Recolher o teclado do sistema";
"收起远程控制菜单" = "Recolher menu de controle remoto";
"收起鼠标" = "Ocultar mouse";
"数据" = "Dados";
"断开后将返回首页" = "Retornará à página inicial após desconectar";
"断开远程连接?" = "Desconectar a conexão remota?";
"断开连接" = "Desconectar";
"新版本" = "Nova versão";
"新版本可用:v%1$@" = "Nova versão disponível: v%1$@";
"无效" = "Inválido";
"无未读公告" = "Nenhum anúncio não lido";
"无法" = "Incapaz de";
"无法保存本地阅读状态,请重试。" = "Não é possível salvar o status de leitura localmente. Por favor, tente novamente.";
"无法删除这条公告,请重试。" = "Não é possível excluir este anúncio. Por favor, tente novamente.";
"无法访问本机登录凭据,请解锁后重试" = "Não é possível acessar as credenciais de login locais, desbloqueie e tente novamente";
"无法读取开源许可" = "Não é possível ler a licença de código aberto";
"无法读取所选文件,请重试" = "Não foi possível ler o arquivo selecionado, tente novamente";
"是否撤回隐私授权?" = "Retirar autorização de privacidade?";
"显示器" = "Tela";
"显示器 %1$@" = "Monitor %1$@";
"显示器 1" = "Monitor 1";
"显示密码" = "Mostrar senha";
"暂不同意" = "Agora não";
"暂无公告" = "Sem anúncios";
"最近连接" = "Conexões recentes";
"服务器" = "Servidor";
"服务器地址" = "Endereço do servidor";
"服务器端口" = "Porta do servidor";
"服务器配置无效" = "Configuração de servidor inválida";
"未勾选" = "Não verificado";
"未启用" = "Não habilitado";
"未授权" = "Não autorizado";
"未知" = "Desconhecido";
"未知信令状态" = "Status de sinalização desconhecido";
"未知连接状态" = "Status de conexão desconhecido";
"未读" = "Não lido";
"未连接" = "Não conectado";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Este aplicativo usa OpenFEC para correção direta de erros noCeCILL-C1.0, que fornece garantias limitadas e limita a responsabilidade. Veja a licença completa para detalhes.";
"本机 ID %1$@" = "ID local %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "As credenciais de login desta máquina são temporariamente inseguras. Desbloqueie seu dispositivo e clique em \"Aplicar\" nas configurações para tentar novamente.";
"本版本源码与构建说明" = "Código-fonte e instruções de construção para esta versão";
"本版本源码信息暂不可用。" = "A revisão fonte não está disponível para esta compilação.";
"构建说明" = "Instruções de construção";
"构建配方与项目补丁" = "Crie receitas e patches de projeto";
"查看源码" = "Ver código-fonte";
"检查失败,请稍后重试" = "Falha na verificação. Por favor, tente novamente mais tarde.";
"检查更新" = "Verifique se há atualizações";
"正在关闭隐私屏…" = "Fechando a tela de privacidade…";
"正在准备连接…" = "Preparando para conectar...";
"正在发送 %1$@" = "Enviando %1$@";
"正在发送 Ctrl+Alt+Del…" = "Enviando Ctrl+Alt+Del…";
"正在取回远程画面…" = "Recuperando tela remota...";
"正在建立远程连接" = "Estabelecendo conexão remota";
"正在开启隐私屏…" = "Ativando a tela de privacidade...";
"正在收集 ICE 候选" = "Coletando candidatos do ICE";
"正在检查更新…" = "Verificando atualizações…";
"正在清除…" = "Limpando...";
"正在确认设备状态…" = "Confirmando o status do dispositivo...";
"正在获取本机 ID…" = "Obtendo ID local...";
"正在连接" = "Conectando";
"正在连接信令服务" = "Conectando-se ao serviço de sinalização";
"清除" = "Limpar";
"清除预览图" = "Limpar prévias";
"清除预览图?" = "Limpar prévias?";
"源码与构建说明" = "Código-fonte e instruções de construção";
"源码版本" = "Versão do código-fonte";
"版本" = "Versão";
"版权与许可声明" = "Declaração de direitos autorais e licença";
"电脑键盘" = "Teclado de computador";
"画中画未能开启,请保持应用在前台后重试。" = "Falha ao abrir o Picture-in-picture. Mantenha o aplicativo em primeiro plano e tente novamente.";
"画质优先" = "Qualidade";
"画面偏好" = "Preferência de vídeo";
"画面延时" = "Latência do vídeo";
"画面设置" = "Configurações de vídeo";
"画面质量" = "Qualidade de vídeo";
"画面采集帧率" = "Taxa de quadros de captura de vídeo";
"相对位置" = "Touchpad";
"相对鼠标" = "Rato relativo";
"知道了" = "Entendi";
"确定" = "OK";
"确认" = "Confirmar";
"离线" = "Off-line";
"空格" = "Espaço";
"端口" = "Porto";
"符号功能" = "Símbolos e teclas";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Aguarde o computador retornar ao estado da tela de privacidade; computadores mais antigos podem não suportá-lo.";
"等待连接" = "Aguardando conexão";
"等待隐私授权" = "Aguardando autorização de privacidade";
"系统键盘" = "Teclado do sistema";
"绝对位置" = "Toque direto";
"绝对鼠标" = "Rato Absoluto";
"网络状态" = "Rede";
"网络连接已中断,请重新连接。" = "A conexão de rede foi interrompida, reconecte.";
"自定义" = "Personalizado";
"虚拟鼠标" = "Rato virtual";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "O terminal controlado não suporta entrada direta de chinês. Atualize o terminal controlado pelo macOS; você também pode copiar o texto primeiro, enviá-lo pela área de transferência do menu do controle remoto e depois colá-lo";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "A tela de privacidade remota foi desbloqueada usando o atalho local. Bloqueie-o novamente para restaurar a privacidade e desativar o desbloqueio de atalhos locais.";
"被控端平台:%1$@" = "Plataforma de terminal controlada:%1$@";
"被控端版本过低,请升级到最新版本。" = "A versão controlada é muito baixa, atualize para a versão mais recente.";
"被控端编码器" = "Codificador remoto";
"视频" = "Vídeo";
"视频 %1$@×%2$@ · %3$@ 帧" = "Vídeo %1$@×%2$@· Quadros %3$@";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "A posição de toque corresponde diretamente à posição da tela remota, que é adequada para posicionamento rápido.";
"许可" = "Licença";
"许可与版权声明" = "Licenças e avisos de direitos autorais";
"许可与版权声明可离线查阅。" = "A licença e o aviso de direitos autorais estão disponíveis offline.";
"设备离线" = "Dispositivo off-line";
"设置" = "Configurações";
"设置,有新版本" = "Configurações, há uma nova versão";
"访问密码" = "Senha de acesso";
"访问密码错误,请重新输入。" = "A senha de acesso está errada, digite-a novamente.";
"请求超时,尚未确认电脑的隐私屏状态" = "A solicitação expirou e o status da tela de privacidade do computador não foi confirmado.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Insira uma porta no intervalo de 1 a 65535 ou desative \"Usar servidor personalizado\".";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Insira o endereço do servidor ou desative \"Usar um servidor personalizado\".";
"请输入远程设备 ID" = "Insira o ID do dispositivo remoto";
"调整失败,请重试。" = "Não foi possível aplicar as configurações. Tente novamente.";
"软件许可" = "Licença de software";
"返回控制栏" = "Retornar à barra de controle";
"还没有连接记录" = "Ainda não há registro de conexão";
"这条公告已更新或删除,请返回公告列表查看。" = "Este anúncio foi atualizado ou excluído. Volte à lista de anúncios para visualizá-lo.";
"远程桌面" = "Área de Trabalho Remota";
"远程设备 ID 不存在,请检查设备 ID。" = "O ID do dispositivo remoto não existe, verifique o ID do dispositivo.";
"远程设备当前不可用,请稍后重试。" = "O dispositivo remoto não está disponível no momento. Tente novamente mais tarde.";
"远程连接已结束。" = "A conexão remota foi encerrada.";
"连接" = "Conectar";
"连接延时(RTT)" = "Atraso de conexão (RTT)";
"连接建立后可调整,需被控端支持。" = "Disponível quando conectado a um dispositivo remoto compatível.";
"连接建立后锁定横屏" = "Bloqueie a tela paisagem após a conexão ser estabelecida";
"连接提示" = "Solicitação de conexão";
"连接方式" = "Método de conexão";
"连接远程桌面" = "Conecte-se à área de trabalho remota";
"退格" = "Retrocesso";
"通知公告" = "Avisos";
"部分预览图未能清除,请重试。" = "Não foi possível limpar algumas visualizações. Tente novamente.";
"重试" = "Tente novamente";
"重试保存阅读状态" = "Tente salvar novamente o status de leitura";
"锁定" = "Bloqueio";
"键盘" = "Teclado";
"键盘操作" = "Operação do teclado";
"阅读并授权" = "Ler e autorizar";
"隐私" = "Privacidade";
"隐私与授权" = "Privacidade e Autorização";
"隐私屏" = "Tela de privacidade";
"隐私屏已关闭" = "Privacidade desativada";
"隐私屏已开启" = "Privacidade ativada";
"隐私屏已被解锁" = "Tela de privacidade desbloqueada";
"隐私屏异常" = "Anormalidade na tela de privacidade";
"隐私屏暂不可用" = "A tela de privacidade está temporariamente indisponível";
"隐私屏状态已过期,等待电脑确认" = "O status da tela de privacidade expirou, aguardando confirmação do computador";
"隐私屏状态待确认" = "Status da tela de privacidade a ser confirmado";
"隐私政策" = "Política de Privacidade";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "A política de privacidade não pode ser lida temporariamente. Recuse-se a se conectar à Internet e reinstale o aplicativo.";
"隐藏密码" = "Ocultar senha";
"静音" = "Mudo";
"音频" = "Áudio";
"项目主页" = "Site do projeto";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "As visualizações podem conter informações pessoais ou de trabalho do computador remoto. Após ligá-lo, cada conexão salvará automaticamente uma imagem na máquina e a exibirá nas conexões recentes. Você pode fechar e limpar a qualquer momento.";
"高" = "Alta";
"默认" = "Padrão";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Desligado por padrão. Quando ligado, o iPhone entra e permanece na orientação paisagem quando conectado; quando desligado, segue a orientação do dispositivo. Os iPads não são afetados.";
"鼠标右键" = "Botão direito do mouse";
"鼠标左键" = "Botão esquerdo do mouse";
"鼠标控制" = "Controle do mouse";
"鼠标滚轮" = "Roda do mouse";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk usa a rede local para descobrir e estabelecer conexões de controle remoto P2P nativas.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "Falha na transmissão %1$@";
"%1$@ 台设备" = "Dispositivos: %1$@";
"%1$@ 条未读" = "Itens não lidos %1$@";
"%1$@键盘页" = "Página do teclado %1$@";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, recebendo %2$@, enviando %3$@, taxa de perda de pacotes %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk usa a rede local para descobrir e estabelecer conexões de controle remoto P2P nativas.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del requer ativação remota dos serviços do sistema Windows";
"GNU GPL 第 3 版" = "GNU GPL versão 3";
"OpenFEC 告知" = "Aviso OpenFEC";
"OpenFEC 许可与源码" = "Licença e fonte OpenFEC";
"P2P 直连" = "Conexão direta P2P";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Falha no estabelecimento do link, verifique a rede e tente novamente.";
"SRTP 已启用" = "SRT habilitado";
"TLS 证书校验失败" = "Falha na verificação do certificado TLS";
"TURN 中继" = "GIRAR relé";
"上下滑动,可滑出按键范围" = "Deslize para cima e para baixo para sair do intervalo de teclas";
"丢包率" = "Taxa de perda";
"中" = "Média";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Prioriza manter a imagem suave e reduzirá a resolução de forma mais agressiva quando a largura de banda ou a pressão de codificação forem altas.";
"低" = "Baixa";
"使用自定义服务器" = "Usando um servidor personalizado";
"保存密码" = "Salvar senha";
"保存远程画面预览" = "Salvar visualizações de tela remotas";
"保存远程画面预览?" = "Salvar visualização remota da tela?";
"信令服务器已关闭连接" = "O servidor de sinalização fechou a conexão";
"信令服务重连中" = "Reconexão do serviço de sinalização";
"信令连接失败" = "Falha na conexão de sinalização";
"信令连接已关闭" = "Conexão de sinalização fechada";
"偏好模式" = "Modo de preferência";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Deslize o cursor como um trackpad e opere a posição atual do cursor quando clicado.";
"公告加载失败,请重试。" = "Falha ao carregar o anúncio. Tente novamente.";
"公告详情" = "Detalhes do anúncio";
"共 %1$@ 条 · %2$@ 条未读" = "Total de itens %1$@· Itens não lidos %2$@";
"关于" = "Sobre";
"关于 CrossDesk" = "Sobre o CrossDesk";
"关于,有新版本" = "Sobre, há uma nova versão";
"关闭" = "Fechar";
"关闭控制栏" = "Fechar barra de controle";
"关闭隐私屏" = "Desativar tela de privacidade";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Equilibra clareza e suavidade, reduzindo apenas gradualmente a resolução sob pressão constante.";
"分辨率" = "Resolução";
"切换到电脑键盘" = "Mudar para o teclado do computador";
"切换到系统键盘" = "Mudar para o teclado do sistema";
"删除" = "Excluir";
"删除公告" = "Excluir anúncio";
"删除记录" = "Excluir registro";
"删除这条公告?" = "Excluir este anúncio?";
"刷新公告" = "Anúncio de atualização";
"剪贴板" = "Área de transferência";
"剪贴板中没有文本" = "Nenhum texto na área de transferência";
"剪贴板文本超过 128 KiB" = "O texto da área de transferência excede 128 KiB";
"加载中…" = "Carregando…";
"加载更多" = "Carregue mais";
"升级提示" = "Atualização recomendada";
"单次输入最多 16 KiB,且不能包含空字符" = "Uma única entrada pode ter até 16 KiB e não pode conter caracteres nulos";
"发送" = "Enviar";
"发送文件" = "Enviar arquivo";
"取消" = "Cancelar";
"取消连接" = "Cancelar conexão";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "Cada componente está sujeito à licença de código aberto listada e os direitos concedidos não estão sujeitos aos termos gerais de uso deste aplicativo. A visualização do código-fonte requer uma conexão com a Internet.";
"合计" = "Total";
"同意并继续" = "Concorde e continue";
"回车" = "Entrar";
"在线" = "On-line";
"地址" = "Endereço";
"填写服务器地址" = "Preencha o endereço do servidor";
"填写端口" = "Preencha a porta";
"声音" = "Som";
"失败" = "Falhou";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Sempre mantenha uma resolução mais alta e reduza a taxa de quadros para garantir a clareza da imagem quando a largura de banda for insuficiente.";
"媒体加密" = "Criptografia de mídia";
"字母数字" = "Letras e números";
"完成" = "Feito";
"完成隐私授权后可检查更新" = "Você pode verificar se há atualizações após concluir a autorização de privacidade";
"完成隐私授权后登记本机身份" = "Registre a identidade local após concluir a autorização de privacidade";
"密码将安全保存在本机 Keychain 中" = "As senhas serão armazenadas com segurança no chaveiro local";
"对端 ID" = "ID remoto";
"对端 ID %1$@" = "ID de par %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Interromperá a conexão remota, fechará o salvamento da visualização da tela e limpará a visualização existente. Os registros de conexão e os arquivos recebidos serão retidos e os dados existentes do servidor não serão excluídos. Você pode reautorizar a qualquer momento.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Todas as visualizações remotas salvas nesta máquina serão excluídas, enquanto os registros de conexão, senhas e arquivos recebidos serão mantidos.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Todas as visualizações remotas salvas nesta máquina serão excluídas, enquanto os registros de conexão, senhas e arquivos recebidos serão mantidos. As conexões subsequentes ainda salvarão novas visualizações.";
"屏幕方向" = "Orientação da tela";
"展开远程控制菜单" = "Expandir menu de controle remoto";
"已保存密码" = "Senha salva";
"已勾选" = "Verificado";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del enviado";
"已发送本机剪贴板" = "Área de transferência local enviada";
"已授权" = "Concedido";
"已接收 %1$@" = "%1$@ recebido";
"已接收远端剪贴板" = "Área de transferência remota recebida";
"已读" = "Leia";
"已连接" = "Conectado";
"已连接服务器" = "Conectado ao servidor";
"帧率" = "Taxa de quadros";
"帧率优先" = "Fluidez";
"平衡" = "Equilibrado";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "O arquivo de licença do aplicativo está ausente ou danificado. Reinstale o aplicativo.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "A versão de desenvolvimento contém modificações que ainda não foram lançadas. O link a seguir é o código fonte da versão básica.";
"开启保存" = "Ativar salvamento";
"开启隐私屏" = "Habilitar tela de privacidade";
"开源组件" = "Componentes de código aberto";
"开源软件权利说明" = "Descrição dos direitos de software de código aberto";
"当前已是最新版本" = "Esta é atualmente a versão mais recente";
"成功连接后,这里会显示远端桌面的缩略图。" = "Após uma conexão bem-sucedida, uma miniatura da área de trabalho remota será exibida aqui.";
"我已阅读并同意《隐私政策》" = "Li e concordo com a Política de Privacidade";
"我已阅读并同意隐私政策" = "Li e concordo com a política de privacidade";
"打开隐私政策" = "Política de privacidade aberta";
"指令发送失败,请重试" = "Falha no envio do comando, tente novamente";
"指令已发送,等待电脑确认…" = "O comando foi enviado, aguardando confirmação do computador...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Pressione e arraste para deslizar para fora do intervalo de teclas, solte para encerrar o arrasto";
"授权后可使用远程连接。" = "Conexões remotas estão disponíveis após autorização.";
"授权状态" = "Status de autorização";
"接收" = "Receber";
"控制模式" = "Modo de controle";
"控制端解码器" = "Decodificador local";
"撤回授权" = "Retirar autorização";
"撤回隐私授权" = "Retirar autorização de privacidade";
"收起电脑键盘" = "Ocultar teclado do PC";
"收起系统键盘" = "Recolher o teclado do sistema";
"收起远程控制菜单" = "Recolher menu de controle remoto";
"收起鼠标" = "Ocultar mouse";
"数据" = "Dados";
"断开后将返回首页" = "Retornará à página inicial após desconectar";
"断开远程连接?" = "Desconectar a conexão remota?";
"断开连接" = "Desconectar";
"新版本" = "Nova versão";
"新版本可用:v%1$@" = "Nova versão disponível: v%1$@";
"无效" = "Inválido";
"无未读公告" = "Nenhum anúncio não lido";
"无法" = "Incapaz de";
"无法保存本地阅读状态,请重试。" = "Não é possível salvar o status de leitura localmente. Por favor, tente novamente.";
"无法删除这条公告,请重试。" = "Não é possível excluir este anúncio. Por favor, tente novamente.";
"无法访问本机登录凭据,请解锁后重试" = "Não é possível acessar as credenciais de login locais, desbloqueie e tente novamente";
"无法读取开源许可" = "Não é possível ler a licença de código aberto";
"无法读取所选文件,请重试" = "Não foi possível ler o arquivo selecionado, tente novamente";
"是否撤回隐私授权?" = "Retirar autorização de privacidade?";
"显示器" = "Tela";
"显示器 %1$@" = "Monitor %1$@";
"显示器 1" = "Monitor 1";
"显示密码" = "Mostrar senha";
"暂不同意" = "Agora não";
"暂无公告" = "Sem anúncios";
"最近连接" = "Conexões recentes";
"服务器" = "Servidor";
"服务器地址" = "Endereço do servidor";
"服务器端口" = "Porta do servidor";
"服务器配置无效" = "Configuração de servidor inválida";
"未勾选" = "Não verificado";
"未启用" = "Não habilitado";
"未授权" = "Não autorizado";
"未知" = "Desconhecido";
"未知信令状态" = "Status de sinalização desconhecido";
"未知连接状态" = "Status de conexão desconhecido";
"未读" = "Não lido";
"未连接" = "Não conectado";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Este aplicativo usa OpenFEC para correção direta de erros noCeCILL-C1.0, que fornece garantias limitadas e limita a responsabilidade. Veja a licença completa para detalhes.";
"本机 ID %1$@" = "ID local %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "As credenciais de login desta máquina são temporariamente inseguras. Desbloqueie seu dispositivo e clique em \"Aplicar\" nas configurações para tentar novamente.";
"本版本源码与构建说明" = "Código-fonte e instruções de construção para esta versão";
"本版本源码信息暂不可用。" = "A revisão fonte não está disponível para esta compilação.";
"构建说明" = "Instruções de construção";
"构建配方与项目补丁" = "Crie receitas e patches de projeto";
"查看源码" = "Ver código-fonte";
"检查失败,请稍后重试" = "Falha na verificação. Por favor, tente novamente mais tarde.";
"检查更新" = "Verifique se há atualizações";
"正在关闭隐私屏…" = "Fechando a tela de privacidade…";
"正在准备连接…" = "Preparando para conectar...";
"正在发送 %1$@" = "Enviando %1$@";
"正在发送 Ctrl+Alt+Del…" = "Enviando Ctrl+Alt+Del…";
"正在取回远程画面…" = "Recuperando tela remota...";
"正在建立远程连接" = "Estabelecendo conexão remota";
"正在开启隐私屏…" = "Ativando a tela de privacidade...";
"正在收集 ICE 候选" = "Coletando candidatos do ICE";
"正在检查更新…" = "Verificando atualizações…";
"正在清除…" = "Limpando...";
"正在确认设备状态…" = "Confirmando o status do dispositivo...";
"正在获取本机 ID…" = "Obtendo ID local...";
"正在连接" = "Conectando";
"正在连接信令服务" = "Conectando-se ao serviço de sinalização";
"清除" = "Limpar";
"清除预览图" = "Limpar prévias";
"清除预览图?" = "Limpar prévias?";
"源码与构建说明" = "Código-fonte e instruções de construção";
"源码版本" = "Versão do código-fonte";
"版本" = "Versão";
"版权与许可声明" = "Declaração de direitos autorais e licença";
"电脑键盘" = "Teclado de computador";
"画中画未能开启,请保持应用在前台后重试。" = "Falha ao abrir o Picture-in-picture. Mantenha o aplicativo em primeiro plano e tente novamente.";
"画质优先" = "Qualidade";
"画面偏好" = "Preferência de vídeo";
"画面延时" = "Latência do vídeo";
"画面设置" = "Configurações de vídeo";
"画面质量" = "Qualidade de vídeo";
"画面采集帧率" = "Taxa de quadros de captura de vídeo";
"相对位置" = "Touchpad";
"相对鼠标" = "Rato relativo";
"知道了" = "Entendi";
"确定" = "OK";
"确认" = "Confirmar";
"离线" = "Off-line";
"空格" = "Espaço";
"端口" = "Porto";
"符号功能" = "Símbolos e teclas";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Aguarde o computador retornar ao estado da tela de privacidade; computadores mais antigos podem não suportá-lo.";
"等待连接" = "Aguardando conexão";
"等待隐私授权" = "Aguardando autorização de privacidade";
"系统键盘" = "Teclado do sistema";
"绝对位置" = "Toque direto";
"绝对鼠标" = "Rato Absoluto";
"网络状态" = "Rede";
"网络连接已中断,请重新连接。" = "A conexão de rede foi interrompida, reconecte.";
"自定义" = "Personalizado";
"虚拟鼠标" = "Rato virtual";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "O terminal controlado não suporta entrada direta de chinês. Atualize o terminal controlado pelo macOS; você também pode copiar o texto primeiro, enviá-lo pela área de transferência do menu do controle remoto e depois colá-lo";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "A tela de privacidade remota foi desbloqueada usando o atalho local. Bloqueie-o novamente para restaurar a privacidade e desativar o desbloqueio de atalhos locais.";
"被控端平台:%1$@" = "Plataforma de terminal controlada:%1$@";
"被控端版本过低,请升级到最新版本。" = "A versão controlada é muito baixa, atualize para a versão mais recente.";
"被控端编码器" = "Codificador remoto";
"视频" = "Vídeo";
"视频 %1$@×%2$@ · %3$@ 帧" = "Vídeo %1$@×%2$@· Quadros %3$@";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "A posição de toque corresponde diretamente à posição da tela remota, que é adequada para posicionamento rápido.";
"许可" = "Licença";
"许可与版权声明" = "Licenças e avisos de direitos autorais";
"许可与版权声明可离线查阅。" = "A licença e o aviso de direitos autorais estão disponíveis offline.";
"设备离线" = "Dispositivo off-line";
"设置" = "Configurações";
"设置,有新版本" = "Configurações, há uma nova versão";
"访问密码" = "Senha de acesso";
"访问密码错误,请重新输入。" = "A senha de acesso está errada, digite-a novamente.";
"请求超时,尚未确认电脑的隐私屏状态" = "A solicitação expirou e o status da tela de privacidade do computador não foi confirmado.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Insira uma porta no intervalo de 1 a 65535 ou desative \"Usar servidor personalizado\".";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Insira o endereço do servidor ou desative \"Usar um servidor personalizado\".";
"请输入远程设备 ID" = "Insira o ID do dispositivo remoto";
"调整失败,请重试。" = "Não foi possível aplicar as configurações. Tente novamente.";
"软件许可" = "Licença de software";
"返回控制栏" = "Retornar à barra de controle";
"还没有连接记录" = "Ainda não há registro de conexão";
"这条公告已更新或删除,请返回公告列表查看。" = "Este anúncio foi atualizado ou excluído. Volte à lista de anúncios para visualizá-lo.";
"远程桌面" = "Área de Trabalho Remota";
"远程设备 ID 不存在,请检查设备 ID。" = "O ID do dispositivo remoto não existe, verifique o ID do dispositivo.";
"远程设备当前不可用,请稍后重试。" = "O dispositivo remoto não está disponível no momento. Tente novamente mais tarde.";
"远程连接已结束。" = "A conexão remota foi encerrada.";
"连接" = "Conectar";
"连接延时(RTT)" = "Atraso de conexão (RTT)";
"连接建立后可调整,需被控端支持。" = "Disponível quando conectado a um dispositivo remoto compatível.";
"连接建立后锁定横屏" = "Bloqueie a tela paisagem após a conexão ser estabelecida";
"连接提示" = "Solicitação de conexão";
"连接方式" = "Método de conexão";
"连接远程桌面" = "Conecte-se à área de trabalho remota";
"退格" = "Retrocesso";
"通知公告" = "Avisos";
"部分预览图未能清除,请重试。" = "Não foi possível limpar algumas visualizações. Tente novamente.";
"重试" = "Tente novamente";
"重试保存阅读状态" = "Tente salvar novamente o status de leitura";
"锁定" = "Bloqueio";
"键盘" = "Teclado";
"键盘操作" = "Operação do teclado";
"阅读并授权" = "Ler e autorizar";
"隐私" = "Privacidade";
"隐私与授权" = "Privacidade e Autorização";
"隐私屏" = "Tela de privacidade";
"隐私屏已关闭" = "Privacidade desativada";
"隐私屏已开启" = "Privacidade ativada";
"隐私屏已被解锁" = "Tela de privacidade desbloqueada";
"隐私屏异常" = "Anormalidade na tela de privacidade";
"隐私屏暂不可用" = "A tela de privacidade está temporariamente indisponível";
"隐私屏状态已过期,等待电脑确认" = "O status da tela de privacidade expirou, aguardando confirmação do computador";
"隐私屏状态待确认" = "Status da tela de privacidade a ser confirmado";
"隐私政策" = "Política de Privacidade";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "A política de privacidade não pode ser lida temporariamente. Recuse-se a se conectar à Internet e reinstale o aplicativo.";
"隐藏密码" = "Ocultar senha";
"静音" = "Mudo";
"音频" = "Áudio";
"项目主页" = "Site do projeto";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "As visualizações podem conter informações pessoais ou de trabalho do computador remoto. Após ligá-lo, cada conexão salvará automaticamente uma imagem na máquina e a exibirá nas conexões recentes. Você pode fechar e limpar a qualquer momento.";
"高" = "Alta";
"默认" = "Padrão";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "Desligado por padrão. Quando ligado, o iPhone entra e permanece na orientação paisagem quando conectado; quando desligado, segue a orientação do dispositivo. Os iPads não são afetados.";
"鼠标右键" = "Botão direito do mouse";
"鼠标左键" = "Botão esquerdo do mouse";
"鼠标控制" = "Controle do mouse";
"鼠标滚轮" = "Roda do mouse";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk использует локальную сеть для обнаружения и установления собственных P2P-соединений удаленного управления.";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "Передача %1$@ не удалась.";
"%1$@ 台设备" = "Устройства: %1$@";
"%1$@ 条未读" = "%1$@ непрочитанные элементы";
"%1$@键盘页" = "Страница клавиатуры %1$@";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@, прием %2$@, отправка %3$@, скорость потери пакетов %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk использует локальную сеть для обнаружения и установления собственных P2P-соединений удаленного управления.";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del требует удаленной активации системных служб Windows.";
"GNU GPL 第 3 版" = "GNU GPL версия 3";
"OpenFEC 告知" = "Уведомление OpenFEC";
"OpenFEC 许可与源码" = "Лицензия и исходный код OpenFEC";
"P2P 直连" = "Прямое соединение P2P";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN Не удалось установить соединение. Проверьте сеть и повторите попытку.";
"SRTP 已启用" = "SRTP включен";
"TLS 证书校验失败" = "Проверка TLS certificate не удалась.";
"TURN 中继" = "Реле ПОВОРОТА";
"上下滑动,可滑出按键范围" = "Сдвиньте вверх и вниз, чтобы выйти за пределы диапазона клавиш.";
"丢包率" = "Потери пакетов";
"中" = "Среднее";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "Отдает приоритет сохранению плавности изображения и более агрессивно снижает разрешение при высокой пропускной способности или нагрузке на кодирование.";
"低" = "Низкое";
"使用自定义服务器" = "Использование пользовательского сервера";
"保存密码" = "Сохранить пароль";
"保存远程画面预览" = "Сохранять превью удалённого экрана";
"保存远程画面预览?" = "Сохранить предварительный просмотр удаленного экрана?";
"信令服务器已关闭连接" = "Сервер сигнализации закрыл соединение.";
"信令服务重连中" = "Повторное подключение услуги сигнализации";
"信令连接失败" = "Не удалось установить сигнальное соединение.";
"信令连接已关闭" = "Сигнальное соединение закрыто";
"偏好模式" = "Режим предпочтений";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "Перемещайте курсор, как трекпад, и управляйте текущим положением курсора при нажатии.";
"公告加载失败,请重试。" = "Не удалось загрузить объявление. Повторите попытку.";
"公告详情" = "Подробности объявления";
"共 %1$@ 条 · %2$@ 条未读" = "Всего элементов %1$@· Непрочитанные элементы %2$@";
"关于" = "О программе";
"关于 CrossDesk" = "О CrossDesk";
"关于,有新版本" = "О, вышла новая версия";
"关闭" = "Закрыть";
"关闭控制栏" = "Закрыть панель управления";
"关闭隐私屏" = "Выключить приватный экран";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "Балансирует четкость и плавность изображения, постепенно снижая разрешение под постоянным давлением.";
"分辨率" = "Разрешение";
"切换到电脑键盘" = "Переключение на клавиатуру компьютера.";
"切换到系统键盘" = "Переключение на системную клавиатуру";
"删除" = "Удалить";
"删除公告" = "Удалить объявление";
"删除记录" = "Удалить запись";
"删除这条公告?" = "Удалить это объявление?";
"刷新公告" = "Обновить объявление";
"剪贴板" = "Буфер обмена";
"剪贴板中没有文本" = "Нет текста в буфере обмена.";
"剪贴板文本超过 128 KiB" = "Размер текста в буфере обмена превышает 128 КиБ.";
"加载中…" = "Загрузка…";
"加载更多" = "Загрузить еще";
"升级提示" = "Рекомендуется обновление";
"单次输入最多 16 KiB,且不能包含空字符" = "Один ввод может иметь размер до 16 КиБ и не может содержать нулевые символы.";
"发送" = "Отправка";
"发送文件" = "Отправить файл";
"取消" = "Отмена";
"取消连接" = "Отмена соединения";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "На каждый компонент распространяется действие указанной лицензии с открытым исходным кодом, а предоставленные права не подпадают под действие общих условий использования этого приложения. Для просмотра исходного кода требуется подключение к Интернету.";
"合计" = "Итого";
"同意并继续" = "Согласитесь и продолжайте.";
"回车" = "Ввести";
"在线" = "В сети";
"地址" = "Адрес";
"填写服务器地址" = "Введите адрес сервера";
"填写端口" = "Заполните порт";
"声音" = "Звук";
"失败" = "Ошибка";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "Всегда поддерживайте более высокое разрешение и уменьшайте частоту кадров, чтобы обеспечить четкость изображения при недостаточной полосе пропускания.";
"媒体加密" = "Шифрование мультимедиа";
"字母数字" = "Буквы и цифры";
"完成" = "Готово";
"完成隐私授权后可检查更新" = "Вы можете проверить наличие обновлений после завершения авторизации конфиденциальности.";
"完成隐私授权后登记本机身份" = "Зарегистрируйте локальную личность после завершения авторизации конфиденциальности.";
"密码将安全保存在本机 Keychain 中" = "Пароли будут безопасно храниться в локальной связке ключей.";
"对端 ID" = "Удаленный ID";
"对端 ID %1$@" = "Идентификатор узла %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "Остановит удаленное соединение, закроет сохранение предварительного просмотра экрана и очистит существующий предварительный просмотр. Записи подключений и полученные файлы сохранятся, а существующие данные сервера не будут удалены. Вы можете повторно авторизоваться в любое время.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "Все удаленные предварительные просмотры, сохраненные на этом компьютере, будут удалены, а записи подключений, пароли и полученные файлы сохранятся.";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "Все удаленные предварительные просмотры, сохраненные на этом компьютере, будут удалены, а записи подключений, пароли и полученные файлы сохранятся. Последующие подключения по-прежнему будут сохранять новые предварительные просмотры.";
"屏幕方向" = "Ориентация экрана";
"展开远程控制菜单" = "Развернуть меню пульта дистанционного управления";
"已保存密码" = "Пароль сохранён";
"已勾选" = "Проверено";
"已发送 Ctrl+Alt+Del" = "Ctrl+Alt+Del отправлено";
"已发送本机剪贴板" = "Локальный буфер отправлен";
"已授权" = "Разрешено";
"已接收 %1$@" = "Получено %1$@";
"已接收远端剪贴板" = "Получен удаленный буфер обмена.";
"已读" = "Прочитано";
"已连接" = "Подключено";
"已连接服务器" = "Подключено к серверу";
"帧率" = "Частота кадров";
"帧率优先" = "Плавность";
"平衡" = "Баланс";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "Файл лицензии в приложении отсутствует или поврежден, переустановите приложение.";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "Версия для разработки содержит модификации, которые еще не были выпущены. По следующей ссылке приведен исходный код базовой версии.";
"开启保存" = "Включить сохранение";
"开启隐私屏" = "Включить приватный экран";
"开源组件" = "Компоненты с открытым кодом";
"开源软件权利说明" = "Описание прав на программное обеспечение с открытым исходным кодом.";
"当前已是最新版本" = "На данный момент это последняя версия.";
"成功连接后,这里会显示远端桌面的缩略图。" = "После успешного подключения здесь отобразится миниатюра удаленного рабочего стола.";
"我已阅读并同意《隐私政策》" = "Я прочитал и согласен с Политикой конфиденциальности.";
"我已阅读并同意隐私政策" = "Я прочитал и согласен с политикой конфиденциальности.";
"打开隐私政策" = "Открытая политика конфиденциальности";
"指令发送失败,请重试" = "Не удалось отправить команду. Повторите попытку.";
"指令已发送,等待电脑确认…" = "Команда отправлена, ждём подтверждения от компьютера...";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "Нажмите и перетащите, чтобы выйти за пределы диапазона клавиш, отпустите, чтобы завершить перетаскивание.";
"授权后可使用远程连接。" = "Удаленные подключения доступны после авторизации.";
"授权状态" = "Статус авторизации";
"接收" = "Приём";
"控制模式" = "Режим управления";
"控制端解码器" = "Локальный декодер";
"撤回授权" = "Отозвать разрешение";
"撤回隐私授权" = "Отозвать разрешение на конфиденциальность";
"收起电脑键盘" = "Скрыть клавиатуру ПК";
"收起系统键盘" = "Свернуть системную клавиатуру";
"收起远程控制菜单" = "Свернуть меню пульта дистанционного управления";
"收起鼠标" = "Скрыть мышь";
"数据" = "Данные";
"断开后将返回首页" = "Вернется на домашнюю страницу после отключения.";
"断开远程连接?" = "Отключить удаленное соединение?";
"断开连接" = "Отключить";
"新版本" = "Новая версия";
"新版本可用:v%1$@" = "Доступна новая версия: v%1$@";
"无效" = "Недействительно";
"无未读公告" = "Нет непрочитанных объявлений";
"无法" = "Невозможно";
"无法保存本地阅读状态,请重试。" = "Не удалось сохранить статус чтения локально. Повторите попытку.";
"无法删除这条公告,请重试。" = "Не удалось удалить объявление. Повторите попытку.";
"无法访问本机登录凭据,请解锁后重试" = "Невозможно получить доступ к локальным учетным данным для входа. Разблокируйте и повторите попытку.";
"无法读取开源许可" = "Невозможно прочитать лицензию с открытым исходным кодом.";
"无法读取所选文件,请重试" = "Невозможно прочитать выбранный файл, попробуйте еще раз.";
"是否撤回隐私授权?" = "Отозвать разрешение на конфиденциальность?";
"显示器" = "Экран";
"显示器 %1$@" = "Монитор %1$@";
"显示器 1" = "Монитор 1";
"显示密码" = "Показать пароль";
"暂不同意" = "Не сейчас";
"暂无公告" = "Нет объявлений";
"最近连接" = "Недавние связи";
"服务器" = "Сервер";
"服务器地址" = "Адрес сервера";
"服务器端口" = "Порт сервера";
"服务器配置无效" = "Неверная конфигурация сервера.";
"未勾选" = "Не отмечено";
"未启用" = "Не включено";
"未授权" = "Несанкционированное";
"未知" = "Неизвестно";
"未知信令状态" = "Неизвестный статус сигнализации";
"未知连接状态" = "Неизвестный статус соединения";
"未读" = "Не прочитано";
"未连接" = "Нет соединения";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "Приложение использует OpenFEC для прямого исправления ошибок по лицензии CeCILL-C 1.0 с ограниченной гарантией и ответственностью. Подробности — в тексте лицензии.";
"本机 ID %1$@" = "Локальный идентификатор %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "Учетные данные для входа на этот компьютер временно небезопасны. Пожалуйста, разблокируйте свое устройство и нажмите «Применить» в настройках, чтобы повторить попытку.";
"本版本源码与构建说明" = "Исходный код и инструкции по сборке этой версии.";
"本版本源码信息暂不可用。" = "Ревизия исходного кода этой сборки недоступна.";
"构建说明" = "Инструкции по сборке";
"构建配方与项目补丁" = "Рецепты сборки и исправления проектов.";
"查看源码" = "Открыть исходный код";
"检查失败,请稍后重试" = "Не удалось проверить обновления. Повторите позже.";
"检查更新" = "Проверить обновления";
"正在关闭隐私屏…" = "Закрытие экрана конфиденциальности…";
"正在准备连接…" = "Подготовка к подключению...";
"正在发送 %1$@" = "Отправка %1$@";
"正在发送 Ctrl+Alt+Del…" = "Отправка Ctrl+Alt+Del…";
"正在取回远程画面…" = "Получение удаленного экрана...";
"正在建立远程连接" = "Установление удаленного соединения";
"正在开启隐私屏…" = "Включение экрана конфиденциальности...";
"正在收集 ICE 候选" = "Сбор кандидатов ICE";
"正在检查更新…" = "Проверка обновлений…";
"正在清除…" = "Очистка...";
"正在确认设备状态…" = "Подтверждение статуса устройства...";
"正在获取本机 ID…" = "Получение локального идентификатора...";
"正在连接" = "Подключение";
"正在连接信令服务" = "Подключение к службе сигнализации";
"清除" = "Очистить";
"清除预览图" = "Удалить превью";
"清除预览图?" = "Удалить превью?";
"源码与构建说明" = "Исходный код и сборка";
"源码版本" = "Версия исходного кода";
"版本" = "Версия";
"版权与许可声明" = "Положение об авторских правах и лицензии.";
"电脑键盘" = "Компьютерная клавиатура";
"画中画未能开启,请保持应用在前台后重试。" = "Не удалось открыть функцию «Картинка в картинке». Оставьте приложение на переднем плане и повторите попытку.";
"画质优先" = "Качество";
"画面偏好" = "Режим видео";
"画面延时" = "Задержка видео";
"画面设置" = "Настройки видео";
"画面质量" = "Качество видео";
"画面采集帧率" = "Частота захвата видео";
"相对位置" = "Тачпад";
"相对鼠标" = "Родственная мышь";
"知道了" = "Понятно";
"确定" = "ОК";
"确认" = "Подтвердить";
"离线" = "Не в сети";
"空格" = "Пробел";
"端口" = "Порт";
"符号功能" = "Символы и клавиши";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "Подождите, пока компьютер вернется в состояние экрана конфиденциальности; старые компьютеры могут его не поддерживать.";
"等待连接" = "Ожидание соединения";
"等待隐私授权" = "Ожидание разрешения на конфиденциальность.";
"系统键盘" = "Системная клавиатура";
"绝对位置" = "Касание";
"绝对鼠标" = "Абсолютная мышь";
"网络状态" = "Сеть";
"网络连接已中断,请重新连接。" = "Сетевое соединение прервано, подключитесь заново.";
"自定义" = "Пользовательский";
"虚拟鼠标" = "Виртуальная мышь";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "Управляемый терминал не поддерживает прямой ввод китайского языка, обновите управляемый терминал macOS; вы также можете сначала скопировать текст, отправить его через буфер обмена меню пульта дистанционного управления, а затем вставить";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "Приватный экран отключён локальным сочетанием клавиш. Заблокируйте его снова, чтобы восстановить защиту и запретить локальную разблокировку.";
"被控端平台:%1$@" = "Управляемая терминальная платформа:%1$@.";
"被控端版本过低,请升级到最新版本。" = "Управляемая версия слишком низкая, обновите ее до последней версии.";
"被控端编码器" = "Кодер удалённой стороны";
"视频" = "Видео";
"视频 %1$@×%2$@ · %3$@ 帧" = "Видео %1$@×%2$@·%3$@ кадры";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "Положение касания напрямую соответствует положению удаленного экрана, что подходит для быстрого позиционирования.";
"许可" = "Лицензия";
"许可与版权声明" = "Лицензии и авторские права";
"许可与版权声明可离线查阅。" = "Лицензия и уведомление об авторских правах доступны в автономном режиме.";
"设备离线" = "Устройство не в сети";
"设置" = "Настройки";
"设置,有新版本" = "Настройки, есть новая версия";
"访问密码" = "Пароль доступа";
"访问密码错误,请重新输入。" = "Неверный пароль доступа, пожалуйста, введите его еще раз.";
"请求超时,尚未确认电脑的隐私屏状态" = "Время ожидания запроса истекло, а состояние экрана конфиденциальности компьютера не подтверждено.";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "Введите порт в диапазоне 1–65535 или отключите параметр «Использовать собственный сервер».";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "Введите адрес сервера или отключите параметр «Использовать собственный сервер».";
"请输入远程设备 ID" = "Введите идентификатор удаленного устройства.";
"调整失败,请重试。" = "Не удалось применить настройки. Повторите попытку.";
"软件许可" = "Лицензия программы";
"返回控制栏" = "Возврат к панели управления.";
"还没有连接记录" = "Пока нет записей о подключении";
"这条公告已更新或删除,请返回公告列表查看。" = "Это объявление было обновлено или удалено. Чтобы просмотреть его, вернитесь к списку объявлений.";
"远程桌面" = "Удаленный рабочий стол";
"远程设备 ID 不存在,请检查设备 ID。" = "Идентификатор удаленного устройства не существует, проверьте идентификатор устройства.";
"远程设备当前不可用,请稍后重试。" = "Удаленное устройство в настоящее время недоступно. Повторите попытку позже.";
"远程连接已结束。" = "Удаленное соединение прервано.";
"连接" = "Подключиться";
"连接延时(RTT)" = "Задержка соединения (RTT)";
"连接建立后可调整,需被控端支持。" = "Доступно при подключении к поддерживаемому устройству.";
"连接建立后锁定横屏" = "Блокировка горизонтального экрана после установления соединения.";
"连接提示" = "Запрос на подключение";
"连接方式" = "Способ подключения";
"连接远程桌面" = "Подключение к удаленному рабочему столу";
"退格" = "Возврат";
"通知公告" = "Объявления";
"部分预览图未能清除,请重试。" = "Некоторые предварительные просмотры не удалось удалить. Повторите попытку.";
"重试" = "Попробуйте еще раз";
"重试保存阅读状态" = "Повторить попытку сохранения статуса чтения.";
"锁定" = "Заблокировать";
"键盘" = "Клавиатура";
"键盘操作" = "Работа с клавиатурой";
"阅读并授权" = "Прочитать и авторизоваться";
"隐私" = "Конфиденциальность";
"隐私与授权" = "Конфиденциальность и авторизация";
"隐私屏" = "Приватный экран";
"隐私屏已关闭" = "Приватный экран выкл.";
"隐私屏已开启" = "Приватный экран вкл.";
"隐私屏已被解锁" = "Приватный экран разблокирован";
"隐私屏异常" = "Неисправность экрана конфиденциальности.";
"隐私屏暂不可用" = "Экран конфиденциальности временно недоступен.";
"隐私屏状态已过期,等待电脑确认" = "Срок действия статуса экрана конфиденциальности истек, ожидание подтверждения от компьютера.";
"隐私屏状态待确认" = "Статус экрана конфиденциальности подлежит подтверждению.";
"隐私政策" = "Политика конфиденциальности";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "Политику конфиденциальности временно невозможно прочитать. Пожалуйста, откажитесь от подключения к Интернету и переустановите приложение.";
"隐藏密码" = "Скрыть пароль";
"静音" = "Без звука";
"音频" = "Аудио";
"项目主页" = "Сайт проекта";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "Предварительный просмотр может содержать личную или рабочую информацию с удаленного компьютера. После его включения каждое подключение будет автоматически сохранять изображение на устройстве и отображать его при последних подключениях. Вы можете закрыть и очистить в любое время.";
"高" = "Высокое";
"默认" = "По умолчанию";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "По умолчанию выключено. При включении iPhone переходит в альбомную ориентацию и остается при подключении; в выключенном состоянии он следует ориентации устройства. iPad не затрагивается.";
"鼠标右键" = "Правая кнопка мыши";
"鼠标左键" = "Левая кнопка мыши";
"鼠标控制" = "Управление мышью";
"鼠标滚轮" = "Колесо мыши";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。";
@@ -0,0 +1,283 @@
// Generated by localization/generate.py.
"%1$@ 传输失败" = "%1$@ 传输失败";
"%1$@ 台设备" = "%1$@ 台设备";
"%1$@ 条未读" = "%1$@ 条未读";
"%1$@键盘页" = "%1$@键盘页";
"%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@" = "%1$@,接收 %2$@,发送 %3$@,丢包率 %4$@";
"CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。" = "CrossDesk 使用局域网发现并建立原生 P2P 远程控制连接。";
"Ctrl+Alt+Del 需要远端启用 Windows 系统服务" = "Ctrl+Alt+Del 需要远端启用 Windows 系统服务";
"GNU GPL 第 3 版" = "GNU GPL 第 3 版";
"OpenFEC 告知" = "OpenFEC 告知";
"OpenFEC 许可与源码" = "OpenFEC 许可与源码";
"P2P 直连" = "P2P 直连";
"P2P/TURN 建链失败,请检查网络后重试。" = "P2P/TURN 建链失败,请检查网络后重试。";
"SRTP 已启用" = "SRTP 已启用";
"TLS 证书校验失败" = "TLS 证书校验失败";
"TURN 中继" = "TURN 中继";
"上下滑动,可滑出按键范围" = "上下滑动,可滑出按键范围";
"丢包率" = "丢包率";
"中" = "中";
"优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。" = "优先保持画面流畅,带宽或编码压力较大时会更积极地降低分辨率。";
"低" = "低";
"使用自定义服务器" = "使用自定义服务器";
"保存密码" = "保存密码";
"保存远程画面预览" = "保存远程画面预览";
"保存远程画面预览?" = "保存远程画面预览?";
"信令服务器已关闭连接" = "信令服务器已关闭连接";
"信令服务重连中" = "信令服务重连中";
"信令连接失败" = "信令连接失败";
"信令连接已关闭" = "信令连接已关闭";
"偏好模式" = "偏好模式";
"像触控板一样滑动光标,点击时操作当前光标位置。" = "像触控板一样滑动光标,点击时操作当前光标位置。";
"公告加载失败,请重试。" = "公告加载失败,请重试。";
"公告详情" = "公告详情";
"共 %1$@ 条 · %2$@ 条未读" = "共 %1$@ 条 · %2$@ 条未读";
"关于" = "关于";
"关于 CrossDesk" = "关于 CrossDesk";
"关于,有新版本" = "关于,有新版本";
"关闭" = "关闭";
"关闭控制栏" = "关闭控制栏";
"关闭隐私屏" = "关闭隐私屏";
"兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。" = "兼顾清晰度和流畅度,只在持续压力下逐步降低分辨率。";
"分辨率" = "分辨率";
"切换到电脑键盘" = "切换到电脑键盘";
"切换到系统键盘" = "切换到系统键盘";
"删除" = "删除";
"删除公告" = "删除公告";
"删除记录" = "删除记录";
"删除这条公告?" = "删除这条公告?";
"刷新公告" = "刷新公告";
"剪贴板" = "剪贴板";
"剪贴板中没有文本" = "剪贴板中没有文本";
"剪贴板文本超过 128 KiB" = "剪贴板文本超过 128 KiB";
"加载中…" = "加载中…";
"加载更多" = "加载更多";
"升级提示" = "升级提示";
"单次输入最多 16 KiB,且不能包含空字符" = "单次输入最多 16 KiB,且不能包含空字符";
"发送" = "发送";
"发送文件" = "发送文件";
"取消" = "取消";
"取消连接" = "取消连接";
"各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。" = "各组件遵循所列开源许可证,其授予的权利不受本应用普通使用条款限制。查看源码需要网络连接。";
"合计" = "合计";
"同意并继续" = "同意并继续";
"回车" = "回车";
"在线" = "在线";
"地址" = "地址";
"填写服务器地址" = "填写服务器地址";
"填写端口" = "填写端口";
"声音" = "声音";
"失败" = "失败";
"始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。" = "始终保持较高分辨率,带宽不足时通过降低帧率来保证画面清晰度。";
"媒体加密" = "媒体加密";
"字母数字" = "字母数字";
"完成" = "完成";
"完成隐私授权后可检查更新" = "完成隐私授权后可检查更新";
"完成隐私授权后登记本机身份" = "完成隐私授权后登记本机身份";
"密码将安全保存在本机 Keychain 中" = "密码将安全保存在本机 Keychain 中";
"对端 ID" = "对端 ID";
"对端 ID %1$@" = "对端 ID %1$@";
"将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。" = "将停止远程连接、关闭画面预览保存并清除已有预览。连接记录和接收文件会保留,已有服务端数据不会删除。您可随时重新授权。";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。" = "将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。";
"将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。" = "将删除本机保存的所有远程预览图,保留连接记录、密码和接收的文件。后续连接仍会保存新的预览图。";
"屏幕方向" = "屏幕方向";
"展开远程控制菜单" = "展开远程控制菜单";
"已保存密码" = "已保存密码";
"已勾选" = "已勾选";
"已发送 Ctrl+Alt+Del" = "已发送 Ctrl+Alt+Del";
"已发送本机剪贴板" = "已发送本机剪贴板";
"已授权" = "已授权";
"已接收 %1$@" = "已接收 %1$@";
"已接收远端剪贴板" = "已接收远端剪贴板";
"已读" = "已读";
"已连接" = "已连接";
"已连接服务器" = "已连接服务器";
"帧率" = "帧率";
"帧率优先" = "帧率优先";
"平衡" = "平衡";
"应用中的许可文件缺失或损坏,请重新安装应用。" = "应用中的许可文件缺失或损坏,请重新安装应用。";
"开发版本包含尚未发布的修改,以下链接为基础版本源码。" = "开发版本包含尚未发布的修改,以下链接为基础版本源码。";
"开启保存" = "开启保存";
"开启隐私屏" = "开启隐私屏";
"开源组件" = "开源组件";
"开源软件权利说明" = "开源软件权利说明";
"当前已是最新版本" = "当前已是最新版本";
"成功连接后,这里会显示远端桌面的缩略图。" = "成功连接后,这里会显示远端桌面的缩略图。";
"我已阅读并同意《隐私政策》" = "我已阅读并同意《隐私政策》";
"我已阅读并同意隐私政策" = "我已阅读并同意隐私政策";
"打开隐私政策" = "打开隐私政策";
"指令发送失败,请重试" = "指令发送失败,请重试";
"指令已发送,等待电脑确认…" = "指令已发送,等待电脑确认…";
"按住并拖动,可滑出按键范围,松开结束拖拽" = "按住并拖动,可滑出按键范围,松开结束拖拽";
"授权后可使用远程连接。" = "授权后可使用远程连接。";
"授权状态" = "授权状态";
"接收" = "接收";
"控制模式" = "控制模式";
"控制端解码器" = "控制端解码器";
"撤回授权" = "撤回授权";
"撤回隐私授权" = "撤回隐私授权";
"收起电脑键盘" = "收起电脑键盘";
"收起系统键盘" = "收起系统键盘";
"收起远程控制菜单" = "收起远程控制菜单";
"收起鼠标" = "收起鼠标";
"数据" = "数据";
"断开后将返回首页" = "断开后将返回首页";
"断开远程连接?" = "断开远程连接?";
"断开连接" = "断开连接";
"新版本" = "新版本";
"新版本可用:v%1$@" = "新版本可用:v%1$@";
"无效" = "无效";
"无未读公告" = "无未读公告";
"无法" = "无法";
"无法保存本地阅读状态,请重试。" = "无法保存本地阅读状态,请重试。";
"无法删除这条公告,请重试。" = "无法删除这条公告,请重试。";
"无法访问本机登录凭据,请解锁后重试" = "无法访问本机登录凭据,请解锁后重试";
"无法读取开源许可" = "无法读取开源许可";
"无法读取所选文件,请重试" = "无法读取所选文件,请重试";
"是否撤回隐私授权?" = "是否撤回隐私授权?";
"显示器" = "显示器";
"显示器 %1$@" = "显示器 %1$@";
"显示器 1" = "显示器 1";
"显示密码" = "显示密码";
"暂不同意" = "暂不同意";
"暂无公告" = "暂无公告";
"最近连接" = "最近连接";
"服务器" = "服务器";
"服务器地址" = "服务器地址";
"服务器端口" = "服务器端口";
"服务器配置无效" = "服务器配置无效";
"未勾选" = "未勾选";
"未启用" = "未启用";
"未授权" = "未授权";
"未知" = "未知";
"未知信令状态" = "未知信令状态";
"未知连接状态" = "未知连接状态";
"未读" = "未读";
"未连接" = "未连接";
"本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。" = "本应用使用 OpenFEC 提供前向纠错功能,遵循 CeCILL-C 1.0 许可。该许可规定了有限保证及责任限制,详情见许可全文。";
"本机 ID %1$@" = "本机 ID %1$@";
"本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。" = "本机登录凭据暂时无法安全存取。请解锁设备后,在设置中点击“应用”重试。";
"本版本源码与构建说明" = "本版本源码与构建说明";
"本版本源码信息暂不可用。" = "本版本源码信息暂不可用。";
"构建说明" = "构建说明";
"构建配方与项目补丁" = "构建配方与项目补丁";
"查看源码" = "查看源码";
"检查失败,请稍后重试" = "检查失败,请稍后重试";
"检查更新" = "检查更新";
"正在关闭隐私屏…" = "正在关闭隐私屏…";
"正在准备连接…" = "正在准备连接…";
"正在发送 %1$@" = "正在发送 %1$@";
"正在发送 Ctrl+Alt+Del…" = "正在发送 Ctrl+Alt+Del…";
"正在取回远程画面…" = "正在取回远程画面…";
"正在建立远程连接" = "正在建立远程连接";
"正在开启隐私屏…" = "正在开启隐私屏…";
"正在收集 ICE 候选" = "正在收集 ICE 候选";
"正在检查更新…" = "正在检查更新…";
"正在清除…" = "正在清除…";
"正在确认设备状态…" = "正在确认设备状态…";
"正在获取本机 ID…" = "正在获取本机 ID…";
"正在连接" = "正在连接";
"正在连接信令服务" = "正在连接信令服务";
"清除" = "清除";
"清除预览图" = "清除预览图";
"清除预览图?" = "清除预览图?";
"源码与构建说明" = "源码与构建说明";
"源码版本" = "源码版本";
"版本" = "版本";
"版权与许可声明" = "版权与许可声明";
"电脑键盘" = "电脑键盘";
"画中画未能开启,请保持应用在前台后重试。" = "画中画未能开启,请保持应用在前台后重试。";
"画质优先" = "画质优先";
"画面偏好" = "画面偏好";
"画面延时" = "画面延时";
"画面设置" = "画面设置";
"画面质量" = "画面质量";
"画面采集帧率" = "画面采集帧率";
"相对位置" = "相对位置";
"相对鼠标" = "相对鼠标";
"知道了" = "知道了";
"确定" = "确定";
"确认" = "确认";
"离线" = "离线";
"空格" = "空格";
"端口" = "端口";
"符号功能" = "符号功能";
"等待电脑返回隐私屏状态;旧版电脑端可能不支持" = "等待电脑返回隐私屏状态;旧版电脑端可能不支持";
"等待连接" = "等待连接";
"等待隐私授权" = "等待隐私授权";
"系统键盘" = "系统键盘";
"绝对位置" = "绝对位置";
"绝对鼠标" = "绝对鼠标";
"网络状态" = "网络状态";
"网络连接已中断,请重新连接。" = "网络连接已中断,请重新连接。";
"自定义" = "自定义";
"虚拟鼠标" = "虚拟鼠标";
"被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴" = "被控端不支持直接输入中文,请更新 macOS 被控端;也可先复制文字,通过远控菜单的剪贴板发送后粘贴";
"被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。" = "被控端已通过组合键解除隐私屏。点击“锁定”将重新开启隐私屏,并禁止本机使用组合键解锁。";
"被控端平台:%1$@" = "被控端平台:%1$@";
"被控端版本过低,请升级到最新版本。" = "被控端版本过低,请升级到最新版本。";
"被控端编码器" = "被控端编码器";
"视频" = "视频";
"视频 %1$@×%2$@ · %3$@ 帧" = "视频 %1$@×%2$@ · %3$@ 帧";
"触摸位置直接对应远端屏幕位置,适合快速定位。" = "触摸位置直接对应远端屏幕位置,适合快速定位。";
"许可" = "许可";
"许可与版权声明" = "许可与版权声明";
"许可与版权声明可离线查阅。" = "许可与版权声明可离线查阅。";
"设备离线" = "设备离线";
"设置" = "设置";
"设置,有新版本" = "设置,有新版本";
"访问密码" = "访问密码";
"访问密码错误,请重新输入。" = "访问密码错误,请重新输入。";
"请求超时,尚未确认电脑的隐私屏状态" = "请求超时,尚未确认电脑的隐私屏状态";
"请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。" = "请输入 1–65535 范围内的端口,或关闭“使用自定义服务器”。";
"请输入服务器地址,或关闭“使用自定义服务器”。" = "请输入服务器地址,或关闭“使用自定义服务器”。";
"请输入远程设备 ID" = "请输入远程设备 ID";
"调整失败,请重试。" = "调整失败,请重试。";
"软件许可" = "软件许可";
"返回控制栏" = "返回控制栏";
"还没有连接记录" = "还没有连接记录";
"这条公告已更新或删除,请返回公告列表查看。" = "这条公告已更新或删除,请返回公告列表查看。";
"远程桌面" = "远程桌面";
"远程设备 ID 不存在,请检查设备 ID。" = "远程设备 ID 不存在,请检查设备 ID。";
"远程设备当前不可用,请稍后重试。" = "远程设备当前不可用,请稍后重试。";
"远程连接已结束。" = "远程连接已结束。";
"连接" = "连接";
"连接延时(RTT)" = "连接延时(RTT)";
"连接建立后可调整,需被控端支持。" = "连接建立后可调整,需被控端支持。";
"连接建立后锁定横屏" = "连接建立后锁定横屏";
"连接提示" = "连接提示";
"连接方式" = "连接方式";
"连接远程桌面" = "连接远程桌面";
"退格" = "退格";
"通知公告" = "通知公告";
"部分预览图未能清除,请重试。" = "部分预览图未能清除,请重试。";
"重试" = "重试";
"重试保存阅读状态" = "重试保存阅读状态";
"锁定" = "锁定";
"键盘" = "键盘";
"键盘操作" = "键盘操作";
"阅读并授权" = "阅读并授权";
"隐私" = "隐私";
"隐私与授权" = "隐私与授权";
"隐私屏" = "隐私屏";
"隐私屏已关闭" = "隐私屏已关闭";
"隐私屏已开启" = "隐私屏已开启";
"隐私屏已被解锁" = "隐私屏已被解锁";
"隐私屏异常" = "隐私屏异常";
"隐私屏暂不可用" = "隐私屏暂不可用";
"隐私屏状态已过期,等待电脑确认" = "隐私屏状态已过期,等待电脑确认";
"隐私屏状态待确认" = "隐私屏状态待确认";
"隐私政策" = "隐私政策";
"隐私政策暂时无法读取,请拒绝联网并重新安装应用。" = "隐私政策暂时无法读取,请拒绝联网并重新安装应用。";
"隐藏密码" = "隐藏密码";
"静音" = "静音";
"音频" = "音频";
"项目主页" = "项目主页";
"预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。" = "预览可能包含远程电脑上的个人或工作信息。开启后,每次连接会自动将一张画面保存在本机并显示在最近连接中。你可以随时关闭并清除。";
"高" = "高";
"默认" = "默认";
"默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。" = "默认关闭。开启后,iPhone 连接时进入并保持横屏;关闭时跟随设备方向。iPad 不受影响。";
"鼠标右键" = "鼠标右键";
"鼠标左键" = "鼠标左键";
"鼠标控制" = "鼠标控制";
"鼠标滚轮" = "鼠标滚轮";
@@ -0,0 +1 @@
"NSLocalNetworkUsageDescription" = "CrossDesk 使用區域網發現並建立原生 P2P 遠端控制連線。";

Some files were not shown because too many files have changed in this diff Show More