chore: adopt source-available license and strengthen privacy disclosures

Replace MIT with a restrictive source-available license and align README/docs copy. Add cloud polish acknowledgment, privacy manifest updates, voice history disclosure, export compliance metadata, and support links for App Store readiness.
This commit is contained in:
Rocky
2026-06-21 16:45:29 +08:00
parent 993784d223
commit 5e5122f172
21 changed files with 429 additions and 64 deletions
+216 -17
View File
@@ -1,21 +1,220 @@
MIT License
OSGKeyboard Source Available License
Version 1.0, June 2026
Copyright (c) 2026 OSGKeyboard Contributors
Copyright (c) 2026 OSGKeyboard Contributors. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
This is NOT an open-source license. The software is made available for
transparency and personal, non-commercial use only. Commercial rights are
reserved exclusively by the copyright holder unless a separate written
agreement is granted.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Contact for commercial licensing: rocky.hk@gmail.com
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
1. Definitions
"Software" means the OSGKeyboard source code, documentation, and
associated files in this repository.
"You" means any individual or legal entity exercising permissions under
this License.
"Commercial Use" means any use of the Software that is intended for or
directed toward commercial advantage or monetary compensation, including
but not limited to: selling, licensing, or distributing the Software or
derivatives; offering a product or service that incorporates or is
derived from the Software; using the Software in production for a
business; or using the Software to develop a competing product.
2. Permitted Uses (Non-Commercial, Personal)
You may, without a separate commercial license:
(a) Clone, download, and view the Software for personal learning and
reference.
(b) Build and run the Software locally on devices you own or control,
for personal, non-commercial evaluation and debugging.
(c) Modify the Software for your own personal, non-commercial use on
devices you own or control, provided you do not distribute,
publish, or make available any modified version to any third party.
3. Prohibited Uses
Without prior written permission from the copyright holder, You may NOT:
(a) Use the Software for any Commercial Use.
(b) Redistribute, publish, sublicense, sell, or otherwise make the
Software or any portion thereof available to any third party,
whether in original or modified form.
(c) Create, maintain, or distribute forked or derivative works based on
the Software, including repositories, packages, or products intended
for public or third-party use.
(d) Remove or alter copyright notices or this License.
(e) Use the Software, or any derivative thereof, as the basis for a
competing keyboard, voice-input, or substantially similar product.
4. Commercial Licensing
Exclusive commercial rights are retained by the copyright holder.
Commercial Use requires a separate written license. To inquire, contact:
rocky.hk@gmail.com
5. Contributions
Contributions (e.g., pull requests, patches, issues with code) are
welcome. By submitting a contribution, You represent that You have the
right to submit it and You grant the copyright holder a perpetual,
worldwide, non-exclusive, royalty-free license to use, reproduce,
modify, and sublicense Your contribution as part of the Software under
this License. You retain ownership of Your contribution; no copyright
transfer is required.
6. App Store Distribution
Distribution of compiled application binaries through the Apple App Store
(or other channels) by the copyright holder or its authorized licensees
does not grant You any rights to the source code beyond those stated in
this License.
7. Third-Party Components
The Software may include or depend on third-party components governed by
their own licenses. This License applies only to OSGKeyboard's own
original work and does not override third-party license terms.
8. Disclaimer of Warranty
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
9. Limitation of Liability
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10. Termination
Your rights under this License terminate automatically if You breach
any term. Sections 39 survive termination.
11. Governing Language
This English text is the authoritative version. A Chinese translation
follows for reference only.
================================================================================
OSGKeyboard 源码可见许可协议
版本 1.02026 年 6 月
版权所有 (c) 2026 OSGKeyboard Contributors。保留所有权利。
本协议并非开源许可。公开源码仅为透明与个人非商用目的。除另行书面授权外,
商业权利由版权持有人独占保留。
商业授权联系:rocky.hk@gmail.com
1. 定义
「软件」指本仓库中的 OSGKeyboard 源代码、文档及相关文件。
「您」指在本协议下行使权利的任何个人或法人。
「商业使用」指以商业利益或金钱报酬为目的的使用,包括但不限于:销售、
许可或分发软件或其衍生作品;提供包含或源于本软件的产品或服务;在
商业生产环境中使用;或用于开发竞争产品。
2. 允许的使用(非商用、个人)
无需另行商业授权,您可:
(a) 克隆、下载、阅读软件,用于个人学习与研究;
(b) 在自有或控制的设备上构建并运行软件,用于个人非商用评估与调试;
(c) 为个人非商用目的修改软件并在自有设备上使用,但不得向任何第三方
分发、发布或提供任何修改版本。
3. 禁止的使用
未经版权持有人事先书面许可,您不得:
(a) 将软件用于任何商业使用;
(b) 以任何形式向第三方再分发、发布、转许可、销售或提供软件或其部分,
无论是否修改;
(c) 创建、维护或分发基于本软件的分叉或衍生作品,包括面向公众或第三
方的仓库、包或产品;
(d) 移除或篡改版权声明或本协议;
(e) 将软件或其衍生作品作为竞争性的键盘、语音输入或实质相似产品的
基础。
4. 商业授权
商业权利由版权持有人独占保留。商业使用须取得单独书面授权。联系:
rocky.hk@gmail.com
5. 贡献
欢迎提交贡献(如 Pull Request、补丁等)。提交即表示您有权提交该内容,
并授予版权持有人一项永久的、全球性的、非独占的、免版税许可,以便在
本协议下将您的贡献作为软件的一部分使用、复制、修改和再许可。您保留
对贡献的所有权;不要求转让版权。
6. App Store 分发
版权持有人或其授权方通过 Apple App Store(或其他渠道)分发编译后的
应用,不授予您超出本协议所述范围的源码权利。
7. 第三方组件
软件可能包含或依赖受各自许可约束的第三方组件。本协议仅适用于
OSGKeyboard 原创作品,不覆盖第三方许可条款。
8. 免责声明
软件按「原样」提供,不提供任何明示或暗示的保证,包括但不限于适销性、
特定用途适用性和不侵权保证。
9. 责任限制
在任何情况下,版权持有人或贡献者均不对因软件或使用软件而产生的任何
索赔、损害或其他责任承担责任。
10. 终止
若您违反本协议任何条款,您的权利自动终止。第 3–9 条在终止后继续有效。
+2
View File
@@ -36,6 +36,8 @@
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>NSAppTransportSecurity</key>
+14 -1
View File
@@ -7,7 +7,20 @@
<key>NSPrivacyTrackingDomains</key>
<array/>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<array>
<dict>
<key>NSPrivacyCollectedDataType</key>
<string>NSPrivacyCollectedDataTypeOtherUserContent</string>
<key>NSPrivacyCollectedDataTypeLinked</key>
<false/>
<key>NSPrivacyCollectedDataTypeTracking</key>
<false/>
<key>NSPrivacyCollectedDataTypePurposes</key>
<array>
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
</array>
</dict>
</array>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
+5
View File
@@ -8,4 +8,9 @@ enum LegalLinks {
static var privacyPolicyURL: URL? {
URL(string: "https://hkgood.github.io/OSGKeyboard/privacy/")
}
/// Support / feedback (GitHub Issues).
static var supportURL: URL? {
URL(string: "https://github.com/hkgood/OSGKeyboard/issues")
}
}
@@ -0,0 +1,93 @@
// PrivacyDisclosureViews.swift
// OSGKeyboard · Main App
//
// Reusable privacy / data-flow disclosure blocks for Settings and Onboarding.
import SwiftUI
import OSGKeyboardShared
/// Card-style block for Full Access and similar permission explanations.
struct PrivacyInfoCard: View {
@Environment(\.themePalette) private var palette: ThemePalette
let title: LocalizedStringKey
let bodyText: LocalizedStringKey
var body: some View {
VStack(alignment: .leading, spacing: Spacing.xs) {
Text(title)
.font(TypeStyle.body)
.foregroundStyle(palette.textPrimary)
Text(bodyText)
.font(TypeStyle.caption2)
.foregroundStyle(palette.textSecondary)
.fixedSize(horizontal: false, vertical: true)
}
.padding(Spacing.md)
.frame(maxWidth: .infinity, alignment: .leading)
.background(palette.surface, in: RoundedRectangle(cornerRadius: Radius.large, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
.stroke(palette.divider, lineWidth: 0.5)
)
}
}
/// Footnote when Cloud polish is active text goes to the user's configured API.
struct CloudPolishDisclosureBanner: View {
@Environment(\.themePalette) private var palette: ThemePalette
var body: some View {
HStack(alignment: .top, spacing: Spacing.sm) {
Image(systemName: "info.circle")
.font(.system(size: 14, weight: .medium))
.foregroundStyle(palette.accent)
Text("settings.privacy.cloud.body")
.font(TypeStyle.caption2)
.foregroundStyle(palette.textSecondary)
.fixedSize(horizontal: false, vertical: true)
}
.padding(Spacing.md)
.frame(maxWidth: .infinity, alignment: .leading)
.background(palette.accentMuted.opacity(0.35), in: RoundedRectangle(cornerRadius: Radius.medium, style: .continuous))
}
}
/// First-time confirmation before enabling Cloud polish (user-configured third-party API).
struct CloudSharingAcknowledgmentModifier: ViewModifier {
@ObservedObject var config: ProviderConfig
@Binding var isPresented: Bool
let onConfirm: () -> Void
let onCancel: () -> Void
func body(content: Content) -> some View {
content
.alert("settings.privacy.cloud.alert.title", isPresented: $isPresented) {
Button("common.continue") {
config.hasAcknowledgedCloudSharing = true
onConfirm()
}
Button("common.cancel", role: .cancel) {
onCancel()
}
} message: {
Text("settings.privacy.cloud.alert.message")
}
}
}
extension View {
func cloudSharingAcknowledgment(
config: ProviderConfig,
isPresented: Binding<Bool>,
onConfirm: @escaping () -> Void,
onCancel: @escaping () -> Void
) -> some View {
modifier(CloudSharingAcknowledgmentModifier(
config: config,
isPresented: isPresented,
onConfirm: onConfirm,
onCancel: onCancel
))
}
}
+31 -3
View File
@@ -22,6 +22,8 @@ struct EnginePickerSection: View {
@Environment(\.themePalette) private var palette: ThemePalette
@ObservedObject var config: ProviderConfig
@State private var showCloudAcknowledgment = false
@State private var pendingEngineSelection: String?
var body: some View {
VStack(alignment: .leading, spacing: SettingsListMetrics.sectionLabelSpacing) {
@@ -46,8 +48,18 @@ struct EnginePickerSection: View {
RoundedRectangle(cornerRadius: Radius.large, style: .continuous)
.stroke(palette.divider, lineWidth: 0.5)
)
if config.engineMode == "cloud" {
CloudPolishDisclosureBanner()
}
}
.cloudSharingAcknowledgment(
config: config,
isPresented: $showCloudAcknowledgment,
onConfirm: { applyPendingEngineSelection() },
onCancel: { pendingEngineSelection = nil }
)
}
private var localSubtitle: String {
// iOS 26's `SpeechAnalyzer` is always fully on-device, so the
@@ -64,10 +76,13 @@ struct EnginePickerSection: View {
) -> some View {
let isSelected = config.engineMode == id
return Button {
withAnimation(.easeInOut(duration: 0.2)) {
config.engineMode = id
if id == "local" { config.modeId = "transcribe" }
guard config.engineMode != id else { return }
if id == "cloud", !config.hasAcknowledgedCloudSharing {
pendingEngineSelection = id
showCloudAcknowledgment = true
return
}
selectEngine(id)
} label: {
HStack(spacing: Spacing.sm) {
engineMark(
@@ -97,6 +112,19 @@ struct EnginePickerSection: View {
.buttonStyle(.plain)
}
private func selectEngine(_ id: String) {
withAnimation(.easeInOut(duration: 0.2)) {
config.engineMode = id
if id == "local" { config.modeId = "transcribe" }
}
}
private func applyPendingEngineSelection() {
guard let id = pendingEngineSelection else { return }
pendingEngineSelection = nil
selectEngine(id)
}
@ViewBuilder
private func engineMark(assetName: String?, systemIcon: String?, isSelected: Bool) -> some View {
ZStack {
+7
View File
@@ -39,6 +39,13 @@ struct HistoryView: View {
}
}
Text("history.subtitle")
.font(TypeStyle.caption2)
.foregroundStyle(palette.textSecondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, Spacing.md)
.padding(.bottom, Spacing.sm)
ZStack {
palette.background.ignoresSafeArea()
+7
View File
@@ -617,6 +617,13 @@ private struct EnableKeyboardPage: View {
.padding(.horizontal, Spacing.xl)
.padding(.top, OnboardingLayoutMetrics.heroTextGap)
PrivacyInfoCard(
title: "settings.privacy.fullAccess.title",
bodyText: "settings.privacy.fullAccess.body"
)
.padding(.horizontal, Spacing.lg)
.padding(.top, Spacing.lg)
Button {
AppPermissions.openSystemSettings()
} label: {
+5
View File
@@ -42,6 +42,7 @@ struct SettingsView: View {
confirmActionTitle: "common.reset"
) {
config.reset()
SpeechHistoryStore.shared.clearAll()
}
if presentation == .sheet {
Button("common.done") { dismiss() }
@@ -301,6 +302,10 @@ struct SettingsView: View {
footerLinkRow(title: "settings.privacy.policy", url: url)
Divider().background(palette.divider)
}
if let url = LegalLinks.supportURL {
footerLinkRow(title: "settings.link.support", url: url)
Divider().background(palette.divider)
}
footerLinkRow(
title: "settings.link.github",
url: URL(string: "https://github.com/hkgood/OSGKeyboard")!
+4
View File
@@ -0,0 +1,4 @@
/* Permission prompts — English */
"NSMicrophoneUsageDescription" = "OSGKeyboard uses the microphone for voice dictation and keeps a background audio session active while a voice session is running.";
"NSSpeechRecognitionUsageDescription" = "OSGKeyboard uses on-device speech recognition to transcribe your voice. Audio is processed on your device and is not uploaded for transcription.";
+6 -2
View File
@@ -84,7 +84,7 @@
/* Settings */
"settings.title" = "Settings";
"settings.reset.title" = "Reset all settings?";
"settings.reset.message" = "API key, model, and base URL will be cleared.";
"settings.reset.message" = "API key, model, base URL, voice history, and cloud consent will be cleared.";
"settings.reset.confirm" = "Reset all settings";
"settings.engine.title" = "Engine";
"settings.engine.subtitle" = "Pick the recognition engine. Local engine does transcription only, no API key needed.";
@@ -92,7 +92,7 @@
"settings.engine.local.ios26" = "Always on-device, no network.";
"settings.engine.local.legacy" = "On-device ASR. Transcription only, no polish.";
"settings.engine.cloud.title" = "Cloud polish";
"settings.engine.cloud.subtitle" = "ASR + LLM polish. API key required.";
"settings.engine.cloud.subtitle" = "On-device ASR + polish via your API. Text is sent to the endpoint you configure.";
"settings.provider.title" = "Provider";
"settings.provider.subtitle" = "Pick the LLM that polishes your dictation.";
"provider.openai" = "OpenAI";
@@ -123,6 +123,10 @@
"settings.privacy.policy" = "Privacy Policy";
"settings.privacy.fullAccess.title" = "About Full Access";
"settings.privacy.fullAccess.body" = "Full Access is required for the microphone and to read your API key. OSGKeyboard does not record or upload what you type with the keyboard.";
"settings.privacy.cloud.body" = "In Cloud polish mode, transcribed text is sent to the API endpoint you configure (e.g. OpenAI or your own server). OSGKeyboard does not operate servers and does not store transcripts in the cloud.";
"settings.privacy.cloud.alert.title" = "Third-party API";
"settings.privacy.cloud.alert.message" = "Cloud polish sends transcribed text to the third-party API you configure. OSGKeyboard never stores data on our servers. Continue?";
"settings.link.support" = "Help & Feedback";
"settings.link.github" = "GitHub";
/* App group error */
@@ -0,0 +1,4 @@
/* 权限说明 — 简体中文 */
"NSMicrophoneUsageDescription" = "OSGKeyboard 使用麦克风进行语音听写,并在语音会话运行期间保持后台音频会话。";
"NSSpeechRecognitionUsageDescription" = "OSGKeyboard 使用设备端语音识别将你的语音转为文字。音频仅在设备上处理,不会上传用于转写。";
@@ -84,7 +84,7 @@
/* Settings */
"settings.title" = "设置";
"settings.reset.title" = "重置所有设置?";
"settings.reset.message" = "API key、model 和 base URL 都会被清空。";
"settings.reset.message" = "将清空 API Key、模型、接口地址、语音历史及云端确认状态。";
"settings.reset.confirm" = "重置所有设置";
"settings.engine.title" = "引擎";
"settings.engine.subtitle" = "本地不用 Key,只转文字;云端会润色,要配 Key。";
@@ -92,7 +92,7 @@
"settings.engine.local.ios26" = "全程在手机本地,不用联网";
"settings.engine.local.legacy" = "端侧 ASR,仅转录,无润色。";
"settings.engine.cloud.title" = "云端润色";
"settings.engine.cloud.subtitle" = "先转文字,再用 AI 润色,要 API Key";
"settings.engine.cloud.subtitle" = "本地转写 + 你配置的 API 润色,文字发往该第三方服务";
"settings.provider.title" = "提供商";
"settings.provider.subtitle" = "选择 LLM 提供商。";
"provider.openai" = "OpenAI";
@@ -123,6 +123,10 @@
"settings.privacy.policy" = "隐私政策";
"settings.privacy.fullAccess.title" = "关于完全访问";
"settings.privacy.fullAccess.body" = "用来调用麦克风和读取 API Key;不会读取或上传你的输入内容。";
"settings.privacy.cloud.body" = "云端润色模式下,转写文字会发送到你配置的 API(如 OpenAI 或自建服务)。OSGKeyboard 不运营服务器,也不会把转写内容存到云端。";
"settings.privacy.cloud.alert.title" = "第三方 API";
"settings.privacy.cloud.alert.message" = "云端润色会把转写文字发到你配置的第三方 API。OSGKeyboard 不会在自有服务器上存储数据。是否继续?";
"settings.link.support" = "帮助与反馈";
"settings.link.github" = "GitHub";
/* App group error */
+3 -26
View File
@@ -1,10 +1,9 @@
// HostAppLauncher.swift
// OSGKeyboard · Keyboard Extension
//
// Opens the host app via URL using every extension-safe strategy:
// Opens the host app via URL using extension-safe strategies:
// 1. `extensionContext.open` (official)
// 2. Responder-chain `UIApplication.open` (TypeWhisper pattern)
// 3. `sharedApplication` KVC fallback (common in full-access keyboards)
import UIKit
@@ -22,23 +21,12 @@ enum HostAppLauncher {
completion(true)
return
}
completion(openViaFallback(url, from: controller))
completion(openViaResponderChain(url, from: controller))
}
}
return
}
completion(openViaFallback(url, from: controller))
}
@MainActor
private static func openViaFallback(
_ url: URL,
from controller: KeyboardViewController
) -> Bool {
if openViaResponderChain(url, from: controller) {
return true
}
return openViaSharedApplication(url)
completion(openViaResponderChain(url, from: controller))
}
@MainActor
@@ -56,15 +44,4 @@ enum HostAppLauncher {
}
return false
}
@MainActor
private static func openViaSharedApplication(_ url: URL) -> Bool {
guard
let application = UIApplication.value(forKeyPath: "sharedApplication") as? UIApplication
else {
return false
}
application.open(url, options: [:]) { _ in }
return true
}
}
@@ -29,6 +29,7 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
static let engineMode = "config.engineMode"
static let hasCompletedOnboarding = "config.hasCompletedOnboarding"
static let onboardingPage = "config.onboardingPage"
static let hasAcknowledgedCloudSharing = "config.hasAcknowledgedCloudSharing"
}
@Published public var providerId: String {
@@ -80,6 +81,10 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
@Published public var onboardingPage: Int {
didSet { defaults.set(onboardingPage, forKey: Key.onboardingPage) }
}
/// User confirmed that Cloud polish sends transcripts to their configured third-party API.
@Published public var hasAcknowledgedCloudSharing: Bool {
didSet { defaults.set(hasAcknowledgedCloudSharing, forKey: Key.hasAcknowledgedCloudSharing) }
}
public var isConfigured: Bool {
// Local engine (on-device ASR only) doesn't need an API key,
@@ -125,6 +130,7 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
self.hasCompletedOnboarding = resolvedDefaults.bool(forKey: Key.hasCompletedOnboarding)
let savedPage = resolvedDefaults.integer(forKey: Key.onboardingPage)
self.onboardingPage = savedPage > 0 ? savedPage : 0
self.hasAcknowledgedCloudSharing = resolvedDefaults.bool(forKey: Key.hasAcknowledgedCloudSharing)
}
/// Read the API key from the Keychain, falling back to a one-time
@@ -170,5 +176,6 @@ public final class ProviderConfig: ObservableObject, @unchecked Sendable {
apiKey = ""
model = preset.defaultModel
systemPrompt = AppGroupStore.defaultSystemPrompt(for: "openai")
hasAcknowledgedCloudSharing = false
}
}
+4 -4
View File
@@ -1,11 +1,11 @@
# OSGKeyboard
> Hold a key, speak, release — AI-polished text appears at your cursor in any app.
> An open-source, custom-keyboard-based voice input tool for iOS 26+, inspired by [Typeless](https://typeless.com) and [OpenLess](https://github.com/Open-Less/openless).
> A source-available, custom-keyboard-based voice input tool for iOS 26+, inspired by [Typeless](https://typeless.com) and [OpenLess](https://github.com/Open-Less/openless).
![Platform](https://img.shields.io/badge/platform-iOS%2026%2B-0078D4?logo=apple)
![Swift](https://img.shields.io/badge/Swift-6.0-FA7343?logo=swift)
![License](https://img.shields.io/badge/license-MIT-green)
![License](https://img.shields.io/badge/license-Source%20Available-blue)
![CI](https://github.com/hkgood/OSGKeyboard/actions/workflows/ci.yml/badge.svg)
[中文 README](./README.zh.md)
@@ -14,7 +14,7 @@
## What is it?
OSGKeyboard is a free, open alternative to commercial voice-input tools. It runs as a **Custom Keyboard Extension** on iOS, so you can use it in **any app** — Messages, Notes, Mail, ChatGPT, Claude, Cursor, you name it.
OSGKeyboard is a free, source-available alternative to commercial voice-input tools. It runs as a **Custom Keyboard Extension** on iOS, so you can use it in **any app** — Messages, Notes, Mail, ChatGPT, Claude, Cursor, you name it.
1. Press and hold the mic key
2. Speak naturally
@@ -138,7 +138,7 @@ That's it. No other code changes required.
## License
[MIT](./LICENSE) — use it, fork it, ship it. No warranty.
[OSGKeyboard Source Available License](./LICENSE) — personal learning and non-commercial local use only. No commercial use, redistribution, or public forks without permission. Commercial licensing: [rocky.hk@gmail.com](mailto:rocky.hk@gmail.com).
---
+4 -4
View File
@@ -1,11 +1,11 @@
# OSGKeyboard
> 按住说话,松开即得 AI 润色文字,插入任意 App 的光标处。
> 一款源的 iOS 自定义键盘语音输入工具,灵感来自 [Typeless](https://typeless.com) 和 [OpenLess](https://github.com/Open-Less/openless)。
> 一款源码可见的 iOS 自定义键盘语音输入工具,灵感来自 [Typeless](https://typeless.com) 和 [OpenLess](https://github.com/Open-Less/openless)。
![Platform](https://img.shields.io/badge/platform-iOS%2026%2B-0078D4?logo=apple)
![Swift](https://img.shields.io/badge/Swift-6.0-FA7343?logo=swift)
![License](https://img.shields.io/badge/license-MIT-green)
![License](https://img.shields.io/badge/license-Source%20Available-blue)
[English README](./README.md)
@@ -13,7 +13,7 @@
## 这是什么?
OSGKeyboard 是商业语音输入工具的免费开源替代。它以 **iOS 自定义键盘扩展** 的形式运行,所以你可以在 **任何 App** 里使用 —— 微信、备忘录、邮件、ChatGPT、Claude、Cursor,无所不能。
OSGKeyboard 是商业语音输入工具的免费、源码可见替代方案。它以 **iOS 自定义键盘扩展** 的形式运行,所以你可以在 **任何 App** 里使用 —— 微信、备忘录、邮件、ChatGPT、Claude、Cursor,无所不能。
1. 长按麦克风键
2. 自由说话
@@ -137,7 +137,7 @@ LLMProvider(
## 许可
[MIT](./LICENSE) —— 使用、修改、商用均可。无任何担保
[OSGKeyboard 源码可见许可协议](./LICENSE) —— 仅限个人学习与非商用本地使用;禁止商用、再分发及公开 fork。商业授权请联系 [rocky.hk@gmail.com](mailto:rocky.hk@gmail.com)
---
+5 -5
View File
@@ -719,7 +719,7 @@
<footer class="footer">
<div class="footer-inner">
<span data-i18n="footer.copy">© OSGKeyboard — open source under MIT</span>
<span data-i18n="footer.copy">© OSGKeyboard — source available</span>
<span>
<a href="https://github.com/hkgood/OSGKeyboard" data-i18n="footer.source">Source code</a>
·
@@ -732,7 +732,7 @@
const copy = {
en: {
"nav.github": "GitHub",
"hero.eyebrow": "Open source · iOS 26+",
"hero.eyebrow": "Source available · iOS 26+",
"hero.title": "Hold to talk.<br>Polished text in any app.",
"hero.lead": "A custom keyboard that transcribes your voice on-device and optionally polishes the result with the LLM you configure — WeChat, Notes, Mail, ChatGPT, and everywhere else you type.",
"hero.cta.primary": "View on GitHub",
@@ -768,13 +768,13 @@
"privacy.title": "Privacy at a glance",
"privacy.body": "Audio is processed on your device. We do not log ordinary keystrokes. In cloud polish mode, only transcribed text — never audio — goes to the API provider you configure.",
"privacy.cta": "Read full policy",
"footer.copy": "© OSGKeyboard — open source under MIT",
"footer.copy": "© OSGKeyboard — source available",
"footer.source": "Source code",
"footer.privacy": "Privacy"
},
zh: {
"nav.github": "GitHub",
"hero.eyebrow": "源 · 需要 iOS 26+",
"hero.eyebrow": "源码可见 · 需要 iOS 26+",
"hero.title": "按住说话,<br>任意 App 里获得润色文字。",
"hero.lead": "自定义键盘扩展:在设备端转写语音,并可选通过你配置的 LLM 润色结果。适用于微信、备忘录、邮件、ChatGPT 等所有出现键盘的场景。",
"hero.cta.primary": "GitHub 仓库",
@@ -810,7 +810,7 @@
"privacy.title": "隐私摘要",
"privacy.body": "音频在设备端处理。我们不记录普通击键。云端润色模式下,仅转写文字(非音频)会发送到你配置的 API 服务商。",
"privacy.cta": "查看完整政策",
"footer.copy": "© OSGKeyboard · MIT 开源",
"footer.copy": "© OSGKeyboard · 源码可见",
"footer.source": "源代码",
"footer.privacy": "隐私"
}
+2
View File
@@ -45,6 +45,7 @@
<h2>Data retention</h2>
<p>Settings and API keys remain on your device until you delete the app or reset settings. Transcription results are passed to the host app you are typing in and are not stored long-term by OSGKeyboard.</p>
<p><strong>Voice history</strong> — successful transcripts may be saved locally in the main apps History tab for your convenience. This history stays on your device only, is never uploaded, and can be cleared at any time from History or by resetting settings.</p>
<h2>Contact</h2>
<p>Questions: open an issue at <a href="https://github.com/hkgood/OSGKeyboard">github.com/hkgood/OSGKeyboard</a>.</p>
@@ -81,6 +82,7 @@
<h2>数据保留</h2>
<p>设置与 API Key 保留在设备上,直至卸载或重置。识别结果写入你正在使用的宿主 App,OSGKeyboard 不会长期存储。</p>
<p><strong>语音历史</strong> — 成功的转写可能保存在主 App「历史」页,仅供本机查看,不会上传,可随时在历史页清空或通过重置设置清除。</p>
<h2>联系</h2>
<p>问题反馈:<a href="https://github.com/hkgood/OSGKeyboard">github.com/hkgood/OSGKeyboard</a></p>
+2
View File
@@ -46,6 +46,7 @@
<h2>Data retention</h2>
<p>Settings and API keys remain on your device until you delete the app or reset settings. Transcription results are passed to the host app you are typing in and are not stored long-term by OSGKeyboard.</p>
<p><strong>Voice history</strong> — successful transcripts may be saved locally in the main apps History tab for your convenience. This history stays on your device only, is never uploaded, and can be cleared at any time from History or by resetting settings.</p>
<h2>Contact</h2>
<p>Questions: open an issue at <a href="https://github.com/hkgood/OSGKeyboard">github.com/hkgood/OSGKeyboard</a>.</p>
@@ -82,6 +83,7 @@
<h2>数据保留</h2>
<p>设置与 API Key 保留在设备上,直至卸载或重置。识别结果写入你正在使用的宿主 App,OSGKeyboard 不会长期存储。</p>
<p><strong>语音历史</strong> — 成功的转写可能保存在主 App「历史」页,仅供本机查看,不会上传,可随时在历史页清空或通过重置设置清除。</p>
<h2>联系</h2>
<p>问题反馈:<a href="https://github.com/hkgood/OSGKeyboard">github.com/hkgood/OSGKeyboard</a></p>
+2
View File
@@ -65,6 +65,7 @@ targets:
- path: OSGKeyboard/en.lproj
- path: OSGKeyboard/zh-Hans.lproj
- path: OSGKeyboard/Resources/Fonts/MaterialIcons-Regular.ttf
- path: OSGKeyboard/PrivacyInfo.xcprivacy
info:
path: OSGKeyboard/Info.plist
properties:
@@ -91,6 +92,7 @@ targets:
- audio
NSAppTransportSecurity:
NSAllowsArbitraryLoads: false
ITSAppUsesNonExemptEncryption: false
CFBundleURLTypes:
- CFBundleURLName: com.osgkeyboard.ios.settings
CFBundleURLSchemes: