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