fix(ios): preserve localized app display names

This commit is contained in:
joshavant
2026-07-22 22:56:15 -05:00
parent 3c25222fb4
commit 690ed56441
93 changed files with 170 additions and 119 deletions
-16
View File
@@ -15073,14 +15073,6 @@
"surface": "android",
"id": "native.android.709aff840be1f167"
},
{
"kind": "plist-string",
"line": 8,
"path": "apps/ios/ActivityWidget/Info.plist",
"source": "OpenClaw Activity",
"surface": "apple",
"id": "native.apple.8f741d09cc1d5495"
},
{
"kind": "ui-call",
"line": 18,
@@ -15201,14 +15193,6 @@
"surface": "apple",
"id": "native.apple.18d4f8a4eea7f930"
},
{
"kind": "plist-string",
"line": 8,
"path": "apps/ios/ShareExtension/Info.plist",
"source": "OpenClaw Share",
"surface": "apple",
"id": "native.apple.9168fcd110c3019a"
},
{
"kind": "conditional-branch",
"line": 236,
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Activity";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
@@ -1 +1 @@
"CFBundleDisplayName" = "OpenClaw Share";
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(OPENCLAW_APP_DISPLAY_NAME)</string>
<string>OpenClaw</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIconName</key>
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "يستخدم OpenClaw تقويماتك لعرض الأحداث وسياق الجدولة عند تفعيل الوصول إلى التقويم.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw verwendet Ihre Kalender, um Ereignisse und Planungskontext anzuzeigen, wenn Sie den Kalenderzugriff aktivieren.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw usa tus calendarios para mostrar eventos y contexto de programación cuando activas el acceso al calendario.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw از تقویم‌های شما استفاده می‌کند تا وقتی دسترسی به تقویم را فعال می‌کنید، رویدادها و زمینهٔ زمان‌بندی را نشان دهد.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw utilise vos calendriers pour afficher les événements et le contexte de planification lorsque vous activez laccès au calendrier.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "जब आप कैलेंडर एक्सेस सक्षम करते हैं, तो OpenClaw इवेंट और शेड्यूलिंग संदर्भ दिखाने के लिए आपके कैलेंडर का उपयोग करता है.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw menggunakan kalender Anda untuk menampilkan acara dan konteks penjadwalan saat Anda mengaktifkan akses kalender.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw usa i tuoi calendari per mostrare eventi e contesto di pianificazione quando abiliti l'accesso al calendario.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClawは、カレンダーへのアクセスを有効にしたときに、イベントとスケジュールのコンテキストを表示するためにカレンダーを使用します。";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "캘린더 접근을 활성화하면 OpenClaw가 캘린더를 사용하여 이벤트와 일정 컨텍스트를 표시합니다.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw gebruikt je agendas om gebeurtenissen en planningscontext te tonen wanneer je agendatoegang inschakelt.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw używa Twoich kalendarzy do wyświetlania wydarzeń i kontekstu planowania, gdy włączysz dostęp do kalendarza.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "O OpenClaw usa seus calendários para mostrar eventos e contexto de agendamento quando você ativa o acesso ao calendário.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw использует ваши календари, чтобы показывать события и контекст расписания, когда вы включаете доступ к календарю.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw använder dina kalendrar för att visa händelser och schemaläggningssammanhang när du aktiverar kalenderåtkomst.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw ใช้ปฏิทินของคุณเพื่อแสดงกิจกรรมและบริบทการจัดกำหนดการเมื่อคุณเปิดใช้งานการเข้าถึงปฏิทิน";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw, takvim erişimini etkinleştirdiğinizde etkinlikleri ve planlama bağlamını göstermek için takvimlerinizi kullanır.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw використовує ваші календарі, щоб показувати події та контекст планування, коли ви надаєте доступ до календаря.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw sử dụng lịch của bạn để hiển thị sự kiện và ngữ cảnh lên lịch khi bạn bật quyền truy cập lịch.";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "当你启用日历访问权限时,OpenClaw 会使用你的日历来显示事件和日程安排上下文。";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,4 +1,3 @@
"CFBundleDisplayName" = "$(OPENCLAW_APP_DISPLAY_NAME)";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
"NSCalendarsFullAccessUsageDescription" = "OpenClaw 會在你啟用行事曆存取權限時,使用你的行事曆來顯示事件與排程情境。";
/* OpenClaw source: "OpenClaw uses your calendars to show events and scheduling context when you enable calendar access." */
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "يربط OpenClaw ساعة Apple Watch هذه مباشرةً بـ Gateway الخاص بك على الشبكات المحلية الموثوقة.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw verbindet diese Apple Watch in vertrauenswürdigen lokalen Netzwerken direkt mit Ihrem Gateway.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw conecta este Apple Watch directamente a tu Gateway en redes locales de confianza.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw این Apple Watch را در شبکه‌های محلی مورد اعتماد مستقیماً به Gateway شما متصل می‌کند.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw connecte cette Apple Watch directement à votre Gateway sur les réseaux locaux de confiance.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw इस Apple Watch को विश्वसनीय लोकल नेटवर्क पर सीधे आपके Gateway से कनेक्ट करता है।";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw menyambungkan Apple Watch ini langsung ke Gateway Anda di jaringan lokal tepercaya.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw connette questo Apple Watch direttamente al tuo Gateway su reti locali attendibili.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw は信頼できるローカルネットワーク上で、この Apple Watch を Gateway に直接接続します。";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw는 신뢰할 수 있는 로컬 네트워크에서 이 Apple Watch를 Gateway에 직접 연결합니다.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw verbindt deze Apple Watch rechtstreeks met je Gateway op vertrouwde lokale netwerken.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw łączy ten Apple Watch bezpośrednio z Twoim Gateway w zaufanych sieciach lokalnych.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "O OpenClaw conecta este Apple Watch diretamente ao seu Gateway em redes locais confiáveis.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw подключает эти Apple Watch напрямую к вашему Gateway в доверенных локальных сетях.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw ansluter denna Apple Watch direkt till din Gateway på betrodda lokala nätverk.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw เชื่อมต่อ Apple Watch นี้เข้ากับ Gateway ของคุณโดยตรงบนเครือข่ายในพื้นที่ที่เชื่อถือได้";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw, bu Apple Watch'u güvenilir yerel ağlarda doğrudan Gateway'inize bağlar.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw підключає цей Apple Watch безпосередньо до вашого Gateway в довірених локальних мережах.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw kết nối Apple Watch này trực tiếp với Gateway của bạn trên các mạng cục bộ đáng tin cậy.";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "在受信任的本地网络上,OpenClaw 将此 Apple Watch 直接连接到您的 Gateway。";
@@ -1,3 +1,2 @@
"CFBundleDisplayName" = "OpenClaw";
/* OpenClaw source: "OpenClaw connects this Apple Watch directly to your Gateway on trusted local networks." */
"NSLocalNetworkUsageDescription" = "OpenClaw 會在受信任的本機網路上將此 Apple Watch 直接連線到您的 Gateway。";
+1 -3
View File
@@ -114,7 +114,6 @@ targets:
CODE_SIGN_STYLE: "$(OPENCLAW_CODE_SIGN_STYLE)"
DEVELOPMENT_TEAM: "$(OPENCLAW_DEVELOPMENT_TEAM)"
OPENCLAW_ACTIVE_APP_GROUP_ID: "$(OPENCLAW_APP_GROUP_ID)"
OPENCLAW_APP_DISPLAY_NAME: OpenClaw
OPENCLAW_URL_SCHEME: openclaw
PRODUCT_BUNDLE_IDENTIFIER: "$(OPENCLAW_APP_BUNDLE_ID)"
PROVISIONING_PROFILE_SPECIFIER: "$(OPENCLAW_APP_PROFILE)"
@@ -129,7 +128,6 @@ targets:
ASSETCATALOG_COMPILER_APPICON_NAME: AppIconDebug
CODE_SIGN_STYLE: Automatic
OPENCLAW_ACTIVE_APP_GROUP_ID: "$(OPENCLAW_DEBUG_APP_GROUP_ID)"
OPENCLAW_APP_DISPLAY_NAME: OpenClaw
OPENCLAW_CODE_SIGN_ENTITLEMENTS: Sources/OpenClaw.entitlements
OPENCLAW_APNS_ENTITLEMENT_ENVIRONMENT: development
OPENCLAW_URL_SCHEME: openclaw-debug
@@ -145,7 +143,7 @@ targets:
info:
path: Sources/Info.plist
properties:
CFBundleDisplayName: "$(OPENCLAW_APP_DISPLAY_NAME)"
CFBundleDisplayName: OpenClaw
CFBundleIconName: "$(ASSETCATALOG_COMPILER_APPICON_NAME)"
CFBundleURLTypes:
- CFBundleURLName: ai.openclawfoundation.app
+1 -6
View File
@@ -481,9 +481,7 @@ function parseInfoPlistStrings(source: string): Array<{ key: string; source: str
key: decodeXml(match[1] ?? ""),
source: decodeXml(match[2] ?? ""),
}))
.filter(
(entry) => entry.key === "CFBundleDisplayName" || entry.key.endsWith("UsageDescription"),
);
.filter((entry) => entry.key.endsWith("UsageDescription"));
}
type InfoPlistTranslation = {
@@ -855,9 +853,6 @@ async function syncIosInfoPlist(write: boolean): Promise<number> {
const existing = parseStringsFile(existingSource ?? "");
const artifact = translations.find((candidate) => candidate.locale === locale);
const lines = sourceEntries.map(({ key, source }) => {
if (key === "CFBundleDisplayName") {
return `${stringsLiteral(key)} = ${stringsLiteral(source)};`;
}
const sourceId = sourceIds.get([target.sourcePath, source].join("\u0000"));
if (!sourceId) {
throw new Error(`missing native InfoPlist source id for ${target.sourcePath}:${key}`);
+17
View File
@@ -219,11 +219,28 @@ assert_build_provenance() {
fi
}
assert_localized_plists_resolve_build_settings() {
local localized_plist
local rendered
while IFS= read -r -d '' localized_plist; do
if ! rendered="$("${PLUTIL_BIN}" -convert xml1 -o - "${localized_plist}" 2>/dev/null)"; then
echo "Invalid IPA: could not read localized plist ${localized_plist#"${app_path}/"}." >&2
exit 1
fi
if grep -Eq '\$\([A-Za-z0-9_.-]+\)|\$\{[A-Za-z0-9_.-]+\}' <<<"${rendered}"; then
echo "Invalid IPA: unresolved build setting in localized plist ${localized_plist#"${app_path}/"}." >&2
exit 1
fi
done < <(find "${app_path}" -type f -path "*.lproj/InfoPlist.strings" -print0)
}
assert_plist_string "${info_plist}" "CFBundleIdentifier" "${EXPECTED_BUNDLE_ID}" "bundle identifier mismatch"
assert_plist_string "${info_plist}" "CFBundleDisplayName" "OpenClaw" "display name mismatch"
assert_plist_string "${info_plist}" "OpenClawPushMode" "${EXPECTED_PUSH_MODE}" "push mode mismatch"
assert_plist_nonempty_string "${info_plist}" "NSHealthShareUsageDescription" "Health share usage description"
assert_plist_nonempty_string "${info_plist}" "NSHealthUpdateUsageDescription" "Health update usage description"
assert_build_provenance
assert_localized_plists_resolve_build_settings
assert_plist_empty_or_absent "${info_plist}" "OpenClawPushRelayBaseURL" "push relay URL override"
assert_plist_key_absent "${info_plist}" "OpenClawPushTransport" "legacy push transport"
assert_plist_key_absent "${info_plist}" "OpenClawPushDistribution" "legacy push distribution"
+23 -5
View File
@@ -166,7 +166,9 @@ const APPLE_BUILTIN_UI_CALLS = new Set([
"Toggle",
"searchable",
]);
const APPLE_PLIST_STRINGS = /<string>([\s\S]*?)<\/string>/gu;
const APPLE_PLIST_KEYED_STRINGS = /<key>([^<]+)<\/key>\s*<string>([\s\S]*?)<\/string>/gu;
// macOS uses this legacy privacy key instead of the *UsageDescription suffix.
const APPLE_LOCALIZABLE_DESCRIPTION_KEYS = new Set(["NSScreenCaptureDescription"]);
const GENERATED_PATH_RE = /(?:^|[\\/])(?:build|\.gradle|\.build|DerivedData)(?:$|[\\/])/u;
const EXCLUDED_PATH_RE = /(?:^|[\\/])(?:Tests?|UITests?|test|Preview(?:s)?)(?:$|[\\/])/u;
const EXCLUDED_FILE_RE = /(?:Tests?|UITests?|Previews?|Testing)\.(?:swift|kt|kts)$/u;
@@ -196,6 +198,19 @@ function isAsciiAlphaNumeric(character: string): boolean {
);
}
function decodeXml(value: string): string {
return value
.replaceAll("&quot;", '"')
.replaceAll("&apos;", "'")
.replaceAll("&lt;", "<")
.replaceAll("&gt;", ">")
.replaceAll("&amp;", "&");
}
function isLocalizableApplePlistKey(key: string): boolean {
return key.endsWith("UsageDescription") || APPLE_LOCALIZABLE_DESCRIPTION_KEYS.has(key);
}
export function isConditionalBranchIdentifier(source: string): boolean {
let index = 0;
while (index < source.length && isAsciiLowercaseLetter(source.charAt(index))) {
@@ -1058,15 +1073,18 @@ export function extractNativeI18nCandidates(
}
}
if (surface === "apple" && repoPath.endsWith(".plist")) {
for (const match of source.matchAll(APPLE_PLIST_STRINGS)) {
if (match[1]) {
for (const match of source.matchAll(APPLE_PLIST_KEYED_STRINGS)) {
const key = match[1];
const value = match[2];
if (key && isLocalizableApplePlistKey(key) && value) {
const valueOffset = (match.index ?? 0) + match[0].indexOf(value);
addCandidate(
entries,
surface,
repoPath,
match[1],
decodeXml(value),
"plist-string",
lineNumber(source, match.index ?? 0),
lineNumber(source, valueOffset),
);
}
}
+24 -4
View File
@@ -1,4 +1,4 @@
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { describe, expect, it } from "vitest";
@@ -464,7 +464,7 @@ describe("Apple app i18n catalogs", () => {
]);
});
it("generates InfoPlist localizations for every shipped iOS target", async () => {
it("generates only localized usage descriptions for every shipped iOS target", async () => {
const french = await readFile("apps/ios/Sources/fr.lproj/InfoPlist.strings", "utf8");
const watchChinese = await readFile(
"apps/ios/WatchApp/zh-Hans.lproj/InfoPlist.strings",
@@ -483,8 +483,28 @@ describe("Apple app i18n catalogs", () => {
expect(french).toContain('"NSMicrophoneUsageDescription" = ');
expect(french).toContain('"NSHealthUpdateUsageDescription" = ');
expect(watchChinese).toContain('"NSLocalNetworkUsageDescription" = ');
expect(shareGerman).toContain('"CFBundleDisplayName" = "OpenClaw Share";');
expect(activityJapanese).toContain('"CFBundleDisplayName" = "OpenClaw Activity";');
expect(shareGerman.trim()).toBe("");
expect(activityJapanese.trim()).toBe("");
for (const root of [
"apps/ios/Sources",
"apps/ios/WatchApp",
"apps/ios/ShareExtension",
"apps/ios/ActivityWidget",
]) {
const localeDirs = (await readdir(root, { withFileTypes: true })).filter(
(entry) => entry.isDirectory() && entry.name.endsWith(".lproj"),
);
expect(localeDirs).toHaveLength(APPLE_I18N_LOCALES.length);
for (const localeDir of localeDirs) {
const localizedPlist = await readFile(
path.join(root, localeDir.name, "InfoPlist.strings"),
"utf8",
);
expect(localizedPlist).not.toContain("CFBundleDisplayName");
expect(localizedPlist).not.toMatch(/\$\([^)]*\)|\$\{[^}]*\}/u);
}
}
});
it("refreshes InfoPlist copy from translations for the current source", () => {
@@ -128,6 +128,10 @@ function writeFakePlutil(filePath: string): void {
filePath,
`#!/usr/bin/env node
const { readFileSync } = require("node:fs");
if (process.argv[2] === "-convert" && process.argv[3] === "xml1") {
process.stdout.write(readFileSync(process.argv[process.argv.length - 1], "utf8"));
process.exit(0);
}
const extractIndex = process.argv.indexOf("-extract");
const expectIndex = process.argv.indexOf("-expect");
if (extractIndex < 0 || expectIndex < 0 || process.argv[expectIndex + 1] !== "string") process.exit(2);
@@ -209,6 +213,8 @@ async function writeValidFixture(
buildCommit?: string;
buildTimestamp?: string;
healthUpdateUsage?: boolean | string | null;
displayName?: string;
localizedDisplayName?: string;
pushMode?: string;
legacyKey?: boolean;
} = {},
@@ -230,6 +236,7 @@ async function writeValidFixture(
const infoBody = [
plistString("CFBundleIdentifier", "ai.openclawfoundation.app"),
plistString("CFBundleDisplayName", options.displayName ?? "OpenClaw"),
plistString("OpenClawGitCommit", options.buildCommit ?? BUILD_COMMIT),
plistString("OpenClawBuildTimestamp", options.buildTimestamp ?? BUILD_TIMESTAMP),
plistString("OpenClawPushMode", options.pushMode ?? "appStore"),
@@ -249,6 +256,17 @@ async function writeValidFixture(
options.legacyKey ? plistString("OpenClawPushRelayProfile", "production") : "",
].join("");
writeFileSync(path.join(appDir, "Info.plist"), plist(infoBody), "utf8");
const localizedDir = path.join(appDir, "de.lproj");
mkdirSync(localizedDir, { recursive: true });
writeFileSync(
path.join(localizedDir, "InfoPlist.strings"),
plist(
options.localizedDisplayName === undefined
? plistString("NSCameraUsageDescription", "OpenClaw verwendet die Kamera.")
: plistString("CFBundleDisplayName", options.localizedDisplayName),
),
"utf8",
);
writeFileSync(path.join(appDir, "embedded.mobileprovision"), "fixture profile", "utf8");
const entitlementsPath = path.join(fixturesDir, "entitlements.plist");
@@ -406,6 +424,30 @@ describe("scripts/ios-validate-app-store-ipa.sh", () => {
expect(result.stderr).toContain("Health update usage description must be a non-empty string");
});
it("rejects an IPA with the wrong canonical display name", async () => {
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-"));
tempDirs.push(root);
const fixture = await writeValidFixture(root, { displayName: "OpenClaw Debug" });
const result = runValidator(fixture);
expect(result.ok).toBe(false);
expect(result.stderr).toContain("display name mismatch");
});
it("rejects unresolved build settings in localized plist resources", async () => {
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-"));
tempDirs.push(root);
const fixture = await writeValidFixture(root, {
localizedDisplayName: "$(OPENCLAW_APP_DISPLAY_NAME)",
});
const result = runValidator(fixture);
expect(result.ok).toBe(false);
expect(result.stderr).toContain("unresolved build setting in localized plist");
});
it("rejects a non-string Health update purpose value", async () => {
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-"));
tempDirs.push(root);
+19
View File
@@ -275,6 +275,25 @@ describe("native app i18n inventory", () => {
expect(entries.map((entry) => entry.source)).toEqual(["Gateway ready"]);
});
it("extracts only localizable usage descriptions from Apple plists", () => {
const entries = extractNativeI18nCandidates(
"apple",
"apps/ios/Fixture/Info.plist",
`<plist><dict>
<key>CFBundleDisplayName</key>
<string>OpenClaw Fixture</string>
<key>NSCameraUsageDescription</key>
<string>OpenClaw uses the camera to scan setup codes &amp; documents.</string>
<key>OpenClawFixtureValue</key>
<string>Runtime configuration value</string>
</dict></plist>`,
);
expect(entries.map((entry) => entry.source)).toEqual([
"OpenClaw uses the camera to scan setup codes & documents.",
]);
});
it("respects non-translatable Android collections and retains lowercase choices", () => {
const entries = extractNativeI18nCandidates(
"android",