diff --git a/HISTORY.md b/HISTORY.md index d6a686b66d8..e136044e844 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -28,6 +28,18 @@ * chore(common): move to 18.0 alpha (#10713) * chore: move to 18.0 alpha +## 17.0.269 beta 2024-02-15 + +* docs(android): Document using gestures on touch, and remove references to Browser (#10686) +* docs(android/app): Add help page on using the banner (#10691) +* fix(web): corrects Android over-deletion of selected text, other context diffs involving selected text (#10662) +* fix(web): disables banner interaction when suggestions are absent (#10695) +* fix(web): longpress validation by base key, not current location (#10707) + +## 17.0.268 beta 2024-02-15 + +* chore: move to beta + ## 17.0.267 alpha 2024-02-14 * fix(linux): Use temp dir if we can't create cache dir (#10681) diff --git a/TIER.md b/TIER.md index 4a58007052a..65b2df87f7d 100644 --- a/TIER.md +++ b/TIER.md @@ -1 +1 @@ -alpha +beta diff --git a/android/help/android_images/dist-install1-ap.png b/android/help/android_images/dist-install1-ap.png index 9150ea481a7..cf9f068d7dd 100644 Binary files a/android/help/android_images/dist-install1-ap.png and b/android/help/android_images/dist-install1-ap.png differ diff --git a/android/help/android_images/dist-storage-permission-ap.png b/android/help/android_images/dist-storage-permission-ap.png index 0deb5cf41af..11ece5d76b0 100644 Binary files a/android/help/android_images/dist-storage-permission-ap.png and b/android/help/android_images/dist-storage-permission-ap.png differ diff --git a/android/help/android_images/english-dictionaries-ap.png b/android/help/android_images/english-dictionaries-ap.png index fa8fc8da074..37907562d6d 100644 Binary files a/android/help/android_images/english-dictionaries-ap.png and b/android/help/android_images/english-dictionaries-ap.png differ diff --git a/android/help/android_images/settings-khmer-info-ap.png b/android/help/android_images/settings-khmer-info-ap.png index 7106bf5ab7b..6176b20eae9 100644 Binary files a/android/help/android_images/settings-khmer-info-ap.png and b/android/help/android_images/settings-khmer-info-ap.png differ diff --git a/android/help/android_images/settings-suggestions-ap.png b/android/help/android_images/settings-suggestions-ap.png index 100a13704b2..474fee4efda 100644 Binary files a/android/help/android_images/settings-suggestions-ap.png and b/android/help/android_images/settings-suggestions-ap.png differ diff --git a/android/help/android_images/settings1-ap.png b/android/help/android_images/settings1-ap.png index ed57492ef95..ebf7b6b31ae 100644 Binary files a/android/help/android_images/settings1-ap.png and b/android/help/android_images/settings1-ap.png differ diff --git a/android/help/android_images/settings2-ap.png b/android/help/android_images/settings2-ap.png index 2aba1e210c7..fb34d44be63 100644 Binary files a/android/help/android_images/settings2-ap.png and b/android/help/android_images/settings2-ap.png differ diff --git a/android/help/android_images/settings3-ap.png b/android/help/android_images/settings3-ap.png index 9a593152e8a..08b37b5f797 100644 Binary files a/android/help/android_images/settings3-ap.png and b/android/help/android_images/settings3-ap.png differ diff --git a/android/help/android_images/settings4-ap.png b/android/help/android_images/settings4-ap.png index 1c2b207455f..98ca6e2e0da 100644 Binary files a/android/help/android_images/settings4-ap.png and b/android/help/android_images/settings4-ap.png differ diff --git a/android/help/android_images/settings5-ap.png b/android/help/android_images/settings5-ap.png index af0b6eac948..fb3398e86e7 100644 Binary files a/android/help/android_images/settings5-ap.png and b/android/help/android_images/settings5-ap.png differ diff --git a/android/help/android_images/settings7-ap.png b/android/help/android_images/settings7-ap.png index e3e42797e2e..eb1a72c5ed0 100644 Binary files a/android/help/android_images/settings7-ap.png and b/android/help/android_images/settings7-ap.png differ diff --git a/android/help/android_images/themed-banner.png b/android/help/android_images/themed-banner.png new file mode 100644 index 00000000000..ae9fe4442d4 Binary files /dev/null and b/android/help/android_images/themed-banner.png differ diff --git a/android/help/basic/index.md b/android/help/basic/index.md index 2dab69a9690..02d942ca0e8 100644 --- a/android/help/basic/index.md +++ b/android/help/basic/index.md @@ -2,6 +2,8 @@ title: Basic Help --- +* [Using the Banner on the Keyboard](using-the-banner) + * [Switching Between Keyboards](switching-between-keyboards) * [Installing Custom Keyboards/Dictionaries](installing-custom-packages) @@ -9,7 +11,5 @@ title: Basic Help * [Using the Settings Screen](config/) -* [Using the Keyman Browser](using-keyman-browser) - * [Removing Keyboards](uninstalling-keyboards) * [Removing Dictionaries](uninstalling-dictionaries) diff --git a/android/help/basic/using-keyman-browser.md b/android/help/basic/using-keyman-browser.md deleted file mode 100644 index 7c62b8b39b8..00000000000 --- a/android/help/basic/using-keyman-browser.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: Using the Keyman Browser - Keyman for Android Help ---- - -## Using the Keyman Browser -Step 1) -Click the Keyman Browser button in the Keyman app - -Step 2) -Enter the URL of a website to visit into the address bar, for example google.com. - -Keyman Browser will load the page and detect your language if it is present, and reformat it to show your language instead of square boxes. - -Step 3) -Use the bookmark button to save the current page for browsing later. - -Step 4) -Use the Globe icon to swap between languages. diff --git a/android/help/basic/using-the-banner.md b/android/help/basic/using-the-banner.md new file mode 100644 index 00000000000..75862f6df44 --- /dev/null +++ b/android/help/basic/using-the-banner.md @@ -0,0 +1,27 @@ +--- +title: Using the Banner on the Keyboard - Keyman for Android Help +--- + +## About the Keyboard Banner + +Keyman keyboards now always display a banner above the keyboard for one of the following functionalities: + +* Display suggestions (See "Using the Suggestion Banner" below) +* Display a Keyman-themed banner so popups and gestures for the top row of keys are visible +* Reserved for future functionality + +## Using the Suggestion Banner + +If a [dictionary is installed](installing-dictionaries) and enabled for the active Keyman keyboard, the banner will display suggestions that can be selected. + +![](../android_images/settings-suggestions-ap.png) + +* Drag the banner horizontally to see more suggestions +* Overly-long suggestions are partially hidden, but expand when a finger is held on them +* Banner displays up to 8 suggestions + +## The Keyman Themed Banner + +When suggestions are disabled or unavailable, the Keyman-themed banner will display instead. This is displayed so popups and gestures for the top row of keys are visible. + +![](../android_images/themed-banner.png) diff --git a/android/help/context/gestures.md b/android/help/context/gestures.md new file mode 100644 index 00000000000..fe5aaf8138d --- /dev/null +++ b/android/help/context/gestures.md @@ -0,0 +1,13 @@ +--- +title: Gesture Hints and Tips +--- + +## Using Gestures on the Keyboard Keys +There are now several gestures available to interact with some keys on the keyboard. Keys which have gestures available will normally show them as a hint in the top right of the key: + +![](../android_images/touch-hold-ap.png) + +* **Long press**: Press and hold a key, and after a moment a submenu will appear. Slide the finger to the submenu to select a key. Release the finger, and the highlighted key from the submenu will be output. +* **Flick**: Press and hold a key, and then slide the finger in various directions on a key to reach alternate outputs. The key will animate to show the expected output when the finger gets released. A common flick is to slide down to reach numerals on the top row of the keyboard. +* **Multitap**: Some keys can be pressed repeatedly and rapidly to reach alternate outputs. Tapping rapidly twice on Shift will activate Caps Lock on many keyboards. + diff --git a/android/help/context/index.md b/android/help/context/index.md index ba873acdba3..1e351a4369e 100644 --- a/android/help/context/index.md +++ b/android/help/context/index.md @@ -2,5 +2,6 @@ title: Contextual Help --- -* [The Keyman Menu (Phone Layout)](menu-phone) -* [The Keyman Menu (Tablet Layout)](menu-tablet) \ No newline at end of file +* [The Keyboard Keys and Keyman Menu (Phone Layout)](menu-phone) +* [The Keyboard Keys and Keyman Menu (Tablet Layout)](menu-tablet) +* [Gesture Hints and Tips](gestures) \ No newline at end of file diff --git a/android/help/context/menu-phone.md b/android/help/context/menu-phone.md index 363a08b9f40..479e8b17153 100644 --- a/android/help/context/menu-phone.md +++ b/android/help/context/menu-phone.md @@ -9,14 +9,13 @@ title: Keyman for Android (Phone Layout) | ![](../android_images/backspace-ap.png) | Backspace | | ![](../android_images/return-ap.png) | Return | | ![](../android_images/shift-ap.png) | Shift key. Long press this key to access the CTRL, ALT and CTRLALT keys (which can access additional key layers) | -| ![](../android_images/touch-hold-ap.png) | Keys with a small dot in the top right corner indicate a long press key. Access further functionality by long pressing the key | -| ![](../android_images/share-a.png) | Share your text with other apps like Messages, Gmail, or Twitter. Some versions of Android also let you copy your text to the clipboard. | +| ![](../android_images/touch-hold-ap.png) | There are several [gestures](gestures) to interact with some keys on the keyboard. Keys which have gestures available will normally show a hint in the top right of the key.
Gestures include: long press, flick, and multitap. | ---- | | Toolbar Icons | |---|---| -| ![](../android_images/browser-a.png) | Open the Keyman Browser to use the web in your language | +| ![](../android_images/share-a.png) | Share your text with other apps like Messages, Gmail, or Twitter. Some versions of Android also let you copy your text to the clipboard. | | ![](../android_images/menu-icon-a.png) | Open the menu for additional options | | ![](../android_images/font-size-a.png) | Adjust the font size | | ![](../android_images/delete-a.png) | Delete all current text | diff --git a/android/help/context/menu-tablet.md b/android/help/context/menu-tablet.md index f2264c7ac54..8092095ea75 100644 --- a/android/help/context/menu-tablet.md +++ b/android/help/context/menu-tablet.md @@ -9,14 +9,13 @@ title: Keyman for Android (Tablet Layout) | ![](../android_images/backspace-at.png) | Backspace | | ![](../android_images/return-at.png) | Return | | ![](../android_images/shift-at.png) | Shift key. Long press this key to access the CTRL, ALT and CTRLALT keys (which can access additional key layers) | -| ![](../android_images/touch-hold-at.png) | Keys with a small dot in the top right corner indicate a long press key. Access further functionality by long pressing the key | +| ![](../android_images/touch-hold-at.png) | There are several [gestures](gestures) to interact with some keys on the keyboard. Keys which have gestures available will normally show a hint in the top right of the key.
Gestures include: long press, flick, and multitap. | ---- | | Toolbar Icons | |---|---| | ![](../android_images/share-a.png) | Share your text with other atps like Messages, Gmail, or Twitter. Some versions of Android also let you copy your text to the clipboard. | -| ![](../android_images/browser-a.png) | Open the Keyman Browser to use the web in your language | | ![](../android_images/menu-icon-a.png) | Open the menu for additional options | | ![](../android_images/font-size-a.png) | Adjust the font size | | ![](../android_images/delete-a.png) | Delete all current text | diff --git a/android/help/index.md b/android/help/index.md index 617b88abb5f..f03adb523d1 100644 --- a/android/help/index.md +++ b/android/help/index.md @@ -12,9 +12,10 @@ title: Keyman for Android 18.0 Help * [How To - Download and Install a Keyman Keyboard](start/installing-keyboards) * [Enabling Keyman as a System-Wide Keyboard](start/enabling-system-keyboard) -## [Using the Menu](context/) -* [Using the menu buttons (phone layout)](context/menu-phone) -* [Using the menu buttons (tablet layout)](context/menu-tablet) +## [Using the Keyboard Keys and Menu](context/) +* [Using the keyboard keys and menu buttons (phone layout)](context/menu-phone) +* [Using the keyboard keys and menu buttons (tablet layout)](context/menu-tablet) +* [Gesture Hints and Tips](context/gestures) ## Ask for Help * [Ask other users in the SIL Keyman Community](https://community.software.sil.org/c/keyman) @@ -26,11 +27,11 @@ title: Keyman for Android 18.0 Help * [How To - Integrating Keyman for Android](troubleshooting/integrating) ### [Using Keyman for Android](basic/) +* [Using the Banner on the Keyboard](basic/using-the-banner) * [Switching Between Keyboards](basic/switching-between-keyboards) * [Installing Custom Keyboards/Dictionaries](basic/installing-custom-packages) * [Adding Dictionaries](basic/installing-dictionaries) * [Using the Settings Screen](basic/config/) -* [Using the Keyman Browser](basic/using-keyman-browser) * [Removing Keyboards](basic/uninstalling-keyboards) * [Removing Dictionaries](basic/uninstalling-dictionaries) diff --git a/android/help/start/enabling-system-keyboard.md b/android/help/start/enabling-system-keyboard.md index cee8554b59f..381fbb3e272 100644 --- a/android/help/start/enabling-system-keyboard.md +++ b/android/help/start/enabling-system-keyboard.md @@ -5,7 +5,7 @@ title: How To - Enabling Keyman as a System-Wide Keyboard ## Enabling System Wide Keyboards Step 1) Open the Keyman app and select the menu for additional options. -The screenshots below are of a device running Android 9.0 Pie. +The screenshots below are of a device running Android 12 Snow Cone. ![](../android_images/settings1-ap.png) diff --git a/common/web/keyboard-processor/src/index.ts b/common/web/keyboard-processor/src/index.ts index ad7aa495235..f800691cfac 100644 --- a/common/web/keyboard-processor/src/index.ts +++ b/common/web/keyboard-processor/src/index.ts @@ -39,6 +39,7 @@ export { default as KeyMapping } from "./text/keyMapping.js"; export { default as OutputTarget } from "./text/outputTarget.js"; export * from "./text/outputTarget.js"; export { default as RuleBehavior } from "./text/ruleBehavior.js"; +export * from "./text/stringDivergence.js"; export * from "./text/systemStores.js"; export * from "@keymanapp/web-utils"; diff --git a/common/web/keyboard-processor/src/text/outputTarget.ts b/common/web/keyboard-processor/src/text/outputTarget.ts index 97a47928479..ab9656b6999 100644 --- a/common/web/keyboard-processor/src/text/outputTarget.ts +++ b/common/web/keyboard-processor/src/text/outputTarget.ts @@ -1,6 +1,7 @@ /// import { extendString } from "@keymanapp/web-utils"; +import { findCommonSubstringEndIndex } from "./stringDivergence.js"; extendString(); @@ -120,85 +121,24 @@ export default abstract class OutputTarget { * @param from An output target (preferably a Mock) representing the prior state of the input/output system. */ buildTransformFrom(original: OutputTarget): Transform { - let to = this.getText(); - let from = original.getText(); + const toLeft = this.getTextBeforeCaret(); + const fromLeft = original.getTextBeforeCaret(); - let fromCaret = original.getDeadkeyCaret(); - let toCaret = this.getDeadkeyCaret(); + const leftDivergenceIndex = findCommonSubstringEndIndex(fromLeft, toLeft, false); + const deletedLeft = fromLeft.substring(leftDivergenceIndex)._kmwLength(); + // No need for our specialized variant here. + const insertedText = toLeft.substring(leftDivergenceIndex); - // Step 1: Determine the number of left-deletions. - let maxSMPLeftMatch = fromCaret < toCaret ? fromCaret : toCaret; + const toRight = this.getTextAfterCaret(); + const fromRight = original.getTextAfterCaret(); + const rightDivergenceIndex = findCommonSubstringEndIndex(fromRight, toRight, true); - // We need the corresponding non-SMP caret location in order to binary-search efficiently. - // (Examining code units is much more computationally efficient.) - let maxLeftMatch = to._kmwCodePointToCodeUnit(maxSMPLeftMatch); + // Right insertions aren't supported, but right deletions will matter in some scenarios. + // In particular, once we allow right-deletion for pred-text suggestions applied with the + // caret mid-word.. + const deletedRight = fromRight.substring(0, rightDivergenceIndex + 1)._kmwLength(); - // 1.1: use a non-SMP-aware binary search to determine the divergence point. - let start = 0; - let end = maxLeftMatch; // the index AFTER the last possible matching char. - - // This search is O(maxLeftMatch). 1/2 + 1/4 + 1/8 + ... converges to = 1. - while(start < end) { - let mid = Math.floor((end+start+1) / 2); // round up (compare more) - let fromLeft = from.substr(start, mid-start); - let toLeft = to.substr(start, mid-start); - - if(fromLeft == toLeft) { - start = mid; - } else { - end = mid - 1; - } - } - - // At the loop's end: `end` now holds the non-SMP-aware divergence point. - // The 'caret' is after the last matching code unit. - - // 1.2: detect a possible surrogate-pair split scenario, correcting for it - // (by moving the split before the high-surrogate) if detected. - - // If the split location is precisely on either end of the context, we can't - // have split a surrogate pair. - if(end > 0 && end < maxLeftMatch) { - let potentialHigh = from.charCodeAt(end-1); - let potentialFromLow = from.charCodeAt(end); - let potentialToLow = to.charCodeAt(end); - - // if potentialHigh is a possible high surrogate... - if(potentialHigh >= 0xD800 && potentialHigh <= 0xDBFF) { - // and at least one potential 'low' is a possible low surrogate... - let flag = potentialFromLow >= 0xDC00 && potentialFromLow <= 0xDFFF; - flag = flag || (potentialToLow >= 0XDC00 && potentialToLow <= 0xDFFF); - - // Correct the split location, moving it 'before' the high surrogate. - if(flag) { - end = end - 1; - } - } - } - - // 1.3: take substring from start to the split point; determine SMP-aware length. - // This yields the SMP-aware divergence index, which gives the number of left-deletes. - let newCaret = from._kmwCodeUnitToCodePoint(end); - let deletedLeft = fromCaret - newCaret; - - // Step 2: Determine the other properties. - // Since the 'after' OutputTarget's caret indicates the end of any inserted text, we - // can easily calculate the rest. - let insertedLength = toCaret - newCaret; - let delta = to._kmwSubstr(newCaret, insertedLength); - - let undeletedRight = to._kmwLength() - toCaret; - let originalRight = from._kmwLength() - fromCaret; - let deletedRight = originalRight - undeletedRight; - - // May occur when reverting a suggestion that had been applied mid-word. - if(deletedRight < 0) { - // Restores deleteRight characters. - delta = delta + to._kmwSubstr(toCaret, -deletedRight); - deletedRight = 0; - } - - return new TextTransform(delta, deletedLeft, deletedRight); + return new TextTransform(insertedText, deletedLeft, deletedRight); } buildTranscriptionFrom(original: OutputTarget, keyEvent: KeyEvent, readonly: boolean, alternates?: Alternate[]): Transcription { @@ -214,6 +154,13 @@ export default abstract class OutputTarget { * @param original An `OutputTarget` (usually a `Mock`). */ restoreTo(original: OutputTarget) { + this.clearSelection(); + // We currently do not restore selected text; the mechanism isn't supported at present for + // all output target types - especially in regard to re-selecting the text if restored. + // + // I believe this would mostly matter if/when reverting predictions based upon selected text. + // That pattern isn't well-supported yet, though. + // this.setTextBeforeCaret(original.getTextBeforeCaret()); this.setTextAfterCaret(original.getTextAfterCaret()); @@ -223,6 +170,10 @@ export default abstract class OutputTarget { } apply(transform: Transform) { + // Selected text should disappear on any text edit; application of a transform + // certainly qualifies. + this.clearSelection(); + if(transform.deleteRight) { this.setTextAfterCaret(this.getTextAfterCaret()._kmwSubstr(transform.deleteRight)); } diff --git a/common/web/keyboard-processor/src/text/stringDivergence.ts b/common/web/keyboard-processor/src/text/stringDivergence.ts new file mode 100644 index 00000000000..e3461e129a4 --- /dev/null +++ b/common/web/keyboard-processor/src/text/stringDivergence.ts @@ -0,0 +1,93 @@ +// Future TODO: import from @keymanapp/common-types... once we no longer need to support ES5. +import { Uni_IsSurrogate1, Uni_IsSurrogate2 } from '@keymanapp/web-utils'; + +/** + * Returns the index for the code point divergence point between two strings, as measured in code + * unit coordinates. + * @param str1 + * @param str2 + * @param commonSuffix If false, asserts a common prefix to the strings. If true, asserts a common suffix. + * @returns The code unit index within `str1` for the start of the code point not common to both. + * + * Follows the convention of (start, end) substring parameterizations having 'end' be exclusive. + */ +export function findCommonSubstringEndIndex(str1: string, str2: string, commonSuffix: boolean): number { + /** + * The maximum number of iterations to consider; exceeding this would go past a string boundary. + */ + const maxInterval = Math.min(str1.length, str2.length); + + /** + * The first valid index within the string. + */ + let start: number; + + /** + * The current index within the string under consideration as the divergence point. + */ + let index: number; + + /** + * The index at which to terminate the search for a divergence point. + */ + let end: number; + + /** + * Index shift per loop iteration. + */ + let inc: number; + + /** + * Difference in index for comparison between strings. + * Mostly matters when assuming a common right-hand side. + */ + let offset: number; + + if(commonSuffix) { + start = index = str1.length - 1; // e.g. str.length == 10 => start = 9. + end = index - maxInterval; // e.g. maxInterval 8, start 9 => iterate from 9 to 2, end at 1. + inc = -1; + offset = str2.length - str1.length; + } else { + start = index = 0; + end = maxInterval; // last valid index: - 1. e.g. maxInterval 8 => iterate from 0 to 7, end at 8. + inc = 1; + offset = 0; + } + + // Step 1: Find the index for the first code unit different between the strings. + for(; index != end; index += inc) { + if(str1.charAt(index) != str2.charAt(index + offset)) { + break; + } + } + + // Step 2: Ensure that we're not splitting a surrogate pair. + + // `index` corresponds to the first char that is different _in the direction indicated by inc_. + // If it's the start position, it can't split a (completed) surrogate pair. + if(index != start && index != end) { + // if commonLeft, high surrogate; if commonRight, low surrogate. + const commonPotentialSurrogate = str1.charCodeAt(index - inc); + // Opposite surrogate type from the previous variable. + const divergentChar1 = str1.charCodeAt(index); + const divergentChar2 = str2.charCodeAt(index + offset); + + const commonSurrogateChecker = commonSuffix ? Uni_IsSurrogate2 : Uni_IsSurrogate1; + const divergentSurrogateChecker = commonSuffix ? Uni_IsSurrogate1 : Uni_IsSurrogate2; + + // If the last common character if of the direction-appropriate surrogate type (for + // comprising a potential split surrogate pair representing a non-BMP char)... + if(commonSurrogateChecker(commonPotentialSurrogate)) { + // And one of the two divergent chars is a qualifying match - a surrogate + // of the opposite type... + if(divergentSurrogateChecker(divergentChar1) || divergentSurrogateChecker(divergentChar2)) { + // Our current index would split a surrogate pair; decrement the index to + // preserve the pair. + return index - inc; + } + } + } + + return index; +} \ No newline at end of file diff --git a/common/web/keyboard-processor/tests/node/transcriptions.js b/common/web/keyboard-processor/tests/node/transcriptions.js index 073e0dc6b32..6d6be7b540f 100644 --- a/common/web/keyboard-processor/tests/node/transcriptions.js +++ b/common/web/keyboard-processor/tests/node/transcriptions.js @@ -1,22 +1,161 @@ import { assert } from 'chai'; -import { Mock } from '@keymanapp/keyboard-processor'; +import { Mock, findCommonSubstringEndIndex } from '@keymanapp/keyboard-processor'; import { extendString } from '@keymanapp/web-utils'; extendString(); // Ensure KMW's string-extension functionality is available. String.kmwEnableSupplementaryPlane(false); -describe("Transcriptions and Transforms", function() { - var toSupplementaryPairString = function(code){ - var H = Math.floor((code - 0x10000) / 0x400) + 0xD800; - var L = (code - 0x10000) % 0x400 + 0xDC00; +// A unicode-coding like alias for use in constructing non-BMP strings. +const u = String.fromCodePoint; + +/** + * Returns the "Mathematical Sans-Serif Small" non-BMP encoding for + * a passed-in lowercase char between 'a' and 'z', inclusive. + * @param {*} char + * @returns + */ +const ss = (char) => { + const charCodeOffset = char.charCodeAt(0) - 'a'.charCodeAt(0); + return u(0x1d5ba + charCodeOffset); +} + +describe("String divergence calculations", function() { + describe("Common prefix", () => { + it("BMP text", () => { + const result1 = findCommonSubstringEndIndex("apple", "applause", false); + assert.equal(result1, 4); + + const result2 = findCommonSubstringEndIndex("applesauce", "applause", false); + assert.equal(result2, 4); + }); + + it("BMP edge cases", () => { + const result1 = findCommonSubstringEndIndex("applesauce", "applesauce", false); + assert.equal(result1, 10); + + const result2 = findCommonSubstringEndIndex("applesauce", "banana bread", false); + assert.equal(result2, 0); + }); + + it("non-BMP text", () => { + const smp_ify = (str) => str.split('').map(ss).join(''); + + const result1 = findCommonSubstringEndIndex( + smp_ify('apple'), + smp_ify('applause'), + false + ); + + // 2 per non-BMP char; is in code-unit... units. + // Will avoid splitting code points, though. + assert.equal(result1, 8); + + const result2 = findCommonSubstringEndIndex( + smp_ify('applesauce'), + smp_ify('applause'), + false + ); + + assert.equal(result2, 8); + }); + + it("non-BMP edge cases", () => { + const smp_ify = (str) => str.split('').map(ss).join(''); + + const result1 = findCommonSubstringEndIndex( + smp_ify('applesauce'), + smp_ify('applesauce'), + false + ); + + assert.equal(result1, 20); + + const result2 = findCommonSubstringEndIndex( + smp_ify('applesauce'), + smp_ify('banana bread'), + false + ); - return String.fromCharCode(H, L); - } + assert.equal(result2, 0); + }) + }); + + describe("Common suffix", () => { + it("BMP text", () => { + // att|endance + // transc|endance + const result1 = findCommonSubstringEndIndex("attendance", "transcendance", true); + assert.equal(result1, 2); + + // transcend|ance + // happenst|ance + const result2 = findCommonSubstringEndIndex("transcendance", "happenstance", true); + assert.equal(result2, 8); + + }); + + it("BMP edge cases", () => { + // If the two are equal... + const result1 = findCommonSubstringEndIndex("post-caret text", "post-caret text", true); + assert.equal(result1, -1); + + // If the two are completely different... + const result2 = findCommonSubstringEndIndex("post-caret text", "supercalifragilistic", true); + assert.equal(result2, "post-caret text".length-1); + }) + + it("non-BMP text", () => { + const smp_ify = (str) => str.split('').map(ss).join(''); + + // att|endance + // trans|endance + const result1 = findCommonSubstringEndIndex( + smp_ify("attendance"), + smp_ify("transcendance"), + true + ); + + // 2 per non-BMP char; is in code-unit... units. + // Will avoid splitting code points; is odd b/c we get the index of the LAST char of the pair. + assert.equal(result1, 5); + + // transcend|ance + // happenst|ance + const result2 = findCommonSubstringEndIndex( + smp_ify("transcendance"), + smp_ify("happenstance"), + true + ); + assert.equal(result2, 17); + + }); - // Built in-line via function. Looks functionally equivalent to "apple", but with SMP characters. - let u = toSupplementaryPairString; + it("non-BMP edge cases", () => { + const smp_ify = (str) => str.split('').map(ss).join(''); + + // If the two are equal... + const result3 = findCommonSubstringEndIndex( + smp_ify("post-caret text"), + smp_ify("post-caret text"), + true + ); + assert.equal(result3, -1); + + // If the two are completely different... + const result2 = findCommonSubstringEndIndex( + smp_ify("post-caret text"), + smp_ify("supercalifragilistic"), + true + ); + assert.equal(result2, smp_ify("post-caret text").length-1); + }) + }) +}); + +describe("Transcriptions and Transforms", function() { + // Built in-line via function. Looks functionally equivalent to "apple", but with non-BMP characters. let smpApple = u(0x1d5ba)+u(0x1d5c9)+u(0x1d5c9)+u(0x1d5c5)+u(0x1d5be); it("does not store an alias for related OutputTargets", function() { @@ -124,7 +263,7 @@ but not himself.`; // Sheev Palpatine, in the Star Wars prequels. assert.equal(transcription.transform.deleteRight, 1, "Incorrect count for right-of-caret deletions"); }); - it("handles deletions around the caret without text insertion (SMP text)", function() { + it("handles deletions around the caret without text insertion (non-BMP text)", function() { try { String.kmwEnableSupplementaryPlane(true); var target = new Mock(smpApple, 2); @@ -216,7 +355,7 @@ but not himself.`; // Sheev Palpatine, in the Star Wars prequels. assert.equal(transcription.transform.deleteRight, 3, "Incorrect count for right-of-caret deletions"); }); - it("handles deletions around the caret with text insertion (SMP text)", function() { + it("handles deletions around the caret with text insertion (non-BMP text)", function() { try { String.kmwEnableSupplementaryPlane(true); @@ -297,6 +436,35 @@ but not himself.`; // Sheev Palpatine, in the Star Wars prequels. String.kmwEnableSupplementaryPlane(false); } }); + + it('from targets with existing selection', () => { + // | | + const target = new Mock("testing testing one two three"); + target.setSelection(8, 20) + const original = Mock.from(target); + target.clearSelection(); + + const transform = target.buildTransformFrom(original); + assert.deepEqual(transform, { + insert: '', + deleteLeft: 0, + deleteRight: 0 + }); + }); + + it('to targets with existing selection', () => { + // | | + const target = new Mock("testing testing one two three"); + target.setSelection(8, 20) + const transform = { + insert: '', + deleteLeft: 0, + deleteRight: 0 + }; + + target.apply(transform); + assert.equal(target.getText(), 'testing two three'); + }); }); /*describe("Operations with deadkeys", function() { diff --git a/common/web/types/src/ldml-keyboard/pattern-parser.ts b/common/web/types/src/ldml-keyboard/pattern-parser.ts index cc8d8aab522..8901de6dddf 100644 --- a/common/web/types/src/ldml-keyboard/pattern-parser.ts +++ b/common/web/types/src/ldml-keyboard/pattern-parser.ts @@ -400,7 +400,7 @@ export class VariableParser { /** * Pattern for matching a capture set reference `($[set])` */ - public static readonly CAPTURE_SET_REFERENCE = /\(\$\[([0-9A-Za-z_]{1,32})\]\)/g; + public static readonly CAPTURE_SET_REFERENCE = /\(\$\[([0-9A-Za-z_]{1,32})\]\)/; /** * `$[1:variable]` diff --git a/common/web/utils/src/index.ts b/common/web/utils/src/index.ts index fdd49e60f26..1d1e540a80f 100644 --- a/common/web/utils/src/index.ts +++ b/common/web/utils/src/index.ts @@ -21,6 +21,8 @@ export { default as extendString } from "./kmwstring.js"; export { default as ManagedPromise } from "./managedPromise.js"; export { default as TimeoutPromise, timedPromise } from "./timeoutPromise.js"; +export { Uni_IsSurrogate1, Uni_IsSurrogate2 } from "./surrogates.js"; + // // Uncomment the following line and run the bundled output to verify successful // // esbuild bundling of this submodule: // console.log(Version.CURRENT.toString()); \ No newline at end of file diff --git a/common/web/utils/src/surrogates.ts b/common/web/utils/src/surrogates.ts new file mode 100644 index 00000000000..53c1a1e3fae --- /dev/null +++ b/common/web/utils/src/surrogates.ts @@ -0,0 +1,27 @@ +/* + * The definitions below are duplicated from common/web/types/util/util.ts; + * we can't downcompile the originals to ES5 when bundling with esbuild. + * `import type` stuff is fine, but not non-type `import` statements. + * + * TODO: Use those instead, once we're no longer building ES5 versions of Web. + */ + +export const Uni_LEAD_SURROGATE_START = 0xD800; +export const Uni_LEAD_SURROGATE_END = 0xDBFF; +export const Uni_TRAIL_SURROGATE_START = 0xDC00; +export const Uni_TRAIL_SURROGATE_END = 0xDFFF; + +/** + * @brief True if a lead surrogate + * \def Uni_IsSurrogate1 + */ +export function Uni_IsSurrogate1(ch : number) { + return ((ch) >= Uni_LEAD_SURROGATE_START && (ch) <= Uni_LEAD_SURROGATE_END); +} +/** + * @brief True if a trail surrogate + * \def Uni_IsSurrogate2 + */ +export function Uni_IsSurrogate2(ch : number) { + return ((ch) >= Uni_TRAIL_SURROGATE_START && (ch) <= Uni_TRAIL_SURROGATE_END); +} diff --git a/core/src/ldml/ldml_transforms.cpp b/core/src/ldml/ldml_transforms.cpp index 8c6dab86ed0..f7f5202df98 100644 --- a/core/src/ldml/ldml_transforms.cpp +++ b/core/src/ldml/ldml_transforms.cpp @@ -588,7 +588,7 @@ transform_entry::apply(const std::u32string &input, std::u32string &output) cons char32_t *s = new char32_t[group1Len + 1]; assert(s != nullptr); // TODO-LDML: OOM // convert - substr.toUTF32((UChar32 *)s, group1Len + 1, status); + group1.toUTF32((UChar32 *)s, group1Len + 1, status); if (!UASSERT_SUCCESS(status)) { return 0; // TODO-LDML: memory issue } @@ -749,12 +749,9 @@ transforms::apply(const std::u32string &input, std::u32string &output) { for (auto group = transform_groups.begin(); group < transform_groups.end(); group++) { // for each transform group // break out once there's a match - // TODO-LDML: reorders - // Assume it's a non reorder group /** Length of match within this group*/ // find the first match in this group (if present) - // TODO-LDML: check if reorder if (group->type == any_group_type::transform) { std::u32string subOutput; size_t subMatched = group->transform.apply(updatedInput, subOutput); diff --git a/core/tests/unit/ldml/keyboards/ldml_test-test.xml b/core/tests/unit/ldml/keyboards/ldml_test-test.xml new file mode 100644 index 00000000000..a1e75fa310c --- /dev/null +++ b/core/tests/unit/ldml/keyboards/ldml_test-test.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/unit/ldml/keyboards/ldml_test.xml b/core/tests/unit/ldml/keyboards/ldml_test.xml new file mode 100644 index 00000000000..63bd4f1c08a --- /dev/null +++ b/core/tests/unit/ldml/keyboards/ldml_test.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/tests/unit/ldml/keyboards/meson.build b/core/tests/unit/ldml/keyboards/meson.build index b1c7b6c9a22..890a4e65f7a 100644 --- a/core/tests/unit/ldml/keyboards/meson.build +++ b/core/tests/unit/ldml/keyboards/meson.build @@ -33,6 +33,7 @@ tests_without_testdata = [ # These tests have a k_001_tiny-test.xml file as well. tests_with_testdata = [ + 'ldml_test', 'k_001_tiny', 'k_006_backspace', 'k_007_transform_rgx', diff --git a/developer/src/kmc-ldml/src/compiler/tran.ts b/developer/src/kmc-ldml/src/compiler/tran.ts index 4e521f3986c..6bf1e28a1ac 100644 --- a/developer/src/kmc-ldml/src/compiler/tran.ts +++ b/developer/src/kmc-ldml/src/compiler/tran.ts @@ -126,7 +126,6 @@ export abstract class TransformCompiler => { // @@ -194,7 +194,7 @@ export const fixupHistory = async ( let pulls: PRInformation[] = []; try { - pulls = await reportHistory(octokit, base, force, false); + pulls = await reportHistory(octokit, base, force, false, from, to); } catch(e) { logWarning(String(e)); return -1; diff --git a/resources/build/version/src/index.ts b/resources/build/version/src/index.ts index 31e1a2cd046..16f1583e399 100644 --- a/resources/build/version/src/index.ts +++ b/resources/build/version/src/index.ts @@ -108,7 +108,7 @@ const main = async (): Promise => { if(argv._.includes('history')) { logInfo(`# Validating history for ${version}`); - changeCount = await fixupHistory(octokit, argv.base, argv.force, argv['write-github-comment']); + changeCount = await fixupHistory(octokit, argv.base, argv.force, argv['write-github-comment'], argv.from, argv.to); logInfo(`# ${changeCount} change(s) found for ${version}\n`); } diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index 5e11fa48095..6b95cb49a0e 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -205,9 +205,6 @@ export default class KeymanEngine extends KeymanEngineBase { const newScrollLeft = this.scrollState.updateTo(sample); - this.highlightAnimation.setBaseScroll(newScrollLeft); + this.highlightAnimation?.setBaseScroll(newScrollLeft); // Only re-enable the original suggestion, even if the touchpoint finds // itself over a different suggestion. Might happen if a scroll boundary @@ -578,9 +581,9 @@ export class SuggestionBanner extends Banner { }; sourceTracker.suggestion = source.currentSample.item; - markSelection(sourceTracker.suggestion); - - source.currentSample.item.highlight(true); + if(sourceTracker.suggestion) { + markSelection(sourceTracker.suggestion); + } const terminationHandler = () => { if(sourceTracker.suggestion) { diff --git a/web/src/engine/osk/src/input/gestures/specsForLayout.ts b/web/src/engine/osk/src/input/gestures/specsForLayout.ts index 0fb58f7df85..551fa74165d 100644 --- a/web/src/engine/osk/src/input/gestures/specsForLayout.ts +++ b/web/src/engine/osk/src/input/gestures/specsForLayout.ts @@ -443,7 +443,7 @@ export function longpressContactModel(params: GestureParams, enabledFlicks: bool duration: spec.waitLength, expectedResult: true }, - validateItem: (key: KeyElement) => !!key?.key.spec.sk, + validateItem: (_: KeyElement, baseKey: KeyElement) => !!baseKey?.key.spec.sk, pathModel: { evaluate: (path) => { const stats = path.stats; diff --git a/windows/src/desktop/kmshell/locale/pt-PT/strings.xml b/windows/src/desktop/kmshell/locale/pt-PT/strings.xml index 67693c956fe..3ac6fe5801a 100644 --- a/windows/src/desktop/kmshell/locale/pt-PT/strings.xml +++ b/windows/src/desktop/kmshell/locale/pt-PT/strings.xml @@ -51,11 +51,11 @@ - O Keyman ainda está em execução. Clique neste ícone para usar o seu teclado de idioma a qualquer momento + O Keyman ainda está em execução. Clique neste ícone para usar o teclado do seu idioma a qualquer momento - O keyman já está em execução. Clique no ícone do Keyman na área de notificação do sistema para usar o seu teclado de idioma + O keyman já está em execução. Clique no ícone do Keyman na área de notificação do sistema para usar o teclado do seu idioma @@ -99,7 +99,7 @@ - Manter contato + Manter contacto @@ -107,7 +107,7 @@ - Nome do arquivo: + Nome do ficheiro: @@ -139,11 +139,11 @@ - Layout do teclado: + Layout de teclado: - Idioma do teclado: + Idioma de teclado: @@ -207,7 +207,7 @@ - Direitos autorais: + Direitos de autor: @@ -219,7 +219,7 @@ - Baixar teclado.. + Descarregar teclado.. @@ -227,24 +227,24 @@ - Você não tem nenhum teclado instalado. Clique no botão Baixar Teclado para instalar um layout de teclado do website da Tavultesoft. + Não tem nenhum teclado instalado. Clique no botão Descarregar Teclado para instalar um layout de teclado do website da Tavultesoft. - Você só pode desinstalar o teclado \'%1$s\' se você for um Administrador + Só é possível desinstalar o teclado \'%1$s\' se for um Administrador - Compartilhar teclado + Partilhar teclado - Escaneie este código para carregar este teclado em outro dispositivo ou + Digitalize este código para carregar este teclado noutro dispositivo ou - compartilhar online + partilhar online @@ -276,31 +276,31 @@ - Tratar os defeitos de hardware como chaves simples + Tratar teclas mortas como teclas simples - Mostrar mensagens de sugestão + Mostrar mensagens com sugestões - Automaticamente reportar erros para keyman.com + Reportar erros automaticamente para keyman.com - Compartilhar estatísticas de uso anônimas com keyman.com + Partilhar estatísticas de uso anónimas com keyman.com - Rodar ao iniciar o Windows + Correr ao iniciar o Windows - Mostrar tela de advertência + Mostrar ecrã de aviso - Mostrar tela de bem-vindo + Mostrar ecrã de bem-vindo @@ -312,11 +312,11 @@ - Liberar Shift/Ctrl/Alt no Teclado Virtual depois de clicar numa tecla + Ativar Shift/Ctrl/Alt no Teclado Virtual depois de clicar numa tecla - Sempre mostrar Teclado Virtual enquanto teclado Keyman estiver selecionado. + Mostrar sempre Teclado Virtual enquanto o teclado Keyman estiver selecionado. @@ -357,7 +357,7 @@ - Abrir a Configuração + Abrir Configuração @@ -385,15 +385,15 @@ - Se você tiver quaisquer problemas usando o Keyman, faça uma pergunta no Fórum da Comunidade de Keyman. + Se tiver algum problema com o Keyman, coloque uma questão no Fórum da Comunidade Keyman. - Abrir Fórum da Comunidade de Keyman + Abrir Fórum da Comunidade Keyman - Criado pelo SIL International + Criado por SIL International @@ -405,7 +405,7 @@ - Links Úteis + Ligações Úteis @@ -417,11 +417,11 @@ - Configurações do Proxy... + Configurações de Proxy... - Definições do Sistema... + Definições de Sistema... @@ -429,20 +429,20 @@ - Baixar teclado do site da Tavultesoft + Descarregar teclado do site da Tavultesoft - Não instalar, somente baixar + Não instalar, apenas descarregar - Não foi possível baixar o teclado, erro %2$d: %1$s + Não foi possível descarregar o teclado, erro %2$d: %1$s - < de volta + < Anterior @@ -462,7 +462,7 @@ - Configuração do Servidor Proxy + Configuração de Servidor Proxy @@ -474,11 +474,11 @@ - Usuário: + Utilizador: - Senha: + Palavra-passe: @@ -486,8 +486,8 @@ - Selecione o script latim base -teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente ao seu layout preferido. + Selecione o teclado latino básico + que utiliza no Windows. O teclado Keyman irá se adaptar automaticamente ao seu layout preferido. @@ -496,7 +496,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Selecione uma tecla de atalho padrão ou escolha \'Personalizado\' e segure Ctrl, Shift e/ou Alt e digite a tecla de atalho desejada para o idioma %1$s: + Selecione uma tecla de atalho padrão ou escolha \'Personalizado\' e mantenha pressionadas as teclas Ctrl, Shift e/ou Alt e escreva a tecla de atalho desejada para o idioma %1$s: @@ -505,17 +505,17 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - A tecla de atalho %1$s entrará em conflito com o uso normal do teclado. Você deve usar pelo menos Ctrl ou Alt. Você quer alterar isto agora\? + A tecla de atalho %1$s entrará em conflito com o uso normal do teclado. É necessário utilizar pelo menos Ctrl ou Alt. Quer alterar isto agora\? - A tecla de atalho %1$s conflita com a tecla de atalho selecionada para o teclado %2$s. Se você continuar, a tecla de atalho para o teclado %2$s será limpa. Continuar? + A tecla de atalho %1$s entra em conflito com a tecla de atalho selecionada para o teclado %2$s. Se continuar, a tecla de atalho para o teclado %2$s será apagada. Continuar? - A tecla de atalho %1$s está em conflito com outra chave de acesso. Se continuar, a outra tecla de atalho será apagada. Continuar? + A tecla de atalho %1$s está em conflito com outra tecla de atalho. Se continuar, a outra tecla de atalho será apagada. Continuar? @@ -523,11 +523,11 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Atualizações para o Keyman estão disponíveis agora + Há atualizações disponíveis para o Keyman - Favor selecionar as atualizações que você deseja instalar: + Selecione as atualizações que deseja instalar: @@ -565,12 +565,12 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Não foi possível contatar o site da Tavultesoft - certifique-se de estar conectado à Internet e tente novamente. + Não foi possível contactar o site da Tavultesoft - certifique-se que está ligado à Internet e tente novamente. - Não foi possível contatar keyman.com - por favor, certifique-se de que você tem uma conexão de Internet ativa e tente novamente. O erro recebido foi: %1$s + Não foi possível contactar keyman.com - por favor, certifique-se de que tem uma ligação de Internet ativa e tente novamente. O erro recebido foi: %1$s @@ -578,7 +578,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Clique neste ícone para baixar e instalar atualizações + Clique neste ícone para descarregar e instalar atualizações @@ -606,7 +606,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Mostrar esta tela na inicialização + Mostrar este ecrã ao início @@ -614,15 +614,15 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Encontre outros idiomas de exibição online... + Encontre outros idiomas de interface online... - Ajude a traduzir a interface do usuário... + Ajude a traduzir a interface do utilizador... - Português do Portugal + Português de Portugal @@ -630,11 +630,11 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - por-BR + por-PT - por-BR + por-PT @@ -642,21 +642,21 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Ressetar Sugestões + Repor Sugestões - Todas as mensagens de sugestão foram ressetadas e serão mostradas novamente. + Todas as mensagens de sugestões foram repostas e serão mostradas novamente. - Sair do chave\? + Sair de Keyman\? - Você tem certeza que deseja sair do Keyman? Seus teclados de teclado ainda serão listados nos idiomas do Windows, mas não ficarão funcionais até reiniciar o Keyman. + Tem a certeza que deseja sair do Keyman? Os teclados do Keyman ainda serão listados nos idiomas do Windows, mas não funcionarão até reiniciar o Keyman. @@ -666,11 +666,11 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Você fechou o Teclado Virtual. Keyman Desktop ainda está rodando. Você pode abrir o Teclado Virtual em qualquer momento por clicar no ícone do Keyman Desktop e selecionar \"Teclado Virtual\" + Fechou o Teclado Virtual. O Keyman Desktop ainda está a correr. Pode abrir o Teclado Virtual a qualquer momento ao clicar no ícone do Keyman Desktop e selecionar \"Teclado Virtual\" - Não exibir esta dica novamente + Não mostrar esta dica novamente @@ -678,7 +678,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Conteudo da Ajuda + Conteúdo de Ajuda @@ -690,15 +690,15 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Ver no teclado da tela + Ver no teclado do ecrã - Ver Auxiliar da Fonte + Ver Ajuda de Fonte - Visualizar Mapa de Caracteres + Ver Mapa de Caracteres @@ -710,19 +710,19 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Fechar no teclado da tela + Fechar no teclado do ecrã - Alternar Keyman &Desligado + Desativar Keyman &Desligado - Na tela &teclado + No ecrã &teclado - &Font Helper + &Ajuda de Fontes @@ -742,11 +742,11 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - E&xit + E&Sair - Desvanecer quando inativo + Transparente quando inativo @@ -754,11 +754,11 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Salvar como Página da Web... + Guardar como Página da Web... - Print... + Imprimir... @@ -781,17 +781,17 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Um teclado com o nome \'%1$s\' já está instalado. Se você continuar, será desinstalado antes que o novo seja instalado. Continuar? + Um teclado com o nome \'%1$s\' já está instalado. Se continuar, será desinstalado antes que o novo seja instalado. Continuar? - O teclado \'%1$s\' é parte do pacote \'%2$s\'. Você deve desinstalar o pacote inteiro. Continuar? + O teclado \'%1$s\' é parte do pacote \'%2$s\'. Deverá desinstalar o pacote inteiro. Continuar? - Não foi possível instalar o idioma do teclado; Windows tem um limite de 4 idiomas \"transientes\", e talvez você tenha atingido este limite. + Não foi possível instalar o idioma do teclado; Windows tem um limite de 4 idiomas \"transientes\", e talvez tenha atingido este limite. @@ -800,7 +800,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Este sistema de operação não é suportado. Favor contatar Tavultesoft para mais detalhes. + Este sistema operativo não é suportado. Contacte Tavultesoft para mais detalhes. @@ -808,7 +808,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - O arquivo de ajuda não pôde ser encontrado. + Não foi possível encontrar o ficheiro de ajuda. @@ -820,22 +820,22 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - Baixando Arquivo + Descarregando ficheiro - Tem certeza que deseja remover o teclado na tela instalado para %1$s? + Tem a certeza que deseja remover o teclado no ecrã instalado para %1$s? - Tem certeza que deseja desinstalar o teclado %1$s\? + Tem a certeza que deseja desinstalar o teclado %1$s\? - Tem certeza que deseja desinstalar o pacote %1$s\?\n\n%2$s + Tem a certeza que deseja desinstalar o pacote %1$s\?\n\n%2$s @@ -844,7 +844,7 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - O Mapa de Caracteres possui um banco de dados de caracteres que precisa ser construído antes de poder ser usado. Construí-lo agora\? + O Mapa de Caracteres possui um banco de dados de caracteres que precisa ser construído antes de poder ser utilizado. Construí-lo agora\? @@ -864,15 +864,15 @@ teclado que você usa no Windows. Teclado Keyman irá se adaptar automaticamente - O keyman não conseguiu iniciar. Por favor, verifique suas configurações de segurança para certificar-se de que o keyman.exe do programa é permitido iniciar antes de continuar. O erro retornado foi:\n\n\"%1$s\"\n\nVocê quer tentar iniciar o Keyman novamente agora\? + O keyman não conseguiu iniciar. Por favor, verifique suas configurações de segurança para certificar-se de que o programa keyman.exe é permitido iniciar antes de continuar. O erro retornado foi:\n\n\"%1$s\"\n\nQuer tentar iniciar o Keyman novamente agora\? - As informações de depuração do keyman serão armazenadas em um arquivo de log chamado %LOCALAPPDATA%\Keyman\Diag\system#.etl (onde # é um número). Você deve sair do Keyman antes de tentar excluir este arquivo.\n\n\AVISO: Este arquivo pode crescer grande rapidamente. Permitir a depuração pode tornar o sistema mais lento e só deve ser feito se recomendado pelo suporte técnico.\n\nATENÇÃO: Por favor, note que o arquivo de log de depuração registra todos os traços de tecla que você digita. Você só deve ativar o log de depuração durante uma sessão de diagnóstico ou depuração. + As informações de depuração do keyman serão armazenadas em um ficheiro de log chamado %LOCALAPPDATA%\Keyman\Diag\system#.etl (onde # é um número). Deverá sair do Keyman antes de tentar excluir este ficheiro.\n\n\AVISO: Este ficheiro pode crescer grande rapidamente. Permitir a depuração pode tornar o sistema mais lento e só deve ser feito se recomendado pelo suporte técnico.\n\nATENÇÃO: Por favor, note que o ficheiro de log de depuração registra todos os traços de tecla que digita. Só deve ativar o log de depuração durante uma sessão de diagnóstico ou depuração. - Por favor, aguarde enquanto busca fontes relacionadas com o teclado %1$s + Por favor, aguarde enquanto procura fontes relacionadas com o teclado %1$s