Repo // Introducing PhraseEditorUI and its delegate protocol.
This commit is contained in:
parent
5dbbb0fcfa
commit
9f72ecc88a
|
@ -0,0 +1,30 @@
|
||||||
|
// swift-tools-version:5.3
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "PhraseEditorUI",
|
||||||
|
platforms: [
|
||||||
|
.macOS(.v10_11)
|
||||||
|
],
|
||||||
|
products: [
|
||||||
|
.library(
|
||||||
|
name: "PhraseEditorUI",
|
||||||
|
targets: ["PhraseEditorUI"]
|
||||||
|
)
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
.package(path: "../vChewing_LangModelAssembly"),
|
||||||
|
.package(path: "../vChewing_Shared"),
|
||||||
|
.package(path: "../ShapsBenkau_SwiftUIBackports"),
|
||||||
|
],
|
||||||
|
targets: [
|
||||||
|
.target(
|
||||||
|
name: "PhraseEditorUI",
|
||||||
|
dependencies: [
|
||||||
|
.product(name: "LangModelAssembly", package: "vChewing_LangModelAssembly"),
|
||||||
|
.product(name: "SwiftUIBackports", package: "ShapsBenkau_SwiftUIBackports"),
|
||||||
|
.product(name: "Shared", package: "vChewing_Shared"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
|
@ -0,0 +1,13 @@
|
||||||
|
# PhraseEditorUI
|
||||||
|
|
||||||
|
威注音語彙編輯器。
|
||||||
|
|
||||||
|
```
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
```
|
|
@ -0,0 +1,21 @@
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import LangModelAssembly
|
||||||
|
import Shared
|
||||||
|
|
||||||
|
public protocol PhraseEditorDelegate {
|
||||||
|
var currentInputMode: Shared.InputMode { get }
|
||||||
|
func retrieveData(mode: Shared.InputMode, type: vChewingLM.ReplacableUserDataType) -> String
|
||||||
|
@discardableResult func saveData(mode: Shared.InputMode, type: vChewingLM.ReplacableUserDataType, data: String)
|
||||||
|
-> String
|
||||||
|
func checkIfUserPhraseExist(userPhrase: String, mode: Shared.InputMode, key unigramKey: String) -> Bool
|
||||||
|
func consolidate(text strProcessed: inout String, pragma shouldCheckPragma: Bool)
|
||||||
|
func openPhraseFile(mode: Shared.InputMode, type: vChewingLM.ReplacableUserDataType, app: String)
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
// (c) 2021 and onwards The vChewing Project (MIT-NTL License).
|
||||||
|
// ====================
|
||||||
|
// This code is released under the MIT license (SPDX-License-Identifier: MIT)
|
||||||
|
// ... with NTL restriction stating that:
|
||||||
|
// No trademark license is granted to use the trade names, trademarks, service
|
||||||
|
// marks, or product names of Contributor, except as required to fulfill notice
|
||||||
|
// requirements defined in MIT License.
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import LangModelAssembly
|
||||||
|
import Shared
|
||||||
|
import SwiftExtension
|
||||||
|
import SwiftUI
|
||||||
|
import SwiftUIBackports
|
||||||
|
|
||||||
|
private let loc: String =
|
||||||
|
(UserDefaults.standard.array(forKey: UserDef.kAppleLanguages.rawValue) as? [String] ?? ["auto"])[0]
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
extension VwrPhraseEditorUI {
|
||||||
|
@Backport.AppStorage("PhraseEditorAutoReloadExternalModifications")
|
||||||
|
private static var autoReloadExternalModifications: Bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
public struct VwrPhraseEditorUI: View {
|
||||||
|
static var txtContentStorage: String = NSLocalizedString(
|
||||||
|
"Please select Simplified / Traditional Chinese mode above.", comment: ""
|
||||||
|
)
|
||||||
|
@Binding public var txtContent: String
|
||||||
|
@ObservedObject public var fileChangeIndicator = FileObserveProject.shared
|
||||||
|
@State private var selAutoReloadExternalModifications: Bool = UserDefaults.standard.bool(
|
||||||
|
forKey: UserDef.kPhraseEditorAutoReloadExternalModifications.rawValue)
|
||||||
|
@State var lblAddPhraseTag1 = UITerms.AddPhrases.locPhrase.localized.0
|
||||||
|
@State var lblAddPhraseTag2 = UITerms.AddPhrases.locReadingOrStroke.localized.0
|
||||||
|
@State var lblAddPhraseTag3 = UITerms.AddPhrases.locWeight.localized.0
|
||||||
|
@State var lblAddPhraseTag4 = UITerms.AddPhrases.locComment.localized.0
|
||||||
|
@State var txtAddPhraseField1 = ""
|
||||||
|
@State var txtAddPhraseField2 = ""
|
||||||
|
@State var txtAddPhraseField3 = ""
|
||||||
|
@State var txtAddPhraseField4 = ""
|
||||||
|
@State public var selInputMode: Shared.InputMode = .imeModeNULL
|
||||||
|
@State public var selUserDataType: vChewingLM.ReplacableUserDataType = .thePhrases
|
||||||
|
@State private var isLoading = false
|
||||||
|
@State private var isSaved = false
|
||||||
|
@State private var redrawTrigger = false
|
||||||
|
|
||||||
|
public var currentIMEInputMode: Shared.InputMode {
|
||||||
|
delegate?.currentInputMode ?? selInputMode
|
||||||
|
}
|
||||||
|
|
||||||
|
public var delegate: PhraseEditorDelegate? {
|
||||||
|
didSet {
|
||||||
|
guard let delegate = delegate else { return }
|
||||||
|
selInputMode = delegate.currentInputMode
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
public init(delegate theDelegate: PhraseEditorDelegate? = nil) {
|
||||||
|
_txtContent = .init(
|
||||||
|
get: { Self.txtContentStorage },
|
||||||
|
set: { newValue, _ in
|
||||||
|
Self.txtContentStorage.removeAll()
|
||||||
|
Self.txtContentStorage.append(newValue)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
guard let theDelegate = theDelegate else { return }
|
||||||
|
defer { delegate = theDelegate }
|
||||||
|
}
|
||||||
|
|
||||||
|
public func update() {
|
||||||
|
guard let delegate = delegate else { return }
|
||||||
|
updateLabels()
|
||||||
|
clearAllFields()
|
||||||
|
isLoading = true
|
||||||
|
txtContent = NSLocalizedString("Loading…", comment: "")
|
||||||
|
redrawTrigger.toggle()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
txtContent = delegate.retrieveData(mode: selInputMode, type: selUserDataType)
|
||||||
|
redrawTrigger.toggle()
|
||||||
|
isSaved = true
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func updateLabels() {
|
||||||
|
clearAllFields()
|
||||||
|
switch selUserDataType {
|
||||||
|
case .thePhrases:
|
||||||
|
lblAddPhraseTag1 = UITerms.AddPhrases.locPhrase.localized.0
|
||||||
|
lblAddPhraseTag2 = UITerms.AddPhrases.locReadingOrStroke.localized.0
|
||||||
|
lblAddPhraseTag3 = UITerms.AddPhrases.locWeight.localized.0
|
||||||
|
lblAddPhraseTag4 = UITerms.AddPhrases.locComment.localized.0
|
||||||
|
case .theFilter:
|
||||||
|
lblAddPhraseTag1 = UITerms.AddPhrases.locPhrase.localized.0
|
||||||
|
lblAddPhraseTag2 = UITerms.AddPhrases.locReadingOrStroke.localized.0
|
||||||
|
lblAddPhraseTag3 = ""
|
||||||
|
lblAddPhraseTag4 = UITerms.AddPhrases.locComment.localized.0
|
||||||
|
case .theReplacements:
|
||||||
|
lblAddPhraseTag1 = UITerms.AddPhrases.locReplaceTo.localized.0
|
||||||
|
lblAddPhraseTag2 = UITerms.AddPhrases.locReplaceTo.localized.1
|
||||||
|
lblAddPhraseTag3 = ""
|
||||||
|
lblAddPhraseTag4 = UITerms.AddPhrases.locComment.localized.0
|
||||||
|
case .theAssociates:
|
||||||
|
lblAddPhraseTag1 = UITerms.AddPhrases.locInitial.localized.0
|
||||||
|
lblAddPhraseTag2 = {
|
||||||
|
let result = UITerms.AddPhrases.locPhrase.localized.0
|
||||||
|
return (result == "Phrase") ? "Phrases" : result
|
||||||
|
}()
|
||||||
|
lblAddPhraseTag3 = ""
|
||||||
|
lblAddPhraseTag4 = ""
|
||||||
|
case .theSymbols:
|
||||||
|
lblAddPhraseTag1 = UITerms.AddPhrases.locPhrase.localized.0
|
||||||
|
lblAddPhraseTag2 = UITerms.AddPhrases.locReadingOrStroke.localized.0
|
||||||
|
lblAddPhraseTag3 = ""
|
||||||
|
lblAddPhraseTag4 = UITerms.AddPhrases.locComment.localized.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func insertEntry() {
|
||||||
|
txtAddPhraseField1.removeAll { " \t\n\r".contains($0) }
|
||||||
|
if selUserDataType != .theAssociates {
|
||||||
|
txtAddPhraseField2.regReplace(pattern: #"( +| +| +|\t+)+"#, replaceWith: "-")
|
||||||
|
}
|
||||||
|
txtAddPhraseField2.removeAll {
|
||||||
|
selUserDataType == .theAssociates ? "\n\r".contains($0) : " \t\n\r".contains($0)
|
||||||
|
}
|
||||||
|
txtAddPhraseField3.removeAll { !"0123456789.-".contains($0) }
|
||||||
|
txtAddPhraseField4.removeAll { "\n\r".contains($0) }
|
||||||
|
guard !txtAddPhraseField1.isEmpty, !txtAddPhraseField2.isEmpty else { return }
|
||||||
|
var arrResult: [String] = [txtAddPhraseField1, txtAddPhraseField2]
|
||||||
|
if let weightVal = Double(txtAddPhraseField3), weightVal < 0 {
|
||||||
|
arrResult.append(weightVal.description)
|
||||||
|
}
|
||||||
|
if !txtAddPhraseField4.isEmpty { arrResult.append("#" + txtAddPhraseField4) }
|
||||||
|
if let delegate = delegate,
|
||||||
|
delegate.checkIfUserPhraseExist(
|
||||||
|
userPhrase: txtAddPhraseField1, mode: selInputMode, key: txtAddPhraseField2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
arrResult.append("\t#𝙾𝚟𝚎𝚛𝚛𝚒𝚍𝚎")
|
||||||
|
}
|
||||||
|
if let lastChar = txtContent.last, !"\n".contains(lastChar) {
|
||||||
|
arrResult.insert("\n", at: 0)
|
||||||
|
}
|
||||||
|
txtContent.append(arrResult.joined(separator: " ") + "\n")
|
||||||
|
isSaved = false
|
||||||
|
clearAllFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func clearAllFields() {
|
||||||
|
txtAddPhraseField1 = ""
|
||||||
|
txtAddPhraseField2 = ""
|
||||||
|
txtAddPhraseField3 = ""
|
||||||
|
txtAddPhraseField4 = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dropDownMenuDidChange() {
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveAndReload() {
|
||||||
|
guard let delegate = delegate, selInputMode != .imeModeNULL, !isSaved else { return }
|
||||||
|
let toSave = txtContent
|
||||||
|
isLoading = true
|
||||||
|
txtContent = NSLocalizedString("Loading…", comment: "")
|
||||||
|
redrawTrigger.toggle()
|
||||||
|
let newResult = delegate.saveData(mode: selInputMode, type: selUserDataType, data: toSave)
|
||||||
|
txtContent = newResult
|
||||||
|
redrawTrigger.toggle()
|
||||||
|
isLoading = false
|
||||||
|
isSaved = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func consolidate() {
|
||||||
|
guard let delegate = delegate, selInputMode != .imeModeNULL else { return }
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
isLoading = true
|
||||||
|
delegate.consolidate(text: &txtContent, pragma: false) // 強制整理
|
||||||
|
isLoading = false
|
||||||
|
isSaved = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func callExternalAppToOpenPhraseFile() {
|
||||||
|
delegate?.openPhraseFile(mode: selInputMode, type: selUserDataType, app: "Finder")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Main View.
|
||||||
|
|
||||||
|
public var body: some View {
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
HStack {
|
||||||
|
Picker("", selection: $selInputMode.onChange { dropDownMenuDidChange() }) {
|
||||||
|
switch currentIMEInputMode {
|
||||||
|
case .imeModeCHS:
|
||||||
|
Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS)
|
||||||
|
Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT)
|
||||||
|
case .imeModeCHT:
|
||||||
|
Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT)
|
||||||
|
Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS)
|
||||||
|
case .imeModeNULL:
|
||||||
|
Text(Shared.InputMode.imeModeNULL.localizedDescription).tag(Shared.InputMode.imeModeNULL)
|
||||||
|
if loc.contains("Hans") {
|
||||||
|
Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS)
|
||||||
|
Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT)
|
||||||
|
} else {
|
||||||
|
Text(Shared.InputMode.imeModeCHT.localizedDescription).tag(Shared.InputMode.imeModeCHT)
|
||||||
|
Text(Shared.InputMode.imeModeCHS.localizedDescription).tag(Shared.InputMode.imeModeCHS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.labelsHidden()
|
||||||
|
Picker("", selection: $selUserDataType.onChange { dropDownMenuDidChange() }) {
|
||||||
|
Text(vChewingLM.ReplacableUserDataType.thePhrases.localizedDescription).tag(
|
||||||
|
vChewingLM.ReplacableUserDataType.thePhrases)
|
||||||
|
Text(vChewingLM.ReplacableUserDataType.theFilter.localizedDescription).tag(
|
||||||
|
vChewingLM.ReplacableUserDataType.theFilter)
|
||||||
|
Text(vChewingLM.ReplacableUserDataType.theReplacements.localizedDescription).tag(
|
||||||
|
vChewingLM.ReplacableUserDataType.theReplacements)
|
||||||
|
Text(vChewingLM.ReplacableUserDataType.theAssociates.localizedDescription).tag(
|
||||||
|
vChewingLM.ReplacableUserDataType.theAssociates)
|
||||||
|
Text(vChewingLM.ReplacableUserDataType.theSymbols.localizedDescription).tag(
|
||||||
|
vChewingLM.ReplacableUserDataType.theSymbols)
|
||||||
|
}
|
||||||
|
.labelsHidden()
|
||||||
|
Button("Reload") {
|
||||||
|
DispatchQueue.main.async { update() }
|
||||||
|
}.disabled(selInputMode == .imeModeNULL || isLoading)
|
||||||
|
Button("Consolidate") {
|
||||||
|
consolidate()
|
||||||
|
}.disabled(selInputMode == .imeModeNULL || isLoading)
|
||||||
|
if #available(macOS 11.0, *) {
|
||||||
|
Button("Save") {
|
||||||
|
DispatchQueue.main.async { saveAndReload() }
|
||||||
|
}.keyboardShortcut("s", modifiers: [.command])
|
||||||
|
.disabled(isSaved || delegate == nil)
|
||||||
|
} else {
|
||||||
|
Button("Save") {
|
||||||
|
DispatchQueue.main.async { saveAndReload() }
|
||||||
|
}.disabled(isSaved || delegate == nil)
|
||||||
|
}
|
||||||
|
Button("...") {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
saveAndReload()
|
||||||
|
callExternalAppToOpenPhraseFile()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextEditorEX(text: $txtContent.onChange { isSaved = false })
|
||||||
|
.disabled(selInputMode == .imeModeNULL || isLoading)
|
||||||
|
.frame(minWidth: 320, minHeight: 240)
|
||||||
|
.backport.onChange(of: fileChangeIndicator.id) { _ in
|
||||||
|
if Self.autoReloadExternalModifications { update() }
|
||||||
|
}
|
||||||
|
|
||||||
|
VStack(spacing: 4) {
|
||||||
|
if selUserDataType != .theAssociates {
|
||||||
|
HStack {
|
||||||
|
TextField(lblAddPhraseTag4, text: $txtAddPhraseField4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HStack {
|
||||||
|
TextField(lblAddPhraseTag1, text: $txtAddPhraseField1)
|
||||||
|
TextField(lblAddPhraseTag2, text: $txtAddPhraseField2)
|
||||||
|
if selUserDataType == .thePhrases {
|
||||||
|
TextField(
|
||||||
|
lblAddPhraseTag3,
|
||||||
|
text: $txtAddPhraseField3.onChange {
|
||||||
|
guard let weightVal = Double(txtAddPhraseField3) else { return }
|
||||||
|
if weightVal > 0 { txtAddPhraseField3 = "" }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Button(UITerms.AddPhrases.locAdd.localized.0) {
|
||||||
|
DispatchQueue.main.async { insertEntry() }
|
||||||
|
}.disabled(txtAddPhraseField1.isEmpty || txtAddPhraseField2.isEmpty)
|
||||||
|
}
|
||||||
|
}.disabled(selInputMode == Shared.InputMode.imeModeNULL || isLoading)
|
||||||
|
HStack {
|
||||||
|
if #available(macOS 12, *) {
|
||||||
|
Toggle(
|
||||||
|
LocalizedStringKey("This editor only: Auto-reload modifications happened outside of this editor"),
|
||||||
|
isOn: $selAutoReloadExternalModifications.onChange {
|
||||||
|
Self.autoReloadExternalModifications = selAutoReloadExternalModifications
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.controlSize(.small)
|
||||||
|
} else {
|
||||||
|
Text("Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations.")
|
||||||
|
.font(.system(size: 11.0)).foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
}.onDisappear {
|
||||||
|
selInputMode = .imeModeNULL
|
||||||
|
selUserDataType = .thePhrases
|
||||||
|
txtContent = NSLocalizedString("Please select Simplified / Traditional Chinese mode above.", comment: "")
|
||||||
|
redrawTrigger.toggle()
|
||||||
|
}.onAppear {
|
||||||
|
guard let delegate = delegate else { return }
|
||||||
|
selInputMode = delegate.currentInputMode
|
||||||
|
update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VwrPhraseEditorUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension vChewingLM.ReplacableUserDataType {
|
||||||
|
public var localizedDescription: String { NSLocalizedString(rawValue, comment: "") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum UITerms {
|
||||||
|
fileprivate enum AddPhrases: String {
|
||||||
|
case locPhrase = "Phrase"
|
||||||
|
case locReadingOrStroke = "Reading/Stroke"
|
||||||
|
case locWeight = "Weight"
|
||||||
|
case locComment = "Comment"
|
||||||
|
case locReplaceTo = "Replace to"
|
||||||
|
case locAdd = "Add"
|
||||||
|
case locInitial = "Initial"
|
||||||
|
|
||||||
|
var localized: (String, String) {
|
||||||
|
if self == .locAdd {
|
||||||
|
return loc.contains("zh") ? ("添入", "") : loc.contains("ja") ? ("記入", "") : ("Add", "")
|
||||||
|
}
|
||||||
|
let rawArray = NSLocalizedString(self.rawValue, comment: "").components(separatedBy: " ")
|
||||||
|
if rawArray.isEmpty { return ("N/A", "N/A") }
|
||||||
|
let val1: String = rawArray[0]
|
||||||
|
let val2: String = (rawArray.count >= 2) ? rawArray[1] : ""
|
||||||
|
return (val1, val2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,24 @@
|
||||||
"vChewing" = "vChewing";
|
"vChewing" = "vChewing";
|
||||||
|
"Initial" = "Initial";
|
||||||
|
"Phrase" = "Phrase";
|
||||||
|
"Reading/Stroke" = "Reading/Stroke";
|
||||||
|
"Weight" = "Weight";
|
||||||
|
"Comment" = "Comment";
|
||||||
|
"Replace to" = "Replace to";
|
||||||
|
"Save" = "Save";
|
||||||
|
"Phrase Editor" = "Phrase Editor";
|
||||||
|
"thePhrases" = "Phrases";
|
||||||
|
"theFilter" = "Filter";
|
||||||
|
"theReplacements" = "Replacements";
|
||||||
|
"theAssociates" = "Associates";
|
||||||
|
"theSymbols" = "Symbols";
|
||||||
|
"Please select…" = "Please select…";
|
||||||
|
"Loading…" = "Loading…";
|
||||||
|
"Consolidate" = "Consolidate";
|
||||||
|
"Reload" = "Reload";
|
||||||
|
"Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations." = "Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations.";
|
||||||
|
"This editor only: Auto-reload modifications happened outside of this editor" = "This editor only: Auto-reload modifications happened outside of this editor";
|
||||||
|
"Please select Simplified / Traditional Chinese mode above." = "Please select Simplified / Traditional Chinese mode above.";
|
||||||
"⚠︎ Failed from boosting a candidate." = "⚠︎ Failed from boosting a candidate.";
|
"⚠︎ Failed from boosting a candidate." = "⚠︎ Failed from boosting a candidate.";
|
||||||
"⚠︎ Failed from nerfing a candidate." = "⚠︎ Failed from nerfing a candidate.";
|
"⚠︎ Failed from nerfing a candidate." = "⚠︎ Failed from nerfing a candidate.";
|
||||||
"⚠︎ Failed from filtering a candidate." = "⚠︎ Failed from filtering a candidate.";
|
"⚠︎ Failed from filtering a candidate." = "⚠︎ Failed from filtering a candidate.";
|
||||||
|
|
|
@ -1,4 +1,24 @@
|
||||||
"vChewing" = "vChewing";
|
"vChewing" = "vChewing";
|
||||||
|
"Initial" = "Initial";
|
||||||
|
"Phrase" = "Phrase";
|
||||||
|
"Reading/Stroke" = "Reading/Stroke";
|
||||||
|
"Weight" = "Weight";
|
||||||
|
"Comment" = "Comment";
|
||||||
|
"Replace to" = "Replace to";
|
||||||
|
"Save" = "Save";
|
||||||
|
"Phrase Editor" = "Phrase Editor";
|
||||||
|
"thePhrases" = "Phrases";
|
||||||
|
"theFilter" = "Filter";
|
||||||
|
"theReplacements" = "Replacements";
|
||||||
|
"theAssociates" = "Associates";
|
||||||
|
"theSymbols" = "Symbols";
|
||||||
|
"Please select…" = "Please select…";
|
||||||
|
"Loading…" = "Loading…";
|
||||||
|
"Consolidate" = "Consolidate";
|
||||||
|
"Reload" = "Reload";
|
||||||
|
"Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations." = "Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations.";
|
||||||
|
"This editor only: Auto-reload modifications happened outside of this editor" = "This editor only: Auto-reload modifications happened outside of this editor";
|
||||||
|
"Please select Simplified / Traditional Chinese mode above." = "Please select Simplified / Traditional Chinese mode above.";
|
||||||
"⚠︎ Failed from boosting a candidate." = "⚠︎ Failed from boosting a candidate.";
|
"⚠︎ Failed from boosting a candidate." = "⚠︎ Failed from boosting a candidate.";
|
||||||
"⚠︎ Failed from nerfing a candidate." = "⚠︎ Failed from nerfing a candidate.";
|
"⚠︎ Failed from nerfing a candidate." = "⚠︎ Failed from nerfing a candidate.";
|
||||||
"⚠︎ Failed from filtering a candidate." = "⚠︎ Failed from filtering a candidate.";
|
"⚠︎ Failed from filtering a candidate." = "⚠︎ Failed from filtering a candidate.";
|
||||||
|
|
|
@ -1,4 +1,24 @@
|
||||||
"vChewing" = "威注音入力アプリ";
|
"vChewing" = "威注音入力アプリ";
|
||||||
|
"Initial" = "頭文字";
|
||||||
|
"Phrase" = "語彙";
|
||||||
|
"Reading/Stroke" = "音読/筆画";
|
||||||
|
"Weight" = "優先度";
|
||||||
|
"Comment" = "メモ";
|
||||||
|
"Replace to" = "単語 →";
|
||||||
|
"Save" = "セーブ";
|
||||||
|
"Phrase Editor" = "辞書編集";
|
||||||
|
"thePhrases" = "ユーザー辞書";
|
||||||
|
"theFilter" = "排除リスト";
|
||||||
|
"theReplacements" = "言葉置換";
|
||||||
|
"theAssociates" = "連想語彙";
|
||||||
|
"theSymbols" = "絵文字&符号";
|
||||||
|
"Please select…" = "お選びを…";
|
||||||
|
"Loading…" = "読み込む中…";
|
||||||
|
"Consolidate" = "整理";
|
||||||
|
"Reload" = "再読込";
|
||||||
|
"Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations." = "システム API 制限のため、一部の機能は macOS 10.15 と macOS 11 で提供できません。";
|
||||||
|
"This editor only: Auto-reload modifications happened outside of this editor" = "このエディターの外部からの編集結果をこのエディターに自動的に読み込む";
|
||||||
|
"Please select Simplified / Traditional Chinese mode above." = "まずは上記のメニューで簡体/繁体中国語モードをお選びください。";
|
||||||
"⚠︎ Failed from boosting a candidate." = "⚠︎ 指定された文字候補の順位は最優先にできませんでした。";
|
"⚠︎ Failed from boosting a candidate." = "⚠︎ 指定された文字候補の順位は最優先にできませんでした。";
|
||||||
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 指定された文字候補の優先順位を下げることに失敗ししました。";
|
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 指定された文字候補の優先順位を下げることに失敗ししました。";
|
||||||
"⚠︎ Failed from filtering a candidate." = "⚠︎ 指定された文字候補は排除表に登録できませんでした。";
|
"⚠︎ Failed from filtering a candidate." = "⚠︎ 指定された文字候補は排除表に登録できませんでした。";
|
||||||
|
|
|
@ -1,4 +1,24 @@
|
||||||
"vChewing" = "威注音输入法";
|
"vChewing" = "威注音输入法";
|
||||||
|
"Initial" = "首字";
|
||||||
|
"Phrase" = "词语";
|
||||||
|
"Reading/Stroke" = "读音/字根";
|
||||||
|
"Weight" = "权重";
|
||||||
|
"Comment" = "注释";
|
||||||
|
"Replace to" = "置换 为";
|
||||||
|
"Save" = "存档";
|
||||||
|
"Phrase Editor" = "语汇编辑器";
|
||||||
|
"thePhrases" = "自订语汇";
|
||||||
|
"theFilter" = "滤除清单";
|
||||||
|
"theReplacements" = "语汇置换";
|
||||||
|
"theAssociates" = "联想字词";
|
||||||
|
"theSymbols" = "绘文字符号";
|
||||||
|
"Please select…" = "请选择…";
|
||||||
|
"Loading…" = "正在载入…";
|
||||||
|
"Consolidate" = "整理";
|
||||||
|
"Reload" = "重新载入";
|
||||||
|
"Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations." = "因系统 API 限制,个别功能无法对 macOS 10.15 和 macOS 11 提供。";
|
||||||
|
"This editor only: Auto-reload modifications happened outside of this editor" = "仅限该编辑器:自动读入来自该编辑器外部的档案内容修改";
|
||||||
|
"Please select Simplified / Traditional Chinese mode above." = "请先借由上方选单选择简繁模式。";
|
||||||
"⚠︎ Failed from boosting a candidate." = "⚠︎ 候选字词提权失败。";
|
"⚠︎ Failed from boosting a candidate." = "⚠︎ 候选字词提权失败。";
|
||||||
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 候选字词降权失败。";
|
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 候选字词降权失败。";
|
||||||
"⚠︎ Failed from filtering a candidate." = "⚠︎ 候选字词滤除失败。";
|
"⚠︎ Failed from filtering a candidate." = "⚠︎ 候选字词滤除失败。";
|
||||||
|
|
|
@ -1,4 +1,24 @@
|
||||||
"vChewing" = "威注音輸入法";
|
"vChewing" = "威注音輸入法";
|
||||||
|
"Initial" = "首字";
|
||||||
|
"Phrase" = "詞語";
|
||||||
|
"Reading/Stroke" = "讀音/字根";
|
||||||
|
"Weight" = "權重";
|
||||||
|
"Comment" = "註釋";
|
||||||
|
"Replace to" = "置換 為";
|
||||||
|
"Save" = "存檔";
|
||||||
|
"Phrase Editor" = "語彙編輯器";
|
||||||
|
"thePhrases" = "自訂語彙";
|
||||||
|
"theFilter" = "濾除清單";
|
||||||
|
"theReplacements" = "語彙置換";
|
||||||
|
"theAssociates" = "聯想字詞";
|
||||||
|
"theSymbols" = "繪文字符號";
|
||||||
|
"Please select…" = "請選擇…";
|
||||||
|
"Loading…" = "正在載入…";
|
||||||
|
"Consolidate" = "整理";
|
||||||
|
"Reload" = "重新載入";
|
||||||
|
"Some features are unavailable for macOS 10.15 and macOS 11 due to API limitations." = "因系統 API 限制,個別功能無法對 macOS 10.15 和 macOS 11 提供。";
|
||||||
|
"This editor only: Auto-reload modifications happened outside of this editor" = "僅限該編輯器:自動讀入來自該編輯器外部的檔案內容修改";
|
||||||
|
"Please select Simplified / Traditional Chinese mode above." = "請先藉由上方選單選擇繁簡模式。";
|
||||||
"⚠︎ Failed from boosting a candidate." = "⚠︎ 候選字詞提權失敗。";
|
"⚠︎ Failed from boosting a candidate." = "⚠︎ 候選字詞提權失敗。";
|
||||||
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 候選字詞降權失敗。";
|
"⚠︎ Failed from nerfing a candidate." = "⚠︎ 候選字詞降權失敗。";
|
||||||
"⚠︎ Failed from filtering a candidate." = "⚠︎ 候選字詞濾除失敗。";
|
"⚠︎ Failed from filtering a candidate." = "⚠︎ 候選字詞濾除失敗。";
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
5B660A8628F64A8800E5E4F6 /* SymbolMenuDefaultData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B660A8528F64A8800E5E4F6 /* SymbolMenuDefaultData.swift */; };
|
5B660A8628F64A8800E5E4F6 /* SymbolMenuDefaultData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B660A8528F64A8800E5E4F6 /* SymbolMenuDefaultData.swift */; };
|
||||||
5B6C141228A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */; };
|
5B6C141228A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */; };
|
||||||
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
5B73FB5E27B2BE1300E9BF49 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5B73FB6027B2BE1300E9BF49 /* InfoPlist.strings */; };
|
||||||
|
5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5B765F08293A253C00122315 /* PhraseEditorUI */; };
|
||||||
5B782EC4280C243C007276DE /* InputHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */; };
|
5B782EC4280C243C007276DE /* InputHandler_HandleCandidate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */; };
|
||||||
5B78EE0D28A562B4009456C1 /* VwrPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */; };
|
5B78EE0D28A562B4009456C1 /* VwrPrefPaneDevZone.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */; };
|
||||||
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
|
5B7BC4B027AFFBE800F66C24 /* frmPrefWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7BC4AE27AFFBE800F66C24 /* frmPrefWindow.xib */; };
|
||||||
|
@ -236,6 +237,7 @@
|
||||||
5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCtl_HandleEvent.swift; sourceTree = "<group>"; };
|
5B6C141128A9D4B30098ADF8 /* SessionCtl_HandleEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionCtl_HandleEvent.swift; sourceTree = "<group>"; };
|
||||||
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
5B73FB5427B2BD6900E9BF49 /* PhraseEditor-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "PhraseEditor-Info.plist"; path = "UserPhraseEditor/PhraseEditor-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||||
5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
5B73FB5F27B2BE1300E9BF49 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
5B765F07293A250000122315 /* vChewing_PhraseEditorUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = vChewing_PhraseEditorUI; path = Packages/vChewing_PhraseEditorUI; sourceTree = "<group>"; };
|
||||||
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
5B782EC3280C243C007276DE /* InputHandler_HandleCandidate.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; fileEncoding = 4; indentWidth = 2; lineEnding = 0; path = InputHandler_HandleCandidate.swift; sourceTree = "<group>"; tabWidth = 2; usesTabs = 0; };
|
||||||
5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VwrPrefPaneDevZone.swift; sourceTree = "<group>"; };
|
5B78EE0C28A562B4009456C1 /* VwrPrefPaneDevZone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VwrPrefPaneDevZone.swift; sourceTree = "<group>"; };
|
||||||
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; };
|
5B7BC4AF27AFFBE800F66C24 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/frmPrefWindow.xib; sourceTree = "<group>"; };
|
||||||
|
@ -385,6 +387,7 @@
|
||||||
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */,
|
5B98114828D6198700CBC605 /* PinyinPhonaConverter in Frameworks */,
|
||||||
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */,
|
5BFC63CF28D4ACA3004A77B7 /* LangModelAssembly in Frameworks */,
|
||||||
5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */,
|
5BC5E02128DDEFE00094E427 /* TooltipUI in Frameworks */,
|
||||||
|
5B765F09293A253C00122315 /* PhraseEditorUI in Frameworks */,
|
||||||
5BDB7A3928D4824A001AC277 /* BookmarkManager in Frameworks */,
|
5BDB7A3928D4824A001AC277 /* BookmarkManager in Frameworks */,
|
||||||
5B5C8ED828FC0EA9002C93A5 /* SSPreferences in Frameworks */,
|
5B5C8ED828FC0EA9002C93A5 /* SSPreferences in Frameworks */,
|
||||||
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */,
|
5B5A603028E81CC50001AE8D /* SwiftUIBackports in Frameworks */,
|
||||||
|
@ -622,6 +625,7 @@
|
||||||
5BFC63CD28D4AC98004A77B7 /* vChewing_LangModelAssembly */,
|
5BFC63CD28D4AC98004A77B7 /* vChewing_LangModelAssembly */,
|
||||||
5BDB7A3328D47587001AC277 /* vChewing_Megrez */,
|
5BDB7A3328D47587001AC277 /* vChewing_Megrez */,
|
||||||
5BC5E01C28DDE4270094E427 /* vChewing_NotifierUI */,
|
5BC5E01C28DDE4270094E427 /* vChewing_NotifierUI */,
|
||||||
|
5B765F07293A250000122315 /* vChewing_PhraseEditorUI */,
|
||||||
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */,
|
5B98114628D6198000CBC605 /* vChewing_PinyinPhonaConverter */,
|
||||||
5BC5E02228DE07250094E427 /* vChewing_PopupCompositionBuffer */,
|
5BC5E02228DE07250094E427 /* vChewing_PopupCompositionBuffer */,
|
||||||
5B963C9E28D5C14600DCEE88 /* vChewing_Shared */,
|
5B963C9E28D5C14600DCEE88 /* vChewing_Shared */,
|
||||||
|
@ -864,6 +868,7 @@
|
||||||
5BA8C30228DF0360004C5CC4 /* CandidateWindow */,
|
5BA8C30228DF0360004C5CC4 /* CandidateWindow */,
|
||||||
5B5A602F28E81CC50001AE8D /* SwiftUIBackports */,
|
5B5A602F28E81CC50001AE8D /* SwiftUIBackports */,
|
||||||
5B5C8ED728FC0EA9002C93A5 /* SSPreferences */,
|
5B5C8ED728FC0EA9002C93A5 /* SSPreferences */,
|
||||||
|
5B765F08293A253C00122315 /* PhraseEditorUI */,
|
||||||
);
|
);
|
||||||
productName = vChewing;
|
productName = vChewing;
|
||||||
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */;
|
productReference = 6A0D4EA215FC0D2D00ABF4B3 /* vChewing.app */;
|
||||||
|
@ -2034,6 +2039,10 @@
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = SSPreferences;
|
productName = SSPreferences;
|
||||||
};
|
};
|
||||||
|
5B765F08293A253C00122315 /* PhraseEditorUI */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
productName = PhraseEditorUI;
|
||||||
|
};
|
||||||
5B963C9C28D5BFB800DCEE88 /* CocoaExtension */ = {
|
5B963C9C28D5BFB800DCEE88 /* CocoaExtension */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
productName = CocoaExtension;
|
productName = CocoaExtension;
|
||||||
|
|
Loading…
Reference in New Issue