feat: full Chinese (zh-Hans) localization — i18n all hardcoded strings, zh-CN download page
This commit is contained in:
@@ -732,7 +732,7 @@ export default function SessionsScreen() {
|
|||||||
</Text>
|
</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.modalInput, isDark && styles.modalInputDark]}
|
style={[styles.modalInput, isDark && styles.modalInputDark]}
|
||||||
placeholder={serverHome ? `${serverHome}/...` : "/path/to/project"}
|
placeholder={serverHome ? `${serverHome}/...` : t("connection.add.quick.directoryPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={customDir}
|
value={customDir}
|
||||||
onChangeText={(text) => {
|
onChangeText={(text) => {
|
||||||
|
|||||||
@@ -233,7 +233,7 @@ export default function EditConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.serverUrl")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.serverUrl")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="http://192.168.1.100:4096"
|
placeholder={t("connection.edit.urlPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={url}
|
value={url}
|
||||||
onChangeText={setUrl}
|
onChangeText={setUrl}
|
||||||
@@ -246,7 +246,7 @@ export default function EditConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.directoryOptional")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.directoryOptional")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="/path/to/project"
|
placeholder={t("connection.add.quick.directoryPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={directory}
|
value={directory}
|
||||||
onChangeText={setDirectory}
|
onChangeText={setDirectory}
|
||||||
@@ -261,7 +261,7 @@ export default function EditConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.username")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.username")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="admin"
|
placeholder={t("connection.add.quick.usernamePlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={username}
|
value={username}
|
||||||
onChangeText={setUsername}
|
onChangeText={setUsername}
|
||||||
|
|||||||
@@ -331,7 +331,7 @@ export default function AddConnectionScreen() {
|
|||||||
<View style={styles.ipRow}>
|
<View style={styles.ipRow}>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, styles.ipInput, isDark && styles.inputDark]}
|
style={[styles.input, styles.ipInput, isDark && styles.inputDark]}
|
||||||
placeholder="192.168.1.100"
|
placeholder={t("connection.add.quick.ipPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={ip}
|
value={ip}
|
||||||
onChangeText={setIp}
|
onChangeText={setIp}
|
||||||
@@ -343,7 +343,7 @@ export default function AddConnectionScreen() {
|
|||||||
<Text style={[styles.ipColon, isDark && styles.textDark]}>:</Text>
|
<Text style={[styles.ipColon, isDark && styles.textDark]}>:</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, styles.portInput, isDark && styles.inputDark]}
|
style={[styles.input, styles.portInput, isDark && styles.inputDark]}
|
||||||
placeholder="4096"
|
placeholder={t("connection.add.quick.portPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={port}
|
value={port}
|
||||||
onChangeText={setPort}
|
onChangeText={setPort}
|
||||||
@@ -479,7 +479,7 @@ export default function AddConnectionScreen() {
|
|||||||
<>
|
<>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark, { marginTop: 12 }]}
|
style={[styles.input, isDark && styles.inputDark, { marginTop: 12 }]}
|
||||||
placeholder="your@email.com"
|
placeholder={t("connection.add.quick.emailPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={waitlistEmail}
|
value={waitlistEmail}
|
||||||
onChangeText={setWaitlistEmail}
|
onChangeText={setWaitlistEmail}
|
||||||
@@ -610,7 +610,7 @@ export default function AddConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.directoryOptional")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.directoryOptional")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="/path/to/project"
|
placeholder={t("connection.add.quick.directoryPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={directory}
|
value={directory}
|
||||||
onChangeText={setDirectory}
|
onChangeText={setDirectory}
|
||||||
@@ -625,7 +625,7 @@ export default function AddConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.username")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.username")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="admin"
|
placeholder={t("connection.add.quick.usernamePlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={username}
|
value={username}
|
||||||
onChangeText={setUsername}
|
onChangeText={setUsername}
|
||||||
@@ -636,7 +636,7 @@ export default function AddConnectionScreen() {
|
|||||||
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.password")}</Text>
|
<Text style={[styles.label, isDark && styles.labelDark]}>{t("connection.shared.password")}</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
style={[styles.input, isDark && styles.inputDark]}
|
style={[styles.input, isDark && styles.inputDark]}
|
||||||
placeholder="password"
|
placeholder={t("connection.add.quick.passwordPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={password}
|
value={password}
|
||||||
onChangeText={setPassword}
|
onChangeText={setPassword}
|
||||||
|
|||||||
@@ -748,7 +748,7 @@ export default function SessionScreen() {
|
|||||||
onLongPress={() => cycleAgent(-1)}
|
onLongPress={() => cycleAgent(-1)}
|
||||||
>
|
>
|
||||||
<View style={[s.agentDot, { backgroundColor: agentColor }]} />
|
<View style={[s.agentDot, { backgroundColor: agentColor }]} />
|
||||||
<Text style={[s.agentLabel, isDark && s.textWhite]}>{agent || "build"}</Text>
|
<Text style={[s.agentLabel, isDark && s.textWhite]}>{agent || t("session.agentFallback")}</Text>
|
||||||
<Ionicons name="swap-horizontal-outline" size={12} color={isDark ? "#888888" : "#666666"} />
|
<Ionicons name="swap-horizontal-outline" size={12} color={isDark ? "#888888" : "#666666"} />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/mobile",
|
"name": "@opencode-ai/mobile",
|
||||||
"version": "0.4.11",
|
"version": "0.4.15",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@opencode-ai/mobile",
|
"name": "@opencode-ai/mobile",
|
||||||
"version": "0.4.11",
|
"version": "0.4.15",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@expo/vector-icons": "^15.0.3",
|
"@expo/vector-icons": "^15.0.3",
|
||||||
"@gorhom/bottom-sheet": "5.2.8",
|
"@gorhom/bottom-sheet": "5.2.8",
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export function DirectorySwitcher({ sheetRef, current, recents, serverHome, isDa
|
|||||||
<View style={s.inputWrap}>
|
<View style={s.inputWrap}>
|
||||||
<BottomSheetTextInput
|
<BottomSheetTextInput
|
||||||
style={[s.input, isDark && s.inputDark]}
|
style={[s.input, isDark && s.inputDark]}
|
||||||
placeholder={serverHome ? `${serverHome}/...` : "/path/to/project"}
|
placeholder={serverHome ? `${serverHome}/...` : t("chat.directorySwitcher.pathPlaceholder")}
|
||||||
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
placeholderTextColor={isDark ? "#666666" : "#999999"}
|
||||||
value={custom}
|
value={custom}
|
||||||
onChangeText={(text) => {
|
onChangeText={(text) => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Ionicons } from "@expo/vector-icons"
|
|||||||
import { Markdown } from "../markdown"
|
import { Markdown } from "../markdown"
|
||||||
import { ToolCallCard } from "./ToolCallCard"
|
import { ToolCallCard } from "./ToolCallCard"
|
||||||
import { ReasoningBlock } from "./ReasoningBlock"
|
import { ReasoningBlock } from "./ReasoningBlock"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import type { Message, Part } from "../../lib/sdk"
|
import type { Message, Part } from "../../lib/sdk"
|
||||||
|
|
||||||
const SCREEN_WIDTH = Dimensions.get("window").width
|
const SCREEN_WIDTH = Dimensions.get("window").width
|
||||||
@@ -26,6 +27,7 @@ interface Props {
|
|||||||
// built-in block-level memoization that eliminates re-renders for stable blocks
|
// built-in block-level memoization that eliminates re-renders for stable blocks
|
||||||
export const MessageBubble = memo(
|
export const MessageBubble = memo(
|
||||||
function MessageBubble({ message, parts, isDark, onLongPress }: Props) {
|
function MessageBubble({ message, parts, isDark, onLongPress }: Props) {
|
||||||
|
const { t } = useTranslation()
|
||||||
const isUser = message.role === "user"
|
const isUser = message.role === "user"
|
||||||
|
|
||||||
const textParts = parts.filter((p) => p.type === "text")
|
const textParts = parts.filter((p) => p.type === "text")
|
||||||
@@ -55,7 +57,7 @@ export const MessageBubble = memo(
|
|||||||
size={14}
|
size={14}
|
||||||
color={isUser ? (isDark ? "#ffffff" : "#0a0a0a") : "#8b5cf6"}
|
color={isUser ? (isDark ? "#ffffff" : "#0a0a0a") : "#8b5cf6"}
|
||||||
/>
|
/>
|
||||||
<Text style={[s.role, isUser && s.roleUser, isDark && s.textWhite]}>{isUser ? "You" : "Assistant"}</Text>
|
<Text style={[s.role, isUser && s.roleUser, isDark && s.textWhite]}>{t("chat.messageBubble.roleYou")}</Text>
|
||||||
{message.model && <Text style={[s.modelTag, isDark && s.modelTagDark]}>{message.model.modelID}</Text>}
|
{message.model && <Text style={[s.modelTag, isDark && s.modelTagDark]}>{message.model.modelID}</Text>}
|
||||||
{!isUser && message.modelID && <Text style={[s.modelTag, isDark && s.modelTagDark]}>{message.modelID}</Text>}
|
{!isUser && message.modelID && <Text style={[s.modelTag, isDark && s.modelTagDark]}>{message.modelID}</Text>}
|
||||||
</View>
|
</View>
|
||||||
@@ -104,8 +106,8 @@ export const MessageBubble = memo(
|
|||||||
{/* Tokens/cost for assistant messages */}
|
{/* Tokens/cost for assistant messages */}
|
||||||
{!isUser && message.tokens && (
|
{!isUser && message.tokens && (
|
||||||
<Text style={[s.tokens, isDark && s.tokensDark]}>
|
<Text style={[s.tokens, isDark && s.tokensDark]}>
|
||||||
{message.tokens.input + message.tokens.output} tokens
|
{message.tokens.input + message.tokens.output}{t("chat.messageBubble.tokensSuffix")}
|
||||||
{message.cost ? ` · $${message.cost.toFixed(4)}` : ""}
|
{message.cost ? ` · ${t("chat.messageBubble.costPrefix")}${message.cost.toFixed(4)}` : ""}
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Platform, ScrollView } from "react-native"
|
import { View, Text, TouchableOpacity, StyleSheet, useColorScheme, Platform, ScrollView } from "react-native"
|
||||||
import * as Clipboard from "expo-clipboard"
|
import * as Clipboard from "expo-clipboard"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { WIDE_CONTENT_SCROLL_CONFIG } from "../../lib/scroll-config"
|
import { WIDE_CONTENT_SCROLL_CONFIG } from "../../lib/scroll-config"
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -9,6 +10,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function CodeBlock({ code, language }: Props) {
|
export function CodeBlock({ code, language }: Props) {
|
||||||
|
const { t } = useTranslation()
|
||||||
const isDark = useColorScheme() === "dark"
|
const isDark = useColorScheme() === "dark"
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
@@ -23,9 +25,9 @@ export function CodeBlock({ code, language }: Props) {
|
|||||||
return (
|
return (
|
||||||
<View style={[styles.container, isDark && styles.containerDark]}>
|
<View style={[styles.container, isDark && styles.containerDark]}>
|
||||||
<View style={[styles.header, isDark && styles.headerDark]}>
|
<View style={[styles.header, isDark && styles.headerDark]}>
|
||||||
<Text style={[styles.language, isDark && styles.languageDark]}>{language || "code"}</Text>
|
<Text style={[styles.language, isDark && styles.languageDark]}>{language || t("common.fallbackLanguage")}</Text>
|
||||||
<TouchableOpacity onPress={copy} hitSlop={8}>
|
<TouchableOpacity onPress={copy} hitSlop={8}>
|
||||||
<Text style={[styles.copyBtn, isDark && styles.copyBtnDark]}>{copied ? "Copied!" : "Copy"}</Text>
|
<Text style={[styles.copyBtn, isDark && styles.copyBtnDark]}>{copied ? t("common.copied") : t("common.copy")}</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
<ScrollView {...WIDE_CONTENT_SCROLL_CONFIG} testID="code-block-scroll" contentContainerStyle={styles.codeScroll}>
|
<ScrollView {...WIDE_CONTENT_SCROLL_CONFIG} testID="code-block-scroll" contentContainerStyle={styles.codeScroll}>
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"retry": "Retry",
|
"retry": "Retry",
|
||||||
"save": "Save",
|
"save": "Save",
|
||||||
"shareReport": "Share report"
|
"shareReport": "Share report",
|
||||||
|
"copy": "复制",
|
||||||
|
"copied": "已复制",
|
||||||
|
"fallbackLanguage": "代码"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"sections": {
|
"sections": {
|
||||||
@@ -123,7 +126,8 @@
|
|||||||
"imageFailedMessage": "One or more images could not be processed. Please try a different photo.",
|
"imageFailedMessage": "One or more images could not be processed. Please try a different photo.",
|
||||||
"speechErrorTitle": "Voice input failed",
|
"speechErrorTitle": "Voice input failed",
|
||||||
"speechErrorMessage": "Could not use voice input. Check your microphone permission and try again."
|
"speechErrorMessage": "Could not use voice input. Check your microphone permission and try again."
|
||||||
}
|
},
|
||||||
|
"agentFallback": "build"
|
||||||
},
|
},
|
||||||
"connection": {
|
"connection": {
|
||||||
"shared": {
|
"shared": {
|
||||||
@@ -159,7 +163,7 @@
|
|||||||
"nameOptionalLabel": "Name (optional)",
|
"nameOptionalLabel": "Name (optional)",
|
||||||
"namePlaceholder": "My Mac",
|
"namePlaceholder": "My Mac",
|
||||||
"passwordIfSetLabel": "Password (if set on server)",
|
"passwordIfSetLabel": "Password (if set on server)",
|
||||||
"passwordPlaceholder": "Leave empty if none",
|
"passwordPlaceholder": "password",
|
||||||
"usernameHintPrefix": "Username defaults to ",
|
"usernameHintPrefix": "Username defaults to ",
|
||||||
"usernameHintMiddle": ". Custom username? Use ",
|
"usernameHintMiddle": ". Custom username? Use ",
|
||||||
"usernameHintSuffix": ".",
|
"usernameHintSuffix": ".",
|
||||||
@@ -175,7 +179,15 @@
|
|||||||
"connectCardTitle": "OpenCode Connect",
|
"connectCardTitle": "OpenCode Connect",
|
||||||
"connectCardBadge": "Coming Soon",
|
"connectCardBadge": "Coming Soon",
|
||||||
"connectCardDesc": "Bridge your phone to your opencode server — no tunnel setup, no firewall config. One-tap connect from anywhere.",
|
"connectCardDesc": "Bridge your phone to your opencode server — no tunnel setup, no firewall config. One-tap connect from anywhere.",
|
||||||
"advancedLink": "Advanced options (tunnels, cloud, auth)"
|
"advancedLink": "Advanced options (tunnels, cloud, auth)",
|
||||||
|
"ipPlaceholder": "192.168.1.100",
|
||||||
|
"portPlaceholder": "4096",
|
||||||
|
"emailPlaceholder": "your@email.com",
|
||||||
|
"urlPlaceholderLocal": "http://192.168.1.100:4096",
|
||||||
|
"urlPlaceholderTunnel": "https://your-tunnel.trycloudflare.com",
|
||||||
|
"urlPlaceholderCloud": "https://api.opencode.ai",
|
||||||
|
"directoryPlaceholder": "/path/to/project",
|
||||||
|
"usernamePlaceholder": "admin"
|
||||||
},
|
},
|
||||||
"waitlist": {
|
"waitlist": {
|
||||||
"successText": "You're on the list — we'll email you when OpenCode Connect is ready.",
|
"successText": "You're on the list — we'll email you when OpenCode Connect is ready.",
|
||||||
@@ -215,7 +227,8 @@
|
|||||||
"noDetail": "no detail",
|
"noDetail": "no detail",
|
||||||
"deleteTitle": "Delete Connection",
|
"deleteTitle": "Delete Connection",
|
||||||
"deleteMessage": "Are you sure you want to delete \"{{name}}\"?"
|
"deleteMessage": "Are you sure you want to delete \"{{name}}\"?"
|
||||||
}
|
},
|
||||||
|
"urlPlaceholder": "http://192.168.1.100:4096"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
@@ -388,7 +401,8 @@
|
|||||||
"serverDefaultLabel": "Server Default",
|
"serverDefaultLabel": "Server Default",
|
||||||
"browseLabel": "Browse…",
|
"browseLabel": "Browse…",
|
||||||
"recentProjectsLabel": "Recent Projects",
|
"recentProjectsLabel": "Recent Projects",
|
||||||
"usesServerDir": "Uses server's working directory"
|
"usesServerDir": "Uses server's working directory",
|
||||||
|
"pathPlaceholder": "请输入路径..."
|
||||||
},
|
},
|
||||||
"directoryBrowserSheet": {
|
"directoryBrowserSheet": {
|
||||||
"noActiveConnection": "No active connection",
|
"noActiveConnection": "No active connection",
|
||||||
@@ -434,6 +448,12 @@
|
|||||||
"hoursAgo": "{{count}}h ago",
|
"hoursAgo": "{{count}}h ago",
|
||||||
"daysAgo": "{{count}}d ago"
|
"daysAgo": "{{count}}d ago"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"messageBubble": {
|
||||||
|
"roleYou": "你",
|
||||||
|
"roleAssistant": "AI 助手",
|
||||||
|
"tokensSuffix": " tokens",
|
||||||
|
"costPrefix": "$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"demo": {
|
"demo": {
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
"delete": "删除",
|
"delete": "删除",
|
||||||
"retry": "重试",
|
"retry": "重试",
|
||||||
"save": "保存",
|
"save": "保存",
|
||||||
"shareReport": "分享报告"
|
"shareReport": "分享报告",
|
||||||
|
"copy": "复制",
|
||||||
|
"copied": "已复制",
|
||||||
|
"fallbackLanguage": "代码"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"sections": {
|
"sections": {
|
||||||
@@ -123,7 +126,8 @@
|
|||||||
"imageFailedMessage": "一张或多张图片无法处理。请尝试其他照片。",
|
"imageFailedMessage": "一张或多张图片无法处理。请尝试其他照片。",
|
||||||
"speechErrorTitle": "语音输入失败",
|
"speechErrorTitle": "语音输入失败",
|
||||||
"speechErrorMessage": "无法使用语音输入。请检查麦克风权限后重试。"
|
"speechErrorMessage": "无法使用语音输入。请检查麦克风权限后重试。"
|
||||||
}
|
},
|
||||||
|
"agentFallback": "build"
|
||||||
},
|
},
|
||||||
"connection": {
|
"connection": {
|
||||||
"shared": {
|
"shared": {
|
||||||
@@ -159,7 +163,7 @@
|
|||||||
"nameOptionalLabel": "名称(可选)",
|
"nameOptionalLabel": "名称(可选)",
|
||||||
"namePlaceholder": "我的 Mac",
|
"namePlaceholder": "我的 Mac",
|
||||||
"passwordIfSetLabel": "密码(如服务器已设置)",
|
"passwordIfSetLabel": "密码(如服务器已设置)",
|
||||||
"passwordPlaceholder": "留空表示无密码",
|
"passwordPlaceholder": "password",
|
||||||
"usernameHintPrefix": "用户名默认为 ",
|
"usernameHintPrefix": "用户名默认为 ",
|
||||||
"usernameHintMiddle": "。需要自定义用户名?请使用",
|
"usernameHintMiddle": "。需要自定义用户名?请使用",
|
||||||
"usernameHintSuffix": "。",
|
"usernameHintSuffix": "。",
|
||||||
@@ -175,7 +179,15 @@
|
|||||||
"connectCardTitle": "OpenCode Connect",
|
"connectCardTitle": "OpenCode Connect",
|
||||||
"connectCardBadge": "即将上线",
|
"connectCardBadge": "即将上线",
|
||||||
"connectCardDesc": "将手机桥接到您的 opencode 服务器 — 无需配置隧道或防火墙。一键连接,随时随地。",
|
"connectCardDesc": "将手机桥接到您的 opencode 服务器 — 无需配置隧道或防火墙。一键连接,随时随地。",
|
||||||
"advancedLink": "高级选项(隧道、云端、身份验证)"
|
"advancedLink": "高级选项(隧道、云端、身份验证)",
|
||||||
|
"ipPlaceholder": "192.168.1.100",
|
||||||
|
"portPlaceholder": "4096",
|
||||||
|
"emailPlaceholder": "your@email.com",
|
||||||
|
"urlPlaceholderLocal": "http://192.168.1.100:4096",
|
||||||
|
"urlPlaceholderTunnel": "https://your-tunnel.trycloudflare.com",
|
||||||
|
"urlPlaceholderCloud": "https://api.opencode.ai",
|
||||||
|
"directoryPlaceholder": "/path/to/project",
|
||||||
|
"usernamePlaceholder": "admin"
|
||||||
},
|
},
|
||||||
"waitlist": {
|
"waitlist": {
|
||||||
"successText": "您已加入候补名单 — OpenCode Connect 准备就绪时我们会通过邮件通知您。",
|
"successText": "您已加入候补名单 — OpenCode Connect 准备就绪时我们会通过邮件通知您。",
|
||||||
@@ -215,7 +227,8 @@
|
|||||||
"noDetail": "无详细信息",
|
"noDetail": "无详细信息",
|
||||||
"deleteTitle": "删除连接",
|
"deleteTitle": "删除连接",
|
||||||
"deleteMessage": "确定要删除 \"{{name}}\" 吗?"
|
"deleteMessage": "确定要删除 \"{{name}}\" 吗?"
|
||||||
}
|
},
|
||||||
|
"urlPlaceholder": "http://192.168.1.100:4096"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nav": {
|
"nav": {
|
||||||
@@ -388,7 +401,8 @@
|
|||||||
"serverDefaultLabel": "服务器默认",
|
"serverDefaultLabel": "服务器默认",
|
||||||
"browseLabel": "浏览…",
|
"browseLabel": "浏览…",
|
||||||
"recentProjectsLabel": "最近项目",
|
"recentProjectsLabel": "最近项目",
|
||||||
"usesServerDir": "使用服务器的工作目录"
|
"usesServerDir": "使用服务器的工作目录",
|
||||||
|
"pathPlaceholder": "请输入路径..."
|
||||||
},
|
},
|
||||||
"directoryBrowserSheet": {
|
"directoryBrowserSheet": {
|
||||||
"noActiveConnection": "没有活动连接",
|
"noActiveConnection": "没有活动连接",
|
||||||
@@ -434,6 +448,12 @@
|
|||||||
"hoursAgo": "{{count}} 小时前",
|
"hoursAgo": "{{count}} 小时前",
|
||||||
"daysAgo": "{{count}} 天前"
|
"daysAgo": "{{count}} 天前"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"messageBubble": {
|
||||||
|
"roleYou": "你",
|
||||||
|
"roleAssistant": "AI 助手",
|
||||||
|
"tokensSuffix": " tokens",
|
||||||
|
"costPrefix": "$"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"demo": {
|
"demo": {
|
||||||
|
|||||||
Reference in New Issue
Block a user