MainAssembly // + Candidate Service (Menu & Editor).

This commit is contained in:
ShikiSuen 2024-02-28 22:29:38 +08:00
parent dc79c629a1
commit 5eec7cd604
12 changed files with 541 additions and 8 deletions

View File

@ -200,7 +200,8 @@ public extension IMEState {
case .ofCandidates where cursor != marker: return data.attributedStringMarking(for: session)
case .ofCandidates where cursor == marker: break
case .ofAssociates: return data.attributedStringPlaceholder(for: session)
case .ofSymbolTable where displayedText.isEmpty: return data.attributedStringPlaceholder(for: session)
case .ofSymbolTable where displayedText.isEmpty || node.containsCandidateServices:
return data.attributedStringPlaceholder(for: session)
case .ofSymbolTable where !displayedText.isEmpty: break
default: break
}

View File

@ -94,6 +94,7 @@ extension InputHandler {
delegate.candidateSelectionConfirmedByInputHandler(at: ctlCandidate.highlightedIndex)
}
let highlightedCandidate = state.candidates[ctlCandidate.highlightedIndex] //
if let keyCodeType = KeyCode(rawValue: input.keyCode) {
switch keyCodeType {
case .kLineFeed, .kCarriageReturn:
@ -101,7 +102,6 @@ extension InputHandler {
delegate.switchState(IMEState.ofAbortion())
return true
}
let highlightedCandidate = state.candidates[ctlCandidate.highlightedIndex] //
var handleAssociates = !prefs.useSCPCTypingMode && prefs.associatedPhrasesEnabled //
handleAssociates = handleAssociates && compositor.cursor == compositor.length //
confirmHighlightedCandidate()
@ -336,9 +336,17 @@ extension InputHandler {
// MARK: - Flipping pages by using symbol menu keys (when they are not occupied).
if input.isSymbolMenuPhysicalKey {
let candidateTextServiceMenuRunning = state.node.containsCandidateServices && state.type == .ofSymbolTable
switch input.commonKeyModifierFlags {
case .shift, [],
.option where state.type != .ofSymbolTable:
.option where !candidateTextServiceMenuRunning:
if !candidateTextServiceMenuRunning {
let handled = handleServiceMenuInitiation(
candidateText: highlightedCandidate.value,
reading: highlightedCandidate.keyArray
)
if handled { return true }
}
var updated = true
let reverseTrigger = input.isShiftHold || input.isOptionHold
updated = reverseTrigger ? ctlCandidate.showPreviousLine() : ctlCandidate.showNextLine()

View File

@ -898,6 +898,20 @@ extension InputHandler {
}
}
// MARK: - (Service Menu)
func handleServiceMenuInitiation(candidateText: String, reading: [String]) -> Bool {
guard let delegate = delegate, delegate.state.type != .ofDeactivated else { return false }
guard !candidateText.isEmpty else { return false }
let rootNode = CandidateTextService.getCurrentServiceMenu(candidate: candidateText, reading: reading)
guard let rootNode = rootNode else { return false }
// commit buffer ESC
let textToCommit = generateStateOfInputting(sansReading: true).displayedText
delegate.switchState(IMEState.ofCommitting(textToCommit: textToCommit))
delegate.switchState(IMEState.ofSymbolTable(node: rootNode))
return true
}
// MARK: - Caps Lock Caps Lock and Alphanumerical mode
/// CapsLock

View File

@ -0,0 +1,63 @@
// (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 AppKit
import Foundation
public class CtlServiceMenuEditor: NSWindowController {
let viewController = VwrServiceMenuEditor()
public static var shared: CtlServiceMenuEditor?
public init() {
super.init(
window: .init(
contentRect: CGRect(x: 401, y: 295, width: 770, height: 335),
styleMask: [.titled, .closable, .miniaturizable],
backing: .buffered,
defer: true
)
)
viewController.windowController = self
viewController.loadView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
public static func show() {
if shared == nil {
shared = CtlServiceMenuEditor()
}
guard let shared = shared, let sharedWindow = shared.window else { return }
if !sharedWindow.isVisible {
shared.windowDidLoad()
}
sharedWindow.setPosition(vertical: .center, horizontal: .right, padding: 20)
sharedWindow.orderFrontRegardless() //
sharedWindow.title = "Service Menu Editor".localized
sharedWindow.level = .statusBar
if #available(macOS 10.10, *) {
sharedWindow.titlebarAppearsTransparent = true
}
shared.showWindow(shared)
NSApp.popup()
}
override public func windowDidLoad() {
super.windowDidLoad()
let view = viewController.view
window?.contentView = view
if let window = window {
var frame = window.frame
frame.size = view.fittingSize
window.setFrame(frame, display: true)
}
window?.setPosition(vertical: .center, horizontal: .right, padding: 20)
}
}

View File

@ -0,0 +1,368 @@
// (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 AppKit
import CocoaExtension
import Foundation
import Shared
public class VwrServiceMenuEditor: NSViewController {
let windowWidth: CGFloat = 770
let contentWidth: CGFloat = 750
let tableHeight: CGFloat = 230
lazy var tblServices: NSTableView = .init()
lazy var btnShowInstructions = NSButton("How to Fill", target: self, action: #selector(btnShowInstructionsClicked(_:)))
lazy var btnAddService = NSFileDragRetrieverButton(
"Add Service",
target: self,
action: #selector(btnAddServiceClicked(_:)),
postDrag: handleDrag
)
lazy var btnRemoveService = NSButton("Remove Selected", target: self, action: #selector(btnRemoveServiceClicked(_:)))
lazy var btnResetService = NSButton("Reset Default", target: self, action: #selector(btnResetServiceClicked(_:)))
lazy var btnCopyAllToClipboard = NSButton("Copy All to Clipboard", target: self, action: #selector(btnCopyAllToClipboardClicked(_:)))
lazy var tableColumn1Cell = NSTextFieldCell()
lazy var tableColumn1 = NSTableColumn()
lazy var tableColumn2Cell = NSTextFieldCell()
lazy var tableColumn2 = NSTableColumn()
var windowController: NSWindowController?
public convenience init(windowController: NSWindowController? = nil) {
self.init()
self.windowController = windowController
}
override public func loadView() {
tblServices.reloadData()
view = body ?? .init()
(view as? NSStackView)?.alignment = .centerX
view.makeSimpleConstraint(.width, relation: .equal, value: windowWidth)
btnRemoveService.keyEquivalent = .init(NSEvent.SpecialKey.delete.unicodeScalar)
}
var body: NSView? {
NSStackView.build(.vertical, insets: .new(all: 14)) {
NSStackView.build(.horizontal) {
btnAddService
btnRemoveService
btnCopyAllToClipboard
btnShowInstructions
NSView()
btnResetService
}
makeScrollableTable()
.makeSimpleConstraint(.height, relation: .equal, value: tableHeight)
NSStackView.build(.horizontal) {
let descriptionWidth = contentWidth - 10
NSStackView.build(.vertical) {
let strDescription = "i18n:CandidateServiceMenuEditor.description"
strDescription.makeNSLabel(descriptive: true, fixWidth: descriptionWidth)
.makeSimpleConstraint(.width, relation: .greaterThanOrEqual, value: descriptionWidth)
NSView()
}
}
}
}
func makeScrollableTable() -> NSScrollView {
let scrollContainer = NSScrollView()
scrollContainer.scrollerStyle = .legacy
scrollContainer.autohidesScrollers = true
scrollContainer.documentView = tblServices
scrollContainer.hasVerticalScroller = true
scrollContainer.hasHorizontalScroller = true
if #available(macOS 11.0, *) {
tblServices.style = .inset
}
tblServices.addTableColumn(tableColumn1)
tblServices.addTableColumn(tableColumn2)
// tblServices.headerView = nil
tblServices.delegate = self
tblServices.allowsExpansionToolTips = true
tblServices.allowsMultipleSelection = true
tblServices.autoresizingMask = [.width, .height]
tblServices.autosaveTableColumns = false
tblServices.backgroundColor = NSColor.controlBackgroundColor
tblServices.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle
tblServices.frame = CGRect(x: 0, y: 0, width: 728, height: tableHeight)
tblServices.gridColor = NSColor.clear
tblServices.intercellSpacing = CGSize(width: 15, height: 0)
tblServices.setContentHuggingPriority(.defaultHigh, for: .vertical)
tblServices.registerForDraggedTypes([.kUTTypeData, .kUTTypeFileURL])
tblServices.dataSource = self
tblServices.target = self
if #available(macOS 11.0, *) { tblServices.style = .inset }
tableColumn1.identifier = NSUserInterfaceItemIdentifier("colTitle")
tableColumn1.headerCell.title = "i18n:CandidateServiceMenuEditor.table.field.MenuTitle".localized
tableColumn1.maxWidth = 280
tableColumn1.minWidth = 200
tableColumn1.resizingMask = [.autoresizingMask, .userResizingMask]
tableColumn1.width = 200
tableColumn1.dataCell = tableColumn1Cell
tableColumn1Cell.font = NSFont.systemFont(ofSize: 13)
tableColumn1Cell.isEditable = true
tableColumn1Cell.isSelectable = true
tableColumn1Cell.lineBreakMode = .byTruncatingTail
tableColumn1Cell.stringValue = "Text Cell"
tableColumn1Cell.textColor = NSColor.controlTextColor
tableColumn2.identifier = NSUserInterfaceItemIdentifier("colValue")
tableColumn2.headerCell.title = "i18n:CandidateServiceMenuEditor.table.field.Value".localized
tableColumn2.maxWidth = 1000
tableColumn2.minWidth = 40
tableColumn2.resizingMask = [.autoresizingMask, .userResizingMask]
tableColumn2.width = 480
tableColumn2.dataCell = tableColumn2Cell
tableColumn2Cell.backgroundColor = NSColor.controlBackgroundColor
tableColumn2Cell.font = NSFont.systemFont(ofSize: 13)
tableColumn2Cell.isEditable = true
tableColumn2Cell.isSelectable = true
tableColumn2Cell.lineBreakMode = .byTruncatingTail
tableColumn2Cell.stringValue = "Text Cell"
tableColumn2Cell.textColor = NSColor.controlTextColor
return scrollContainer
}
}
// MARK: - UserDefaults Handlers.
public extension VwrServiceMenuEditor {
static var servicesList: [CandidateTextService] {
get {
PrefMgr.shared.candidateServiceMenuContents.parseIntoCandidateTextServiceStack()
}
set {
PrefMgr.shared.candidateServiceMenuContents = newValue.rawRepresentation
}
}
static func removeService(at index: Int) {
guard index < Self.servicesList.count else { return }
Self.servicesList.remove(at: index)
}
}
// MARK: - Common Operation Methods.
extension VwrServiceMenuEditor {
func refresh() {
tblServices.reloadData()
reassureButtonAvailability()
}
func reassureButtonAvailability() {
btnRemoveService.isEnabled = (0 ..< Self.servicesList.count).contains(
tblServices.selectedRow)
}
func handleDrag(_ givenURL: URL) {
guard let string = try? String(contentsOf: givenURL) else { return }
Self.servicesList.append(contentsOf: string.components(separatedBy: .newlines).parseIntoCandidateTextServiceStack())
refresh()
}
}
// MARK: - IBActions.
extension VwrServiceMenuEditor {
@IBAction func btnShowInstructionsClicked(_: Any) {
let strTitle = "How to Fill".localized
let strFillGuide = "i18n:CandidateServiceMenuEditor.formatGuide".localized
windowController?.window.callAlert(title: strTitle, text: strFillGuide)
}
@IBAction func btnResetServiceClicked(_: Any) {
PrefMgr.shared.candidateServiceMenuContents = PrefMgr.kDefaultCandidateServiceMenuItem
tblServices.reloadData()
}
@IBAction func btnCopyAllToClipboardClicked(_: Any) {
var resultArrayLines = [String]()
Self.servicesList.forEach { currentService in
resultArrayLines.append("\(currentService.key)\t\(currentService.definedValue)")
}
let result = resultArrayLines.joined(separator: "\n").appending("\n")
NSPasteboard.general.declareTypes([.string], owner: nil)
NSPasteboard.general.setString(result, forType: .string)
}
@IBAction func btnRemoveServiceClicked(_: Any) {
guard let minIndexSelected = tblServices.selectedRowIndexes.min() else { return }
if minIndexSelected >= Self.servicesList.count { return }
if minIndexSelected < 0 { return }
var isLastRow = false
tblServices.selectedRowIndexes.sorted().reversed().forEach { index in
isLastRow = {
if Self.servicesList.count < 2 { return false }
return minIndexSelected == Self.servicesList.count - 1
}()
if index < Self.servicesList.count {
Self.removeService(at: index)
}
}
if isLastRow {
tblServices.selectRowIndexes(.init(arrayLiteral: minIndexSelected - 1), byExtendingSelection: false)
}
tblServices.reloadData()
btnRemoveService.isEnabled = (0 ..< Self.servicesList.count).contains(minIndexSelected)
}
@IBAction func btnAddServiceClicked(_: Any) {
guard let window = windowController?.window else { return }
let alert = NSAlert()
alert.messageText = NSLocalizedString(
"i18n:CandidateServiceMenuEditor.prompt", comment: ""
)
alert.informativeText = NSLocalizedString(
"i18n:CandidateServiceMenuEditor.howToGetGuide", comment: ""
)
alert.addButton(withTitle: NSLocalizedString("OK", comment: ""))
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
let maxFloat = Double(Float.greatestFiniteMagnitude)
let scrollview = NSScrollView(frame: NSRect(x: 0, y: 0, width: 512, height: 200))
let contentSize = scrollview.contentSize
scrollview.borderType = .noBorder
scrollview.hasVerticalScroller = true
scrollview.hasHorizontalScroller = true
scrollview.horizontalScroller?.scrollerStyle = .legacy
scrollview.verticalScroller?.scrollerStyle = .legacy
scrollview.autoresizingMask = [.width, .height]
let theTextView = NSTextView(frame: NSRect(x: 0, y: 0, width: contentSize.width, height: contentSize.height))
scrollview.documentView = theTextView
theTextView.minSize = NSSize(width: 0.0, height: contentSize.height)
theTextView.maxSize = NSSize(width: maxFloat, height: maxFloat)
theTextView.isVerticallyResizable = true
theTextView.isHorizontallyResizable = false
theTextView.autoresizingMask = .width
theTextView.textContainer?.containerSize = NSSize(width: contentSize.width, height: maxFloat)
theTextView.textContainer?.widthTracksTextView = true
theTextView.enclosingScrollView?.hasHorizontalScroller = true
theTextView.isHorizontallyResizable = true
theTextView.autoresizingMask = [.width, .height]
theTextView.textContainer?.containerSize = NSSize(width: maxFloat, height: maxFloat)
theTextView.textContainer?.widthTracksTextView = false
theTextView.toolTip = "i18n:CandidateServiceMenuEditor.formatGuide".localized
alert.accessoryView = scrollview
alert.beginSheetModal(at: window) { result in
switch result {
case .alertFirstButtonReturn:
let rawLines = theTextView.textContainer?.textView?.string.components(separatedBy: .newlines) ?? []
self.tblServices.beginUpdates()
Self.servicesList.append(contentsOf: rawLines.parseIntoCandidateTextServiceStack())
self.tblServices.endUpdates()
default: return
}
}
}
}
// MARK: - TableView Extensions.
extension VwrServiceMenuEditor: NSTableViewDelegate, NSTableViewDataSource {
public func numberOfRows(in _: NSTableView) -> Int {
Self.servicesList.count
}
public func tableView(_: NSTableView, shouldEdit _: NSTableColumn?, row _: Int) -> Bool {
false
}
public func tableView(_: NSTableView, objectValueFor column: NSTableColumn?, row: Int) -> Any? {
defer {
self.btnRemoveService.isEnabled = (0 ..< Self.servicesList.count).contains(
self.tblServices.selectedRow)
}
guard row < Self.servicesList.count else { return "" }
if let column = column {
let colName = column.identifier.rawValue
switch colName {
case "colTitle": return Self.servicesList[row].key
case "colValue": return Self.servicesList[row].definedValue // TODO:
default: return ""
}
}
return Self.servicesList[row]
}
// MARK: Pasteboard Operations.
public func tableView(
_: NSTableView, pasteboardWriterForRow row: Int
) -> NSPasteboardWriting? {
let pasteboard = NSPasteboardItem()
pasteboard.setString(row.description, forType: .string)
return pasteboard
}
public func tableView(
_: NSTableView,
validateDrop _: NSDraggingInfo,
proposedRow _: Int,
proposedDropOperation _: NSTableView.DropOperation
) -> NSDragOperation {
.move
}
public func tableView(
_ tableView: NSTableView,
acceptDrop info: NSDraggingInfo,
row: Int,
dropOperation _: NSTableView.DropOperation
) -> Bool {
var oldIndexes = [Int]()
info.enumerateDraggingItems(
options: [],
for: tableView,
classes: [NSPasteboardItem.self],
searchOptions: [:]
) { dragItem, _, _ in
guard let pasteboardItem = dragItem.item as? NSPasteboardItem else { return }
guard let index = Int(pasteboardItem.string(forType: .string) ?? "NULL"), index >= 0 else { return }
oldIndexes.append(index)
}
var oldIndexOffset = 0
var newIndexOffset = 0
tableView.beginUpdates()
for oldIndex in oldIndexes {
if oldIndex < row {
let contentToMove = Self.servicesList[oldIndex + oldIndexOffset]
Self.servicesList.remove(at: oldIndex + oldIndexOffset)
Self.servicesList.insert(contentToMove, at: row - 1)
tableView.moveRow(at: oldIndex + oldIndexOffset, to: row - 1)
oldIndexOffset -= 1
} else {
let contentToMove = Self.servicesList[oldIndex]
Self.servicesList.remove(at: oldIndex)
Self.servicesList.insert(contentToMove, at: row + newIndexOffset)
tableView.moveRow(at: oldIndex, to: row + newIndexOffset)
newIndexOffset += 1
}
}
tableView.endUpdates()
reassureButtonAvailability()
return true
}
}
// MARK: - Preview.
@available(macOS 14.0, *)
#Preview(traits: .fixedLayout(width: 770, height: 335)) {
VwrServiceMenuEditor()
}

View File

@ -125,6 +125,8 @@ extension SessionCtl: CtlCandidateDelegate {
return shortened ? theEmoji : "\(theEmoji) " + NSLocalizedString("Quick Candidates", comment: "")
} else if PrefMgr.shared.cassetteEnabled {
return shortened ? "📼" : "📼 " + NSLocalizedString("CIN Cassette Mode", comment: "")
} else if state.type == .ofSymbolTable, state.node.containsCandidateServices {
return shortened ? "🌎" : "🌎 " + NSLocalizedString("Service Menu", comment: "")
}
return ""
}
@ -201,6 +203,22 @@ extension SessionCtl: CtlCandidateDelegate {
let node = state.node.members[index]
if !node.members.isEmpty {
switchState(IMEState.ofSymbolTable(node: node))
} else if let serviceNode = node.asServiceMenuNode {
switch serviceNode.service.value {
case let .url(theURL):
// Safari 使
NSWorkspace.shared.open(theURL)
case .selector:
if let response = serviceNode.service.responseFromSelector {
NSPasteboard.general.declareTypes([.string], owner: nil)
NSPasteboard.general.setString(response, forType: .string)
Notifier.notify(message: "i18n:candidateServiceMenu.selectorResponse.succeeded".localized)
} else {
callError("4DFDC487: Candidate Text Service Selector Responsiveness Failure.")
Notifier.notify(message: "i18n:candidateServiceMenu.selectorResponse.failed".localized)
}
}
switchState(IMEState.ofAbortion())
} else {
switchState(IMEState.ofCommitting(textToCommit: node.name))
}

View File

@ -79,13 +79,12 @@ public extension SessionCtl {
func showCandidates() {
guard client() != nil else { return }
updateVerticalTypingStatus()
isVerticalCandidateWindow = (isVerticalTyping || !PrefMgr.shared.useHorizontalCandidateList)
let isServiceMenu = state.type == .ofSymbolTable && state.node.containsCandidateServices
isVerticalCandidateWindow = isVerticalTyping || !PrefMgr.shared.useHorizontalCandidateList
isVerticalCandidateWindow = isVerticalCandidateWindow || isServiceMenu
/// IMK
let candidateLayout: NSUserInterfaceLayoutOrientation =
((isVerticalTyping || !PrefMgr.shared.useHorizontalCandidateList)
? .vertical
: .horizontal)
let candidateLayout: NSUserInterfaceLayoutOrientation = (isVerticalCandidateWindow ? .vertical : .horizontal)
let isInputtingWithCandidates = state.type == .ofInputting && state.isCandidateContainer
/// NSWindow()
@ -93,6 +92,8 @@ public extension SessionCtl {
candidateUI = CtlCandidateTDK(candidateLayout)
var singleLine = isVerticalTyping || PrefMgr.shared.candidateWindowShowOnlyOneLine
singleLine = singleLine || isInputtingWithCandidates
singleLine = singleLine || isServiceMenu
(candidateUI as? CtlCandidateTDK)?.maxLinesPerPage = singleLine ? 1 : 4
candidateUI?.candidateFont = Self.candidateFont(

View File

@ -129,6 +129,9 @@ extension SessionCtl {
NSMenu.Item(verbatim: "Client Manager".localized.withEllipsis)?
.act(#selector(showClientListMgr(_:)))
.nulled(silentMode)
NSMenu.Item(verbatim: "Service Menu Editor".localized.withEllipsis)?
.act(#selector(showServiceMenuEditor(_:)))
.alternated().nulled(silentMode)
NSMenu.Item("Check for Updates…")?
.act(#selector(checkForUpdate(_:)))
.nulled(silentMode)
@ -184,6 +187,11 @@ public extension SessionCtl {
NSApp.popup()
}
@objc func showServiceMenuEditor(_: Any? = nil) {
CtlServiceMenuEditor.show()
NSApp.popup()
}
@objc func toggleCassetteMode(_: Any? = nil) {
resetInputHandler(forceComposerCleanup: true)
if !PrefMgr.shared.cassetteEnabled, !LMMgr.checkCassettePathValidity(PrefMgr.shared.cassettePath) {

View File

@ -26,6 +26,7 @@
"About vChewing…" = "About vChewing…";
"Accept leading intonations in rare cases" = "Accept leading intonations in rare cases";
"Add Client" = "Add Client";
"Add Service" = "Add Service";
"Adjust candidate window location according to current node length" = "Adjust candidate window location according to current node length";
"All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file." = "All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file.";
"Allow backspace-editing miscomposed readings" = "Allow backspace-editing miscomposed readings";
@ -125,6 +126,7 @@
"Completely disable using Shift key to toggle alphanumerical mode" = "Completely disable using Shift key to toggle alphanumerical mode";
"Consolidate the context on confirming candidate selection" = "Consolidate the context on confirming candidate selection";
"Consolidate" = "Consolidate";
"Copy All to Clipboard" = "Copy All to Clipboard";
"Core Dict loading complete." = "Core Dict loading complete.";
"Currency Numeral Output" = "Currency Numeral Output";
"Cursor Selection:" = "Cursor Selection:";
@ -183,6 +185,7 @@
"Harden vertical punctuations during vertical typing (not recommended)" = "Harden vertical punctuations during vertical typing (not recommended)";
"Hold ⇧ to choose associates." = "Hold ⇧ to choose associates.";
"Horizontal" = "Horizontal";
"How to Fill" = "How to Fill";
"Hsu" = "Hsu";
"Hualuo Pinyin with Numeral Intonation" = "Hualuo Pinyin with Numeral Intonation";
"i18n:aboutWindow.ABOUT_APP_TITLE_FULL" = "About vChewing for macOS";
@ -195,6 +198,14 @@
"i18n:aboutWindow.OK_BUTTON" = "I Accept";
"i18n:aboutWindow.WEBSITE_BUTTON" = "Website";
"i18n:CandidateKey.ValidationError.AssignedForOtherPurposes" = "Candidates cannot have those keys who are assigned for other purposes.";
"i18n:candidateServiceMenu.selectorResponse.failed" = "Failed\nEither ObjC Selector mismatch or no result available.";
"i18n:candidateServiceMenu.selectorResponse.succeeded" = "Succeeded\nResults are available in Clipboard.";
"i18n:CandidateServiceMenuEditor.description" = "Candidate Service Menu can be called in Candidate Window by using “(Shift+)SymbolMenuKey”. The currently highlighted candidate will be used as the parameter for calling the Service Menu. Depending on what listed in the current window (Service Menu Editor), some services are for opening a designated web browser page to query the parameter, and some are for local-generating some computed results to your clipboard.\n\nP.S.: If no service is registered in the current window, then the above hotkey will behave as alternative page-flipping shortcuts.";
"i18n:CandidateServiceMenuEditor.formatGuide" = "Here are the formatting rules of the Service Metadata:\n\n1. One definition per line. Use “Tab” as delimiter to divide the two cells. The parameter “%s” written in the Menu Item Title will be replaced using the real parameter data when you are using the service. If the parameter consists of only one character, then its codepoint will be attached to the end of the Menu Item Title.\n\n2. The Defined Value begins with “@SEL:” or “@WEB:” or “@URL:”. Here are their differences:\n\n- “@SEL:” means that the remaining string should be the name of the specified Objective-C Selector. It must be ended with an ASCII colon. This method is to call some internal features in vChewing to retrieve locally-generated query results.\n\n- “@WEB:” and “@URL:” are exactly the same, indicating that the remaining string should be an URL for the web page to query the parameter. The parameter “%s” written in the URL will be replaced using the real parameter data when you are using the service.";
"i18n:CandidateServiceMenuEditor.howToGetGuide" = "You may hover your mouse pointer onto the editing area to see the pop-up data-filling instructions.";
"i18n:CandidateServiceMenuEditor.prompt" = "Please input the service metadata you want to register.";
"i18n:CandidateServiceMenuEditor.table.field.MenuTitle" = "Menu Item Title";
"i18n:CandidateServiceMenuEditor.table.field.Value" = "Defined Value";
"i18n:KimoDataImportError.connectionFailure.errMsg" = "Directly Import is not available due to an NSConnection failure.\n\n- Please enable Yahoo! KeyKey Input Method and run it prior to performing the current action. vChewing now have just triggered Yahoo! KeyKey Input Method to run, so you can retry the import immediately.\n\n- If this copy of vChewing is compiled by yourself, please double-check the Sandbox Entitlements you have modified.\n\n- If this mac is not an Intel Mac, then Rosetta 2 is required to run Yahoo! KeyKey Input Method.\n\n- If all of the above reasons are not met in this case, then the __objc_empty_cache API might be unavailable in the current system. This API is crucial in order to let Yahoo! KeyKey Input Method run. Please double check the Console.app in your system to see whether there are any crash reports related to Yahoo! KeyKey Input Method.";
"i18n:KimoDataImportError.fileHandlerFailure.errMsg" = "Failed in writing new user phrases data.";
"i18n:kimoImportButton.DragFileToHere" = "DRAG FILE TO HERE";
@ -319,12 +330,14 @@
"Reload" = "Reload";
"Remove Selected" = "Remove Selected";
"Replace to" = "Replace to";
"Reset Default" = "Reset Default";
"Reverse Lookup (Phonabets)" = "Reverse Lookup (Phonabets)";
"Save" = "Save";
"Secondary Pinyin with Numeral Intonation" = "Secondary Pinyin with Numeral Intonation";
"Security-harden the composition buffer for all clients" = "Security-harden the composition buffer for all clients";
"Seigyou" = "Seigyou (JinYei)";
"Selection Keys:" = "Selection Keys:";
"Service Menu Editor" = "Service Menu Editor";
"Share alphanumerical mode status across all clients" = "Share alphanumerical mode status across all clients";
"Shift+BackSpace:" = "Shift+BackSpace:";
"Shift+Letter:" = "Shift+Letter:";

View File

@ -26,6 +26,7 @@
"About vChewing…" = "威注音について…";
"Accept leading intonations in rare cases" = "まれな場合には、音調記号の優先入力を許容する";
"Add Client" = "入れる";
"Add Service" = "サービスを追加";
"Adjust candidate window location according to current node length" = "カーソル所在位置の単語の長さによって候補陳列ウィンドウの位置を調整";
"All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file." = "これをチェックしないと、全ての筆画は ASCII キーネームで入力緩衝列で表示してしまうことになる。CIN ファイルの「%keyname」というところで筆画の定義はできる。";
"Allow backspace-editing miscomposed readings" = "効かぬ音読みを BackSpace で再編集";
@ -125,6 +126,7 @@
"Completely disable using Shift key to toggle alphanumerical mode" = "Shift キーの英数入力モードの切り替え機能を徹底的に禁ず";
"Consolidate the context on confirming candidate selection" = "候補陳列ウィンドウで候補を選ぶ時に文脈を強固する";
"Consolidate" = "整理";
"Copy All to Clipboard" = "全てをクリップボードにコピー";
"Core Dict loading complete." = "核心辞書読込完了";
"Currency Numeral Output" = "数字大字変換";
"Cursor Selection:" = "カーソル候補呼出:";
@ -183,6 +185,7 @@
"Harden vertical punctuations during vertical typing (not recommended)" = "縦書きの時に、引用符・括弧などを強制的に縦書き文字と変換する(不推奨)";
"Hold ⇧ to choose associates." = "⇧を押しながら連想候補を選択。";
"Horizontal" = "横型陳列";
"How to Fill" = "記入方法";
"Hsu" = "許氏国音自然配列";
"Hualuo Pinyin with Numeral Intonation" = "中華ローマ弁音 (ローマ字+数字音調)";
"i18n:aboutWindow.ABOUT_APP_TITLE_FULL" = "macOS 版威注音入力アプリについて";
@ -195,6 +198,14 @@
"i18n:aboutWindow.OK_BUTTON" = "うむ";
"i18n:aboutWindow.WEBSITE_BUTTON" = "公式HP";
"i18n:CandidateKey.ValidationError.AssignedForOtherPurposes" = "他の用途に指定したキーは言選りには使えません。";
"i18n:candidateServiceMenu.selectorResponse.failed" = "請求失敗\n一致なる Selector あるいは利用できる結果はございません。";
"i18n:candidateServiceMenu.selectorResponse.succeeded" = "結果獲得完了\n取得した結果はクリップボードに書き込みました。";
"i18n:CandidateServiceMenuEditor.description"="サービスメニューは候補陳列ウィンドウで「(Shift+)符号メニューキー」で呼び出せるメニューです。ハイライトされている候補はサービスメニューを呼び出すためのパラメータとして使用されます。現在のウィンドウ(サービスメニューエディター)に陳列中のメタデータによって、パラメーターをクエリするために指定済みのウェブページを開くサービスもあれば、クリップボードに(このパソコンでの)計算結果を書き出すサービスもございます。\n\n追記: 現在のウィンドウにサービスが登録されていない場合、上記のショートカットは代わりに「ページをめくるショートカット」として動作します。";
"i18n:CandidateServiceMenuEditor.formatGuide" = "サービスメタデータのフォーマット規則です。\n\nイ1 行に 1 定義。2 つのセルを区切るには、区切り文字として「Tab」をご使用ください。メニュー項目タイトルに含むパラメータ「%s」は、サービス利用時に実際のパラメータデータと置き換わります。パラメータが 1 文字の場合は、メニュー項目名の末尾に UTF コードポイントが付きます。\n\nロ定義値の冒頭部分は「@SEL:」か「@WEB:」か「@URL:」であり、それぞれの意味:\n\n- 「@SEL:」がつく場合:残りの文字列はご指定の Objective-C Selector の名前とします。Selector の名前の最後文字は必ず「ASCII コロン」です。これで弊アプリのいくつかの内部機能を用いて(このパソコンでの)計算結果をを取得することができます。\n\n- 「@WEB:」・「@URL:」がつく場合残りの文字列は「パラメータをクエリするための」Web ページの URL です。その URL に含むパラメータ「%s」は、サービス利用時に実際のパラメータデータと置き換わります。";
"i18n:CandidateServiceMenuEditor.howToGetGuide" = "編集エリアにマウスポインターをホバーすると、ポップアップで「データ入力の説明」が表示されます。";
"i18n:CandidateServiceMenuEditor.prompt" = "登録したいサービスのメタデータを入力してください。";
"i18n:CandidateServiceMenuEditor.table.field.MenuTitle" = "メニュー項目タイトル";
"i18n:CandidateServiceMenuEditor.table.field.Value" = "定義値";
"i18n:KimoDataImportError.connectionFailure.errMsg" = "「直接読み込む」作動失敗:両アプリの NSConnection 通信はできませんでした。\n\n- 「直接読込」を続行する前に、まずは Yahoo! KeyKey そのアプリを実行すること。弊アプリは Yahoo! KeyKey をすでに1度呼び覚ませてみたため、すぐにもう一度「直接読込」するのも可能です。\n\n- 今の実行中の弊アプリはご自分で Xcode で組み立てた場合、Xcode プロジェクトの Sandbox Entitlements の配置の正しさをご確認ください。\n\n- 今のパソコンは Intel Mac ではない場合、必ず Rosetta 2 をご実装ください。Yahoo! KeyKey の実行には Rosetta 2 は必要条件です。\n\n- 上記のすべての状況を排除したら、多分「__objc_empty_cache」という肝心なる API は今のこのシステムでもう利用できないと推測できます。この API がない限り、Yahoo! KeyKey の実行は不可能です。今のこのシステムの「Console.app」で Yahoo! KeyKey の故障報告の有無をご確認ください。";
"i18n:KimoDataImportError.fileHandlerFailure.errMsg" = "新しい資料はユーザー辞書データファイルに書き込むのはできませんでした。";
"i18n:kimoImportButton.DragFileToHere" = "ここにドラッグして";
@ -319,12 +330,14 @@
"Reload" = "再読込";
"Remove Selected" = "外す";
"Replace to" = "単語 →";
"Reset Default" = "全てをリセット";
"Reverse Lookup (Phonabets)" = "注音音読逆引参照";
"Save" = "セーブ";
"Secondary Pinyin with Numeral Intonation" = "国音二式 (ローマ字+数字音調)";
"Security-harden the composition buffer for all clients" = "全ての客体アプリに対して、入力緩衝列にセキュリティ強化対策を起用";
"Seigyou" = "精業配列";
"Selection Keys:" = "言選り用キー:";
"Service Menu Editor" = "サービスメニューエディター";
"Share alphanumerical mode status across all clients" = "全ての客体アプリに英数入力モードの状態を共有";
"Shift+BackSpace:" = "Shift+BackSpace:";
"Shift+Letter:" = "Shift+文字キー:";

View File

@ -26,6 +26,7 @@
"About vChewing…" = "关于威注音…";
"Accept leading intonations in rare cases" = "在个别情况下,允许声调前置键入";
"Add Client" = "登记新客体";
"Add Service" = "新增服务";
"Adjust candidate window location according to current node length" = "根据当前位置的字词长度,自动调整选字窗的位置";
"All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file." = "不启用该选项的话,在组字区内的字根将会以原始键盘按键名称来显示。所有关于字根的定义,均请洽 CIN 磁带档案内的「%keyname」章节。";
"Allow backspace-editing miscomposed readings" = "允许对无效的读音使用 BackSpace 编辑";
@ -125,6 +126,7 @@
"Completely disable using Shift key to toggle alphanumerical mode" = "彻底禁止使用 Shift 键切换英数模式";
"Consolidate the context on confirming candidate selection" = "在使用选字窗选字时,自动巩固上下文";
"Consolidate" = "整理";
"Copy All to Clipboard" = "全部拷贝到剪贴簿";
"Core Dict loading complete." = "核心辞典载入完毕";
"Currency Numeral Output" = "大写汉字数字输出";
"Cursor Selection:" = "选字游标:";
@ -183,6 +185,7 @@
"Harden vertical punctuations during vertical typing (not recommended)" = "在纵排书写时,强制转换标点为纵排形式(不推荐)";
"Hold ⇧ to choose associates." = "摁住⇧以选取关联词语。";
"Horizontal" = "横向布局";
"How to Fill" = "填写指引";
"Hsu" = "许氏国音自然排列";
"Hualuo Pinyin with Numeral Intonation" = "华罗拼音+数字标调";
"i18n:aboutWindow.ABOUT_APP_TITLE_FULL" = "关于 macOS 版「威注音输入法」";
@ -195,6 +198,14 @@
"i18n:aboutWindow.OK_BUTTON" = "确定";
"i18n:aboutWindow.WEBSITE_BUTTON" = "网站";
"i18n:CandidateKey.ValidationError.AssignedForOtherPurposes" = "无法将已挪作他用的按键设为选字键。";
"i18n:candidateServiceMenu.selectorResponse.failed" = "请求失败\nSelector 指令匹配失败、或无结果可用。";
"i18n:candidateServiceMenu.selectorResponse.succeeded" = "成功取得结果\n已将结果写入剪贴簿。";
"i18n:CandidateServiceMenuEditor.description" = "服务选单可以在选字窗内借由「(Shift+)符号选单键」呼出。在您呼出该选单的那一刻,原先的选字窗内的高亮选中的候选字词将成为该服务选单内各个功能的处理参数。根据当前视窗(服务选单管理器)内所陈列的服务的不同,有些服务是会叫出浏览器前往指定网页检索指定内容,而有些服务则可能会将本地计算的某些结果自动拷贝到您的剪贴簿内。\n\n注如果当前视窗内没有任何可用的服务选单资料的话上述热键在选字窗内的对应的行为会是辅助翻页。";
"i18n:CandidateServiceMenuEditor.formatGuide" = "填写规则大致如下:\n\n1. 每行一笔定义,选单条目名称与定义资料值以 Tab 字元分割。选单名称当中的「%s」会在使用服务时被置换成传入的参数。如果参数只有一个字元的话则程式会自动在选单名称结尾用括弧追加显示其统一码码位。\n\n2. 定义资料值的开头是「@SEL:」或「@WEB:」或「@URL:」,其中:\n\n- 「@SEL:」对应 Objective-C Selector 名称,可以呼叫威注音输入法内部预先定义的某些资料生成功能。直接在此之后紧接着填写 Selector 名称即可(须以西文半形冒号结尾)。\n\n- 「@WEB:」与「@URL:」等价,用来紧接着填写网址。网址当中的「%s」会在使用服务时被置换成传入的参数。";
"i18n:CandidateServiceMenuEditor.howToGetGuide" = "可将滑鼠悬停于编辑区内、以检视资料填写指引。";
"i18n:CandidateServiceMenuEditor.prompt" = "请键入您要登记的服务资料。";
"i18n:CandidateServiceMenuEditor.table.field.MenuTitle" = "选单条目名称";
"i18n:CandidateServiceMenuEditor.table.field.Value" = "定义资料值";
"i18n:KimoDataImportError.connectionFailure.errMsg" = "「直接汇入」功能失败:无法成功建立 NSConnection 跨执行绪通讯。\n\n- 在执行该步骤之前,请确保奇摩输入法有在运行。威注音输入法已经在此刻发起了一次对奇摩输入法的运行呼叫请求,于是您可以立刻再次重试。\n\n- 如果您当前运行的威注音输入法的程式包是您自己建置的话,请检查专案内的 Sandbox 权能组态设定档案Entitlements、看看是否发生了与此有关的内容变更。\n\n- 如果这台 Mac 并未使用 Intel CPU 的话,请先安装 Rosetta 2否则奇摩输入法将无法运作。\n\n- 如果以上状况都被排除了的话,那么 __objc_empty_cache 这个 API 可能在当前版本的作业系统内被移除了。这个 API 对奇摩输入法而言至关重要。请检查您当前的作业系统内建的「系统监视程式 Console.app」确认一下是否有与奇摩输入法有关的程式崩溃报告。";
"i18n:KimoDataImportError.fileHandlerFailure.errMsg" = "没能将新的资料成功写入到使用者自订语汇辞典当中。";
"i18n:kimoImportButton.DragFileToHere" = "请将档案拽到这边";
@ -319,12 +330,14 @@
"Reload" = "重新载入";
"Remove Selected" = "移除所选条目";
"Replace to" = "置换 为";
"Reset Default" = "恢复原厂设定";
"Reverse Lookup (Phonabets)" = "注音反查";
"Save" = "存档";
"Secondary Pinyin with Numeral Intonation" = "国音二式+数字标调";
"Security-harden the composition buffer for all clients" = "针对所有客体软体启用强化型组字区安全防护";
"Seigyou" = "精业排列";
"Selection Keys:" = "选字键:";
"Service Menu Editor" = "服务选单编辑器";
"Share alphanumerical mode status across all clients" = "对所有客体应用共用中英文输入切换状态";
"Shift+BackSpace:" = "Shift+退格键:";
"Shift+Letter:" = "Shift+字母键:";

View File

@ -26,6 +26,7 @@
"About vChewing…" = "關於威注音…";
"Accept leading intonations in rare cases" = "在個別情況下,允許聲調前置鍵入";
"Add Client" = "登記新客體";
"Add Service" = "新增服務";
"Adjust candidate window location according to current node length" = "根據當前位置的字詞長度,自動調整選字窗的位置";
"All strokes in the composition buffer will be shown as ASCII keyboard characters unless this option is enabled. Stroke is definable in the “%keyname” section of the CIN file." = "不啟用該選項的話,在組字區內的字根將會以原始鍵盤按鍵名稱來顯示。所有關於字根的定義,均請洽 CIN 磁帶檔案內的「%keyname」章節。";
"Allow backspace-editing miscomposed readings" = "允許對無效的讀音使用 BackSpace 編輯";
@ -125,6 +126,7 @@
"Completely disable using Shift key to toggle alphanumerical mode" = "徹底禁止使用 Shift 鍵切換英數模式";
"Consolidate the context on confirming candidate selection" = "在使用選字窗選字時,自動鞏固上下文";
"Consolidate" = "整理";
"Copy All to Clipboard" = "全部拷貝到剪貼簿";
"Core Dict loading complete." = "核心辭典載入完畢";
"Currency Numeral Output" = "大寫漢字數字輸出";
"Cursor Selection:" = "選字游標:";
@ -183,6 +185,7 @@
"Harden vertical punctuations during vertical typing (not recommended)" = "在縱排書寫時,強制轉換標點為縱排形式(不推薦)";
"Hold ⇧ to choose associates." = "摁住⇧以選取關聯詞語。";
"Horizontal" = "橫向佈局";
"How to Fill" = "填寫指引";
"Hsu" = "許氏國音自然排列";
"Hualuo Pinyin with Numeral Intonation" = "華羅拼音+數字標調";
"i18n:aboutWindow.ABOUT_APP_TITLE_FULL" = "關於 macOS 版「威注音輸入法」";
@ -195,6 +198,14 @@
"i18n:aboutWindow.OK_BUTTON" = "確定";
"i18n:aboutWindow.WEBSITE_BUTTON" = "網站";
"i18n:CandidateKey.ValidationError.AssignedForOtherPurposes" = "無法將已挪作他用的按鍵設為選字鍵。";
"i18n:candidateServiceMenu.selectorResponse.failed" = "請求失敗\nSelector 指令匹配失敗、或無結果可用。";
"i18n:candidateServiceMenu.selectorResponse.succeeded" = "成功取得結果\n已將結果寫入剪貼簿。";
"i18n:CandidateServiceMenuEditor.description" = "服務選單可以在選字窗內藉由「(Shift+)符號選單鍵」呼出。在您呼出該選單的那一刻,原先的選字窗內的高亮選中的候選字詞將成為該服務選單內各個功能的處理參數。根據當前視窗(服務選單管理器)內所陳列的服務的不同,有些服務是會叫出瀏覽器前往指定網頁檢索指定內容,而有些服務則可能會將本地計算的某些結果自動拷貝到您的剪貼簿內。\n\n註如果當前視窗內沒有任何可用的服務選單資料的話上述熱鍵在選字窗內的對應的行為會是輔助翻頁。";
"i18n:CandidateServiceMenuEditor.formatGuide" = "填寫規則大致如下:\n\n1. 每行一筆定義,選單條目名稱與定義資料值以 Tab 字元分割。選單名稱當中的「%s」會在使用服務時被置換成傳入的參數。如果參數只有一個字元的話則程式會自動在選單名稱結尾用括弧追加顯示其統一碼碼位。\n\n2. 定義資料值的開頭是「@SEL:」或「@WEB:」或「@URL:」,其中:\n\n- 「@SEL:」對應 Objective-C Selector 名稱,可以呼叫威注音輸入法內部預先定義的某些資料生成功能。直接在此之後緊接著填寫 Selector 名稱即可(須以西文半形冒號結尾)。\n\n- 「@WEB:」與「@URL:」等價,用來緊接著填寫網址。網址當中的「%s」會在使用服務時被置換成傳入的參數。";
"i18n:CandidateServiceMenuEditor.howToGetGuide" = "可將滑鼠懸停於編輯區內、以檢視資料填寫指引。";
"i18n:CandidateServiceMenuEditor.prompt" = "請鍵入您要登記的服務資料。";
"i18n:CandidateServiceMenuEditor.table.field.MenuTitle" = "選單條目名稱";
"i18n:CandidateServiceMenuEditor.table.field.Value" = "定義資料值";
"i18n:KimoDataImportError.connectionFailure.errMsg" = "「直接匯入」功能失敗:無法成功建立 NSConnection 跨執行緒通訊。\n\n- 在執行該步驟之前,請確保奇摩輸入法有在運行。威注音輸入法已經在此刻發起了一次對奇摩輸入法的運行呼叫請求,於是您可以立刻再次重試。\n\n- 如果您當前運行的威注音輸入法的程式包是您自己建置的話,請檢查專案內的 Sandbox 權能組態設定檔案Entitlements、看看是否發生了與此有關的內容變更。\n\n- 如果這台 Mac 並未使用 Intel CPU 的話,請先安裝 Rosetta 2否則奇摩輸入法將無法運作。\n\n- 如果以上狀況都被排除了的話,那麼 __objc_empty_cache 這個 API 可能在當前版本的作業系統內被移除了。這個 API 對奇摩輸入法而言至關重要。請檢查您當前的作業系統內建的「系統監視程式 Console.app」確認一下是否有與奇摩輸入法有關的程式崩潰報告。";
"i18n:KimoDataImportError.fileHandlerFailure.errMsg" = "沒能將新的資料成功寫入到使用者自訂語彙辭典當中。";
"i18n:kimoImportButton.DragFileToHere" = "請將檔案拽到這邊";
@ -319,12 +330,14 @@
"Reload" = "重新載入";
"Remove Selected" = "移除所選條目";
"Replace to" = "置換 為";
"Reset Default" = "恢復原廠設定";
"Reverse Lookup (Phonabets)" = "注音反查";
"Save" = "存檔";
"Secondary Pinyin with Numeral Intonation" = "國音二式+數字標調";
"Security-harden the composition buffer for all clients" = "針對所有客體軟體啟用強化型組字區安全防護";
"Seigyou" = "精業排列";
"Selection Keys:" = "選字鍵:";
"Service Menu Editor" = "服務選單編輯器";
"Share alphanumerical mode status across all clients" = "對所有客體應用共用中英文輸入切換狀態";
"Shift+BackSpace:" = "Shift+退格鍵:";
"Shift+Letter:" = "Shift+字母鍵:";