mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 13:26:04 -06:00
feat(apple): add Russian and Hindi app catalogs
This commit is contained in:
@@ -836,6 +836,10 @@ jobs:
|
||||
- name: Check Android app i18n resources
|
||||
run: pnpm android:i18n:check
|
||||
|
||||
- name: Check Apple app i18n catalogs
|
||||
if: needs.preflight.outputs.run_macos == 'true'
|
||||
run: pnpm apple:i18n:check
|
||||
|
||||
checks-fast-core:
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Agent" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Agent" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "एजेंट" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Агент" } }
|
||||
}
|
||||
},
|
||||
"Cancel" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Cancel" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "रद्द करें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Отмена" } }
|
||||
}
|
||||
},
|
||||
"Chat" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Chat" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "चैट" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Чат" } }
|
||||
}
|
||||
},
|
||||
"Close" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Close" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "बंद करें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Закрыть" } }
|
||||
}
|
||||
},
|
||||
"Connect" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Connect" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "कनेक्ट करें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Подключить" } }
|
||||
}
|
||||
},
|
||||
"Connect to a Gateway?" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Connect to a Gateway?" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "गेटवे से कनेक्ट करें?" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Подключиться к шлюзу?" } }
|
||||
}
|
||||
},
|
||||
"Connecting…" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Connecting…" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "कनेक्ट हो रहा है…" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Подключение…" } }
|
||||
}
|
||||
},
|
||||
"Control" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Control" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "कंट्रोल" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Управление" } }
|
||||
}
|
||||
},
|
||||
"Done" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Done" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "पूर्ण" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Готово" } }
|
||||
}
|
||||
},
|
||||
"Not now" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Not now" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "अभी नहीं" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Не сейчас" } }
|
||||
}
|
||||
},
|
||||
"No gateways found yet. Make sure your gateway is running and Bonjour discovery is enabled." : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "No gateways found yet. Make sure your gateway is running and Bonjour discovery is enabled." } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "अभी कोई गेटवे नहीं मिला। सुनिश्चित करें कि आपका गेटवे चल रहा है और Bonjour खोज सक्षम है।" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Шлюзы пока не найдены. Убедитесь, что шлюз запущен и обнаружение Bonjour включено." } }
|
||||
}
|
||||
},
|
||||
"Quick Setup" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Quick Setup" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "त्वरित सेटअप" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Быстрая настройка" } }
|
||||
}
|
||||
},
|
||||
"Settings" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Settings" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "सेटिंग्स" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Настройки" } }
|
||||
}
|
||||
},
|
||||
"Trust and connect" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Trust and connect" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "भरोसा करें और कनेक्ट करें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Доверять и подключиться" } }
|
||||
}
|
||||
},
|
||||
"Trust this gateway?" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Trust this gateway?" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "इस गेटवे पर भरोसा करें?" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Доверять этому шлюзу?" } }
|
||||
}
|
||||
},
|
||||
"Writing" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Writing" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "लिख रहा है" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Пишет" } }
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -60,6 +60,8 @@ targets:
|
||||
Release: Signing.xcconfig
|
||||
sources:
|
||||
- path: Sources
|
||||
resources:
|
||||
- path: Resources/Localizable.xcstrings
|
||||
dependencies:
|
||||
- target: OpenClawShareExtension
|
||||
embed: true
|
||||
|
||||
@@ -63,6 +63,7 @@ let package = Package(
|
||||
resources: [
|
||||
.copy("Resources/OpenClaw.icns"),
|
||||
.copy("Resources/DeviceModels"),
|
||||
.process("Resources/Localizable.xcstrings"),
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableUpcomingFeature("StrictConcurrency"),
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"Logout" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Logout" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "लॉग आउट" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Выйти" } }
|
||||
}
|
||||
},
|
||||
"Refresh" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Refresh" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "रिफ्रेश करें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Обновить" } }
|
||||
}
|
||||
},
|
||||
"Run now" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Run now" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "अभी चलाएँ" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Запустить сейчас" } }
|
||||
}
|
||||
},
|
||||
"Save" : {
|
||||
"localizations" : {
|
||||
"en" : { "stringUnit" : { "state" : "translated", "value" : "Save" } },
|
||||
"hi" : { "stringUnit" : { "state" : "translated", "value" : "सहेजें" } },
|
||||
"ru" : { "stringUnit" : { "state" : "translated", "value" : "Сохранить" } }
|
||||
}
|
||||
}
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -1959,6 +1959,7 @@
|
||||
"native:i18n:check": "node --import tsx scripts/native-app-i18n.ts check",
|
||||
"native:i18n:sync": "node --import tsx scripts/native-app-i18n.ts sync --write",
|
||||
"android:i18n:check": "node --import tsx scripts/android-app-i18n.ts check",
|
||||
"apple:i18n:check": "node --import tsx scripts/apple-app-i18n.ts check",
|
||||
"ui:install": "node scripts/ui.js install",
|
||||
"verify": "node scripts/verify.mjs"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(HERE, "..");
|
||||
const CATALOGS = [
|
||||
path.join(ROOT, "apps", "ios", "Resources", "Localizable.xcstrings"),
|
||||
path.join(ROOT, "apps", "macos", "Sources", "OpenClaw", "Resources", "Localizable.xcstrings"),
|
||||
];
|
||||
|
||||
type Catalog = {
|
||||
sourceLanguage?: string;
|
||||
strings?: Record<
|
||||
string,
|
||||
{
|
||||
localizations?: Record<string, { stringUnit?: { value?: string } }>;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
export async function checkAppleAppI18n() {
|
||||
let checked = 0;
|
||||
for (const catalogPath of CATALOGS) {
|
||||
const catalog = JSON.parse(await readFile(catalogPath, "utf8")) as Catalog;
|
||||
if (catalog.sourceLanguage !== "en" || !catalog.strings) {
|
||||
throw new Error(`invalid Apple string catalog: ${path.relative(ROOT, catalogPath)}`);
|
||||
}
|
||||
for (const [key, entry] of Object.entries(catalog.strings)) {
|
||||
for (const locale of ["en", "ru", "hi"]) {
|
||||
const value = entry.localizations?.[locale]?.stringUnit?.value?.trim();
|
||||
if (!value) {
|
||||
throw new Error(
|
||||
`Apple catalog ${path.relative(ROOT, catalogPath)} is missing ${locale} for ${JSON.stringify(key)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
checked += 1;
|
||||
}
|
||||
}
|
||||
process.stdout.write(
|
||||
`apple-app-i18n: catalogs=${CATALOGS.length} keys=${checked} locales=ru,hi\n`,
|
||||
);
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === `file://${path.resolve(process.argv[1])}`) {
|
||||
const [command] = process.argv.slice(2);
|
||||
if (command !== "check") {
|
||||
throw new Error("usage: node --import tsx scripts/apple-app-i18n.ts check");
|
||||
}
|
||||
await checkAppleAppI18n();
|
||||
}
|
||||
@@ -746,6 +746,7 @@ const TOOLING_SOURCE_TEST_TARGETS = new Map([
|
||||
["scripts/ci-changed-scope.mjs", ["src/scripts/ci-changed-scope.test.ts"]],
|
||||
["scripts/ci-docker-pull-retry.sh", ["test/scripts/ci-docker-pull-retry.test.ts"]],
|
||||
["scripts/control-ui-i18n.ts", ["test/scripts/control-ui-i18n.test.ts"]],
|
||||
["scripts/apple-app-i18n.ts", ["test/scripts/apple-app-i18n.test.ts"]],
|
||||
["scripts/native-app-i18n.ts", ["test/scripts/native-app-i18n.test.ts"]],
|
||||
["scripts/android-app-i18n.ts", ["test/scripts/android-app-i18n.test.ts"]],
|
||||
[
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { checkAppleAppI18n } from "../../scripts/apple-app-i18n.ts";
|
||||
|
||||
describe("Apple app i18n catalogs", () => {
|
||||
it("keeps the checked-in Russian and Hindi catalog entries complete", async () => {
|
||||
await expect(checkAppleAppI18n()).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user