This article aims to deeply discuss the design and implementation of a multi-language intelligent input method and is summarized based on actual development practices. Mainly serving as a carrier for technical sharing and exchange, it is inevitable that there may be errors and omissions. Colleagues are welcome to put forward valuable opinions and questions for common progress. This article is original content. Any form of reprint must indicate the source and original author.
In today's globalized world, multi-language intelligent input methods have become an important tool for people's cross-language communication. It not only needs to support input in multiple languages but also should have convenient switching functions and real-time translation capabilities to meet users' needs in different scenarios. This article will introduce in detail how to design and implement such a multi-language intelligent input method application in the Huawei HarmonyOS Next system (API 12), including aspects such as project requirement analysis, architecture design, key technology implementation, data consistency, and error handling.
I. Project Requirements and Design Analysis
(I) User Requirements
When users use a multi-language intelligent input method, they expect to be able to easily switch between keyboard layouts of different languages, such as quickly switching between Chinese and English input methods. At the same time, the text entered by users can be translated into the target language in real time without manually triggering the translation operation to improve communication efficiency.
(II) Architecture Design
To achieve the above requirements, we adopt a layered architecture design. The input method is divided into an input interface layer, an input method core layer, and a translation service layer. The input interface layer is responsible for displaying keyboard layouts of different languages and receiving user input; the input method core layer manages the state of the input method, processes user input events, and communicates with the translation service layer; the translation service layer is responsible for calling the translation API to translate text and returning the results to the input method core layer. This decoupled design makes the responsibilities of each layer clear and easy to maintain and expand.
II. Multi-Language Keyboard Layout and Subtype Switching
(I) Configuring Multi-Language Subtypes
In the HarmonyOS system, we configure the subtypes of the input method through ohos_extension.input_method
. The following is a simple configuration example. Suppose we support two language subtypes, Chinese and English:
{
"module": {
"extensionAbilities": [
{
"description": "InputMethodExtDemo",
"icon": "Smedia:icon",
"name": "InputMethodExtAbility",
"srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
"type": "inputMethod",
"exported": true,
"metadata": [
{
"name": "ohos.extension.input_method",
"resource": "Sprofile:input_method_config"
}
]
}
]
},
"subtypes": [
{
"icon": "Smedia:icon",
"id": "InputMethodExtAbility",
"label": "$string:english",
"locale": "en-US",
"mode": "lower"
},
{
"icon": "Smedia:icon",
"id": "InputMethodExtAbility1",
"label": "$string:chinese",
"locale": "zh-CN",
"mode": "lower"
}
]
}
(II) Implementing the Subtype Switching Interface
In the input method application, we use the switchCurrentInputMethodSubtype
interface to implement subtype switching. The following is an example code:
import { InputMethodSubtype, inputMethod } from '@kit.IMEKit';
export class KeyboardController {
async switchCurrentInputMethodSubtype() {
let subTypes = await inputMethod.getSetting().listCurrentInputMethodSubtype();
let currentSubType = inputMethod.getCurrentInputMethodSubtype();
for (let i = 0; i < subTypes.length; i++) {
if (subTypes[i].id!== currentSubType.id) {
await inputMethod.switchCurrentInputMethodSubtype(subTypes[i]);
}
}
}
}
(III) Listening to the setSubtype
Event
To load the corresponding input interface according to different subtypes, we need to listen to the setSubtype
event. The following is an example code:
import { InputMethodSubtype, inputMethodEngine, inputMethod } from '@kit.IMEKit';
export class KeyboardController {
async handleSubtypeChange() {
let panel: inputMethodEngine.Panel;
let inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
inputMethodAbility.on('setSubtype', (inputMethodSubtype: InputMethodSubtype) => {
if (inputMethodSubtype.id === 'InputMethodExtAbility') {
panel.setUiContent('pages/Index');
} else if (inputMethodSubtype.id === 'InputMethodExtAbility1') {
panel.setUiContent('pages/Index1');
}
});
}
}
III. Design and Implementation of Real-Time Translation Function Module
(I) Using Asynchronous Tasks to Manage Translation Requests
To avoid translation requests blocking user input, we use asynchronous tasks to manage translation requests. When users enter text, we trigger an asynchronous translation task to send the text to the translation service layer for translation. The following is a simple asynchronous task example (using Promise
):
async function translateText(text: string): Promise<string> {
return new Promise((resolve, reject) => {
// Here we simulate an asynchronous translation operation. In practical applications, a real translation API should be called.
setTimeout(() => {
resolve(`Translation result: ${text}`);
}, 1000);
});
}
(II) Implementing Cross-Process Calls to Translation APIs
In the HarmonyOS system, the input method application and translation service may run in different processes. Therefore, cross-process communication needs to be implemented to call the translation API. We can use the inter-process communication mechanism provided by the system (such as communication between Ability
) to achieve this. The following is a simple cross-process call example (assuming the translation service is a ServiceAbility
):
import { FeatureAbility } from '@ohos.ability.featureAbility';
async function callTranslationService(text: string): Promise<string> {
let want = {
bundleName: 'com.example.translation',
abilityName: 'TranslationServiceAbility'
};
try {
let result = await FeatureAbility.callAbility(want, { text });
return result;
} catch (error) {
console.error('Translation service call failed:', error);
return '';
}
}
(III) Obtaining Translation Results and Returning to the Input Method Application
When the translation service completes the translation, the translation results are returned to the input method application through a callback or message mechanism. In the input method application, we need to process the translation results and display them to the user. The following is a simple processing example:
callTranslationService('Hello').then((result) => {
console.log('Translation result:', result);
// Display the translation result on the input interface.
});
IV. Data Consistency and Error Handling
(I) Handling Network Request Errors During Translation
During the translation process, there may be situations where network request errors occur or the translation service is unavailable. We need to handle these errors and provide users with friendly prompts. For example:
callTranslationService('Hello').catch((error) => {
console.error('Translation failed:', error);
// Display an error prompt to the user, such as popping up a dialog box to prompt "Translation failed. Please check the network connection."
});
(II) Ensuring Synchronized Display of User-Entered Text and Translation Results
To ensure the synchronized display of user-entered text and translation results, we need to update the input interface at the appropriate time. For example, after the translation result is returned, display the result next to the input box or at other specified locations in a timely manner. At the same time, handle the situation where the user continues to enter text during the translation process to avoid display confusion.
V. Sample Code and Architecture Diagram
(I) Sample Code
The following is an example of the main code structure of a simplified multi-language intelligent input method application:
// InputMethodService.ts
import { Want } from '@kit.AbilityKit';
import keyboardController from './model/KeyboardController';
import { InputMethodExtensionAbility } from '@kit.IMEKit';
export default class InputDemoService extends InputMethodExtensionAbility {
onCreate(want: Want): void {
keyboardController.onCreate(this.context);
}
onDestroy(): void {
keyboardController.onDestroy();
}
}
// KeyboardController.ts
import { display } from '@kit.ArkUT';
import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit';
const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
export class KeyboardController {
private mContext: InputMethodExtensionContext | undefined = undefined;
private panel: inputMethodEngine.Panel | undefined = undefined;
private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
constructor() {}
public onCreate(context: InputMethodExtensionContext): void {
this.mContext = context;
this.initWindow();
this.registerListener();
}
public onDestroy(): void {
this.unRegisterListener();
if (this.panel) {
this.panel.hide();
inputMethodAbility.destroyPanel(this.panel);
}
if (this.mContext) {
this.mContext.destroy();
}
}
public insertText(text: string): void {
if (this.textInputClient) {
this.textInputClient.insertText(text);
// Trigger translation task
this.translateText(text);
}
}
public deleteForward(length: number): void {
if (this.textInputClient) {
this.textInputClient.deleteForward(length);
}
}
private initWindow(): void {
if (this.mContext === undefined) {
return;
}
let dis = display.getDefaultDisplaySync();
let dWidth = dis.width;
let dHeight = dis.height;
let keyHeightRate = 0.47;
let keyHeight = dHeight * keyHeightRate;
let nonBarPosition = dHeight - keyHeight;
let panelInfo: inputMethodEngine.PanelInfo = {
type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
flag: inputMethodEngine.PanelFlag.FLG_FIXED
};
inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
this.panel = inputPanel;
if (this.panel) {
await this.panel.resize(dWidth, keyHeight);
await this.panel.moveTo(0, nonBarPosition);
await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');
}
});
}
private registerListener(): void {
this.registerInputListener();
}
private registerInputListener(): void {
inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
this.textInputClient = textInputClient;
this.keyboardController = kbController;
});
inputMethodAbility.on('inputStop', () => {
this.onDestroy();
});
}
private unRegisterListener(): void {
inputMethodAbility.off('inputStart');
inputMethodAbility.off('inputStop', () => {});
}
private async translateText(text: string): Promise<void> {
try {
let result = await callTranslationService(text);
// Process and display the translation result
console.log('Translation result:', result);
} catch (error) {
console.error('Translation failed:', error);
}
}
}
const keyboardController = new KeyboardController();
export default keyboardController;
(II) Architecture Diagram
The following is a schematic diagram of the architecture of a multi-language intelligent input method application:
Layer | Function Description |
---|---|
Input interface layer | Display keyboard layouts of different languages, receive user input, and display translation results. |
Input method core layer | Manage the input method state, process user input events, call translation services, and update the input interface. |
Translation service layer | Call the translation API to translate text and return the translation results to the input method core layer. |
Through the above design and implementation, we have successfully created a multi-language intelligent input method application that meets users' needs in multi-language input and real-time translation. In actual development, performance can be further optimized, more language support can be added, and user experience can be improved. I hope this article can provide useful references and inspiration for the development of input methods in the HarmonyOS system.
Top comments (0)