diff --git a/International/Internationalization/.gitignore b/International/Internationalization/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/International/Internationalization/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/International/Internationalization/AppScope/app.json5 b/International/Internationalization/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..789ce3dec92fed62e2d085120c42a1f787485a95 --- /dev/null +++ b/International/Internationalization/AppScope/app.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "app": { + "bundleName": "com.samples.internationalization", + "vendor": "sample", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/International/Internationalization/AppScope/resources/base/element/string.json b/International/Internationalization/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..999cd2ffc592f588885f354853a12461aa7f682b --- /dev/null +++ b/International/Internationalization/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Internationalization" + } + ] +} diff --git a/International/Internationalization/AppScope/resources/base/media/app_icon.png b/International/Internationalization/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/International/Internationalization/AppScope/resources/base/media/app_icon.png differ diff --git a/International/Internationalization/README_zh.md b/International/Internationalization/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..83dea2f22e37d267e45f17e66f52332d3a5bdb4d --- /dev/null +++ b/International/Internationalization/README_zh.md @@ -0,0 +1,82 @@ +# 国际化Internationalization + +## 介绍 + +本示例依照指南 开发->应用框架->Localization Kit(本地化开发服务)->[本地化开发服务(应用国际化)](https://gitee.com/openharmony/docs/tree/OpenHarmony-5.0.1-Release/zh-cn/application-dev/internationalization)进行编写。 +本示例主要展示了国际化的相关基础功能,通过调用`intl`,`i18n`实现简单的国际化操作。 + +### 效果预览 + +| **主页面** | **电话号码格式化** | **设置日历和历法界面** | **时间日期国际化界面** | +| ------------------------------------- | ------------------------------------------------------------ | --------------------------------------------------- | --------------------------------------------------------- | +| ![MainPage](./screenshots/MainPage.jpg) | ![PhoneNumberFormatting](./screenshots/PhoneNumberFormatting.jpg) | ![CalendarSetting](./screenshots/CalendarSetting.jpg) | ![DateTimeFormatting](./screenshots/DateTimeFormatting.jpg) | + +使用说明: + +1. 启动应用,进入主页面。 +2. 点击主页面不同的功能按钮,进入对应国际化功能输出判断显示界面。 +3. 功能界面中点击返回按钮,返回主页面。 +4. 测试超时设置为:Time out(s):30。 + +### 工程目录 + +``` +Internationalization +entry/src/main/ets +|---component +| |---TitleBar.ets // 公共标题栏 +| |---AssertEqual.ets // 断言判断函数 +|---entryability +| |---EntryAbility.ets // 程序入口类 +|---entrybackupability +| |---EntryBackupAbility.ets +|---i18napplication // 页面文件 +| |---CalendarSetting.ets // 设置日历和历法界面 +| |---CharacterProcessing.ets // 字符处理界面 +| |---DateTimeFormatting.ets // 时间日期国际化界面 +| |---LanguagePreferenceSetting.ets // 设置语言与用户偏好界面 +| |---MultilingualSorting.ets // 多语言排序界面 +| |---NameLocalization.ets // 本地化名称界面 +| |---NumberMeasurementFormatting.ets // 数字与度量衡国际化界面 +| |---PhoneNumberFormatting.ets // 电话号码格式化界面 +| |---TimezoneDstSetting.ets // 时区与夏时令国际化界面 +|---pages +| |---Index.ets // 主界面 +|---resources // 资源文件目录 +``` + +### 具体实现 + +- 设置在主页面集成所有功能界面模块,将`Listitem`中`Row`组件设置点击事件进入相关需求界面模块的展示。 + +- 每个子模块页面的实现代码对应国际化开发指南中的具体章节,子模块目录按其功能进行命名,如`DateTimeFormatting(时间日期格式化)`,示例代码内容由`Text`文本输出显示。 + +- 公共功能(如标题栏)提取为公共组件供各个子模块复用,设置公共组件`TitleBar`标题栏展示需求界面模块的功能名称,公共功能(如标题栏)提取为公共组件供各个子模块复用。 + +### 相关权限 + +[ohos.permission.UPDATE_CONFIGURATION](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-system-apps.md#ohospermissionupdate_configuration) + +### 依赖 + +不涉及。 + +### 约束与限制 + +1. 本示例仅支持标准系统上运行,支持设备:RK3568。 +2. 本示例为Stage模型,支持API18版本full-SDK,版本号:6.0.0。 +3. 支持的IDE版本:本示例已支持DevEco Studio 5.1.1 Release (构建版本:5.1.1.830,构建 2025年8月4日)编译运行。 +4. 本示例涉及[ohos.permission.UPDATE_CONFIGURATION](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/AccessToken/permissions-for-system-apps.md#ohospermissionupdate_configuration)为system_core级别,需要配置高权限签名,可参考[特殊权限配置方法](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/hapsigntool-overview.md)。 +5. 本示例涉及系统接口,需要配置系统应用签名,可以参考[特殊权限配置方法](https://gitee.com/openharmony/docs/blob/OpenHarmony-5.0.1-Release/zh-cn/application-dev/security/hapsigntool-overview.md),把配置文件中的“app-feature”字段信息改为“hos_system_app”。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/DocsSample/International/Internationalization > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` \ No newline at end of file diff --git a/International/Internationalization/build-profile.json5 b/International/Internationalization/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..70e22cb5cb97a869c9459e5f86a5b4d0b41ce9af --- /dev/null +++ b/International/Internationalization/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "6.0.1(21)", + "targetSdkVersion": "6.0.1(21)", + "runtimeOS": "HarmonyOS" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/.gitignore b/International/Internationalization/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/International/Internationalization/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/International/Internationalization/entry/build-profile.json5 b/International/Internationalization/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f2d19af2241c9760a16135687e22a736338ae506 --- /dev/null +++ b/International/Internationalization/entry/build-profile.json5 @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "apiType": "stageMode", + "buildOption": { + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": true, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + "runtimeOS": "HarmonyOS" + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/hvigorfile.ts b/International/Internationalization/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4f43d54667f8327c367c8096bd08bb8c75aff54 --- /dev/null +++ b/International/Internationalization/entry/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/International/Internationalization/entry/obfuscation-rules.txt b/International/Internationalization/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..69c4d6a8a5531548e4886fa766090c5c157a87d9 --- /dev/null +++ b/International/Internationalization/entry/obfuscation-rules.txt @@ -0,0 +1,18 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope \ No newline at end of file diff --git a/International/Internationalization/entry/oh-package.json5 b/International/Internationalization/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c9cb6c8174858277c9b0d465a51547dcab16d5ff --- /dev/null +++ b/International/Internationalization/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/International/Internationalization/entry/src/main/ets/component/AssertEqual.ets b/International/Internationalization/entry/src/main/ets/component/AssertEqual.ets new file mode 100644 index 0000000000000000000000000000000000000000..170dfaa64982dd035987be28c7b0184f7824e2dc --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/component/AssertEqual.ets @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function assertEqual(actual: T, expected: T, description: Resource): string { + if (actual === expected) { + return `${getContext().resourceManager.getStringSync(description)}${expected} --passed`; + } else { + return `${getContext().resourceManager + .getStringSync(description)} --failed: expected ${expected}, but got ${actual}`; + } +} + +export function assertEqualLong(actual: string, expected: string[], description: Resource): string { + if (expected.every(expected => actual.includes(expected))) { + return `${getContext().resourceManager.getStringSync(description)}passed`; + } else { + return `${getContext().resourceManager.getStringSync(description)}failed: expected ${expected}, but got ${actual}`; + } +} + +export function assertEqualFuzzy(actual: string, expected: string[], description: Resource): string { + if (expected.every(expected => actual.includes(expected))) { + return `${getContext().resourceManager.getStringSync(description)}${actual} --passed`; + } else { + return `${getContext().resourceManager.getStringSync(description)}failed: expected ${expected}, but got ${actual}`; + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/component/ResourceToString.ets b/International/Internationalization/entry/src/main/ets/component/ResourceToString.ets new file mode 100644 index 0000000000000000000000000000000000000000..a7057ee6b0089b79e3a2edacbd1081bba52640de --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/component/ResourceToString.ets @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export function resourceToString(resource: Resource): string { + const context = getContext(); + if (!context || !context.resourceManager) { + console.error('Context or resourceManager is undefined'); + return ''; + } + return context.resourceManager.getStringSync(resource); +} diff --git a/International/Internationalization/entry/src/main/ets/component/TitleBar.ets b/International/Internationalization/entry/src/main/ets/component/TitleBar.ets new file mode 100644 index 0000000000000000000000000000000000000000..f3cbe555d1961c2149ac4679d5b34bf2a49cf3f1 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/component/TitleBar.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@ohos.router'; + +@Component +export default struct TitleBar { + private title: string | Resource = $r('app.string.Internationalization'); + private hasBackPress: boolean = false; + + build() { + Row() { + if (this.hasBackPress) { + Row() { + Image($r('app.media.back')) + .id('btnBack') + .width(12) + .height(12) + } + .height('100%') + .aspectRatio(1) + .margin({ left: 24 }) + .onClick(() => { + router.back() + }) + } + Text(this.title) + .fontSize(20) + .fontColor(Color.Black) + .margin(this.hasBackPress ? {} : { left: 24 }) + Blank() + } + .width('100%') + .height(56) + .backgroundColor(Color.Transparent) + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/entryability/EntryAbility.ets b/International/Internationalization/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f2f8b94aa24b0a50e272270e4e18b6df93ac5fd --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/International/Internationalization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/International/Internationalization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..b1e212947256c5533c7b06285a597c94f840a6e3 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/CalendarSetting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/CalendarSetting.ets new file mode 100644 index 0000000000000000000000000000000000000000..f88948e05e54a3efba11074dd3136f5dc593cdd0 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/CalendarSetting.ets @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +// [Start check_and_set_date] +let calendar: i18n.Calendar = i18n.getCalendar('zh-Hans', 'gregory'); +// 设置日历对象的时间日期为2022.06.13 08:00:00 +calendar.setTime(new Date(2022, 5, 13, 8, 0, 0)); +calendar.setTime(10540800000); + +// 设置日历对象的时间日期为2022.06.13 08:00:00 +calendar.set(2022, 5, 13, 8, 0, 0); + +// 设置日历对象的时区 +calendar.setTimeZone('Asia/Shanghai'); + +// 获取日历对象的时区 +let timezone = calendar.getTimeZone(); // timezone = 'Asia/Shanghai' + +// 获取日历对象的一周起始日 +let firstDayOfWeek = calendar.getFirstDayOfWeek(); // firstDayOfWeek = 1 + +// 设置每一周的起始日 +calendar.setFirstDayOfWeek(1); + +// 获取一年中第一周的最小天数 +let minimalDaysInFirstWeek = calendar.getMinimalDaysInFirstWeek(); // minimalDaysInFirstWeek = 1 + +// 设置一年中第一周的最小天数 +calendar.setMinimalDaysInFirstWeek(3); + +// 获取日历对象中与field相关联的值 +let year = calendar.get('year'); // year = 2022 + +// 获取日历对象本地化名称 +let calendarName = calendar.getDisplayName('zh-Hans'); // calendarName = '公历' + +// 判断指定的日期在日历中是否为周末 +let isWeekend = calendar.isWeekend(new Date(2023, 9, 15)); // isWeekend = true + +// 在日历的给定字段进行加减操作 +calendar.set(2023, 10, 15); +calendar.add('date', 2); +let day = calendar.get('date'); // day = 17 + +// 比较日历和指定日期相差的天数 +let daysDifference = calendar.compareDays(new Date(2023, 10, 15)); // daysDifference = -3 +// [End check_and_set_date] + +// [Start get_lunar_date] +let calendarChinese: i18n.Calendar = i18n.getCalendar('zh-Hans', 'chinese'); +// 将公历日期设置到calendar对象,时间日期为2023.07.25 08:00:00 +calendarChinese.setTime(new Date(2023, 6, 25, 8, 0, 0)); + +// 获取农历年月日 +let yearChinese = calendarChinese.get('year'); // year = 40,指干支纪年40,范围1-60 +let monthChinese = calendarChinese.get('month'); // month = 5,指6月 +let dayChinese = calendarChinese.get('date'); // day = 8,指8日 +// [End get_lunar_date] + +const expectedTimezone = 'Asia/Shanghai'; +const expectedFirstDayOfWeek = 1; +const expectedMinimalDaysInFirstWeek = 1; +const expectedYear = 2022; +const expectedCalendarName = resourceToString($r('app.string.Calendar')); +const expectedIsWeekend = true; +const expectedDateAddTwoDays = 17; +const expectedCompareDays = -3; +const expectedCalendarYear = 40; +const expectedCalendarMonth = 5; +const expectedCalendarDate = 8; + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 15, left: 20, right: 20 }); +} + +@Entry +@Component +struct CalendarSetting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.CalendarSetting') }) + Scroll() { + Column() { + Text($r('app.string.ResultsOfTheGregorianCalendar')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text($r('app.string.TheGregorianCalendarSets')) + .textStyle() + Text(assertEqual(timezone, expectedTimezone, $r('app.string.Timezone'))) + .textStyle() + Text(assertEqual(firstDayOfWeek, expectedFirstDayOfWeek, $r('app.string.FirstDayOfWeek'))) + .textStyle() + Text(assertEqual(minimalDaysInFirstWeek, expectedMinimalDaysInFirstWeek, $r('app.string.MinimalDaysInFirstWeek'))) + .textStyle() + Text($r('app.string.SetMinimalDaysInFirstWeek')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(year, expectedYear, $r('app.string.YearOfTheFirstWeekIs3'))) + .textStyle() + Text(assertEqual(calendarName, expectedCalendarName, $r('app.string.LocalizedCalendarName'))) + .textStyle() + Text(assertEqual(isWeekend, expectedIsWeekend, $r('app.string.IsWeekend'))) + .textStyle() + Text(assertEqual(day, expectedDateAddTwoDays, $r('app.string.DateAfterAddingDays'))) + .textStyle() + Text(assertEqual(daysDifference, expectedCompareDays, + $r('app.string.DaysBetweenDates'))) + .textStyle() + Text($r('app.string.LunarCalendar')) + .textStyle().fontWeight(FontWeight.Bold) + Text(assertEqual(yearChinese, expectedCalendarYear, $r('app.string.LunarYear'))) + .textStyle() + Text(assertEqual(monthChinese, expectedCalendarMonth, $r('app.string.LunarMonth'))) + .textStyle() + Text(assertEqual(dayChinese, expectedCalendarDate, $r('app.string.LunarDate'))) + .textStyle() + }.alignItems(HorizontalAlign.Start) + }.id('outerScrollInCalendar') + .width('100%') + .height('100%') + }.width('100%').height('100%').alignItems(HorizontalAlign.Start) + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/CharacterProcessing.ets b/International/Internationalization/entry/src/main/ets/i18napplication/CharacterProcessing.ets new file mode 100644 index 0000000000000000000000000000000000000000..e0f162941af86053d45b4335db5b3e116e59c152 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/CharacterProcessing.ets @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual, assertEqualLong } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************************************************************************************************** + * 字符属性开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 判断字符属性 + * let isDigit: boolean = i18n.Unicode.isDigit(char: string); + * 3. 以一般类别值为例,判断字符类类型,具体请参考getType接口文档 + * let type = i18n.Unicode.getType(char: string); + *********************************************************************************************************************/ + +// [Start identify_character_type] +// 判断字符是否是数字 +let isDigit = i18n.Unicode.isDigit('1'); // isDigit = true + +// 判断字符是否是从右到左语言的字符 +let isRTL = i18n.Unicode.isRTL('a'); // isRTL = false + +// 判断字符是否是表意文字 +let isIdeograph = i18n.Unicode.isIdeograph('华'); // isIdeograph = true + +// 获取字符的一般类别值 +let unicodeType = i18n.Unicode.getType('a'); // unicodeType = 'U_LOWERCASE_LETTER' +// [End identify_character_type] + +/********************************************************************************************************************** + * 音译开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 创建Transliterator对象,获取音译列表 + * // 传入音译支持的ID,创建Transliterator对象 + * let transliterator: i18n.Transliterator = i18n.Transliterator.getInstance(id: string); + * let ids: string[] = i18n.Transliterator.getAvailableIDs(); // 获取音译支持的ID列表 + * 3. 音译文本 + * let res: string = transliterator.transform(text: string); // 对text内容进行音译 + *********************************************************************************************************************/ + +// [Start get_transliteration] +// 音译成Latn格式 +let transliterator: i18n.Transliterator = i18n.Transliterator.getInstance('Any-Latn'); +let translatedText = transliterator.transform('中国'); // translatedText = 'zhōng guó' + +// 汉语音译去声调 +let toneLessTransliterator: i18n.Transliterator = i18n.Transliterator.getInstance('Any-Latn;Latin-Ascii'); +let toneLessTranslatedText = toneLessTransliterator.transform('中国'); // toneLessTranslatedText = 'zhong guo' + +// 汉语姓氏读音 +let nameTransliterator: i18n.Transliterator = i18n.Transliterator.getInstance('Han-Latin/Names'); +let nameTranslatedText = nameTransliterator.transform('单老师'); // nameTranslatedText = 'shàn lǎo shī' + +// 获取音译支持的转换ID列表 +let ids = i18n.Transliterator.getAvailableIDs(); // ids = ['ASCII-Latin', 'Accents-Any', ...] +// [End get_transliteration] + +/********************************************************************************************************************** + * 字符标准化开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 创建标准化对象 + * let normalizer: i18n.Normalizer = i18n.Normalizer.getInstance(mode: NormalizerMode); + * 3. 文本标准化 + * let normalizedText: string = normalizer.normalize(text: string); // 对text文本进行标准化 + *********************************************************************************************************************/ + +// [Start character_normalization] +// 按照NFC范式对文本进行标准化处理 +let normalizer: i18n.Normalizer = i18n.Normalizer.getInstance(i18n.NormalizerMode.NFC); +let normalizedText = normalizer.normalize('\u1E9B\u0323'); // normalizedText = 'ẛ̣' +// [End character_normalization] + +/********************************************************************************************************************** + * 断词换行开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 创建用于断句的对象 + * let iterator: i18n.BreakIterator = i18n.getLineInstance(locale: string); + * 3. 设置要处理的文本 + * iterator.setLineBreakText(text: string); // 设置要处理的文本 + * let breakText: string = iterator.getLineBreakText(); // 查看iterator正在处理的文本 + * 4. 获取可断句的位置 + * // 获取iterator在当前所处理文本中的位置 + * let currentPos: number = iterator.current(); + * // 设置为第一个可断句的分割点,返回该分割点的位置。第一个分割点总是在文本的起始位置,firstPos = 0 + * let firstPos: number = iterator.first(); + * // 将iterator移动number数量个分割点,number为正数代表向后移动,number为负数代表向前移动,默认值为1。 + * // nextPos为移动后在文本中的位置,如果超出文本的长度范围,返回-1 + * let nextPos: number = iterator.next(number); + * // 判断number位置是否是分割点 + * let isBoundary: boolean = iterator.isBoundary(number); + *********************************************************************************************************************/ + +// [Start set_text_line_break_settings] +// 创建获取文本可换行点的对象,该对象将按照指定区域的规则计算文本中的可换行点的位置 +let iterator: i18n.BreakIterator = i18n.getLineInstance('en-GB'); + +// 设置处理文本 +iterator.setLineBreakText('Apple is my favorite fruit.'); + +// 将换行迭代器移动到文本起始位置 +let firstPos = iterator.first(); // firstPos = 0 + +// 将换行迭代器向后移动2个可换行点,nextPos为移动后在文本中的位置,如果超出文本的长度范围,返回-1 +let nextPos = iterator.next(2); // nextPos = 9 + +// 获取换行迭代器在当前所处理文本中的位置 +let currentPos = iterator.current(); // currentPos = 9 + +// 判断某个位置是否是可换行点 +let isBoundary = iterator.isBoundary(9); // isBoundary = true + +// 获取BreakIterator对象处理的文本 +let breakText = iterator.getLineBreakText(); // breakText = 'Apple is my favorite fruit.' +// [End set_text_line_break_settings] + +/********************************************************************************************************************** + * 文件路径镜像处理开发步骤参考 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 文件路径镜像处理 + * let mirrorPath: string = i18n.I18NUtil.getUnicodeWrappedFilePath(path: string, delimiter?: string, locale?: Intl.Locale); + *********************************************************************************************************************/ + +// [Start get_unicode_wrapped_file_path] +let mirrorPath = ''; +let unMirrorPath = ''; + +// 传入镜像语言,对路径进行镜像处理 +let path = 'data/out/tmp'; + +try { + let delimiter = '/'; + let locale: Intl.Locale = new Intl.Locale('ar'); + // mirrorPath = 'tmp/out/data/' + mirrorPath = i18n.I18NUtil.getUnicodeWrappedFilePath(path, delimiter, locale); + + // 传入非镜像语言,不处理路径 + let localeZh: Intl.Locale = new Intl.Locale('zh'); + // unMirrorPath = '/data/out/tmp' + unMirrorPath = i18n.I18NUtil.getUnicodeWrappedFilePath(path, delimiter, localeZh); +} catch (error) { + console.error(`call I18NUtil.getUnicodeWrappedFilePath failed, error code: ${error.code}, message: ${error.message}.`); +} +// [End get_unicode_wrapped_file_path] + +const expectedIsDigit = true; +const expectedIsRTL = false; +const expectedIsIdeograph = true; +const expectedType = 'U_LOWERCASE_LETTER'; +const expectedTransResult = 'zhōng guó'; +const expectedToneTranslatedText = 'zhong guo'; +const expectedNameTranslatedText = 'shàn lǎo shī'; +const expectedIds = ['ASCII-Latin', 'Accents-Any']; +const expectedNormalizedText = 'ẛ̣'; +const expectedFirstPos = 0; +const expectedNextPos = 9; +const expectedIsBoundary = true; +const expectedBreakText = 'Apple is my favorite fruit.'; + +@Extend(Text) +function textStyle() { + .fontSize(18) + .margin({ top: 15, left: 20, right: 20 }); +} + +@Entry +@Component +export default struct CharacterProcessing { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.CharacterProcessing') }) + Scroll() { + Column() { + Text($r('app.string.ResultsOfCharacterType')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(isDigit, expectedIsDigit, $r('app.string.Character1IsNumber'))) + .textStyle() + Text(assertEqual(isRTL, expectedIsRTL, $r('app.string.AIsRightToLeft'))) + .textStyle() + Text(assertEqual(isIdeograph, expectedIsIdeograph, $r('app.string.HuaIsIdeograph'))) + .textStyle() + Text(assertEqual(unicodeType, expectedType, $r('app.string.AGetType'))) + .textStyle() + Text($r('app.string.ResultsOfTrans')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(translatedText, expectedTransResult, $r('app.string.CharacterTransResult'))) + .textStyle() + Text(assertEqual(toneLessTranslatedText, expectedToneTranslatedText, $r('app.string.ChinaTrans'))) + .textStyle() + Text(assertEqual(nameTranslatedText, expectedNameTranslatedText, $r('app.string.TeachersSurnameTrans'))) + .textStyle() + Text(assertEqualLong(ids.join(', '), expectedIds, $r('app.string.ResultsOfSupportedId'))) + .textStyle() + Scroll() { + Text(ids.join(', ')) + .textStyle() + .id('transIdsText') + } + .id('transIdsScroll') + .margin({ left: 50 }) + .border({ + width: 2, + color: $r('app.color.Border_Gray'), + style: BorderStyle.Solid, + radius: 10 + }) + .width('80%') + .height('20%') + + Text($r('app.string.ResultsOfNormalize')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(normalizedText, expectedNormalizedText, $r('app.string.NFCNormalizeResult'))) + .textStyle() + Text($r('app.string.ResultsOfBreakIterator')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(firstPos, expectedFirstPos, $r('app.string.FirstPos'))) + .textStyle() + Text(assertEqual(nextPos, expectedNextPos, $r('app.string.NextPos'))) + .textStyle() + Text(assertEqual(isBoundary, expectedIsBoundary, $r('app.string.IsBoundary'))) + .textStyle() + Text(assertEqual(breakText, expectedBreakText, $r('app.string.BreakText'))) + .textStyle() + Text($r('app.string.ResultsOfMirror')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text($r('app.string.originPath')) + .textStyle() + Text(path) + .textStyle() + Text($r('app.string.MirrorLanguage')) + .textStyle() + Text(mirrorPath) + .textStyle() + Text($r('app.string.UnMirrorLanguage')) + .textStyle() + Text(unMirrorPath) + .textStyle() + Blank().height('10%') + } + .alignItems(HorizontalAlign.Start) + } + .id('outerScrollInCharacter') + .width('100%') + .height('100%') + } + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/LanguagePreferenceSetting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/LanguagePreferenceSetting.ets new file mode 100644 index 0000000000000000000000000000000000000000..29ef28386866ffc9f46491645ef84cb0eabd5852 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/LanguagePreferenceSetting.ets @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual } from '../component/AssertEqual'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +import { BusinessError, commonEventManager } from '@kit.BasicServicesKit'; +// [End import_module] + +// [Start get_system_language_and_region] +// 获取系统语言 +let systemLanguage = i18n.System.getSystemLanguage(); // systemLanguage为当前系统语言 + +// 获取系统地区 +let systemRegion = i18n.System.getSystemRegion(); // systemRegion为当前系统地区 + +// 获取系统区域 +let systemLocale: Intl.Locale = i18n.System.getSystemLocaleInstance(); // systemLocale为当前系统区域 + +// 通过监听公共事件COMMON_EVENT_LOCALE_CHANGED可以感知系统语言、系统地区或系统区域变化 +let subscriber: commonEventManager.CommonEventSubscriber; // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作 +let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + events: [commonEventManager.Support.COMMON_EVENT_LOCALE_CHANGED] +}; +// 创建订阅者 +commonEventManager.createSubscriber(subscribeInfo) + .then((commonEventSubscriber: commonEventManager.CommonEventSubscriber) => { + console.info('CreateSubscriber'); + subscriber = commonEventSubscriber; + commonEventManager.subscribe(subscriber, (err, data) => { + if (err) { + console.error(`Failed to subscribe common event. error code: ${err.code}, message: ${err.message}.`); + return; + } + console.info('The subscribed event has occurred.'); // 系统语言、系统地区或系统区域变化时执行 + }) + }) + .catch((err: BusinessError) => { + console.error(`CreateSubscriber failed, code is ${err.code}, message is ${err.message}`); + }); +// [End get_system_language_and_region] + +/********************************** + * 设置应用偏好语言 + *********************************/ +// [Start set_preferred_language] +try { + i18n.System.setAppPreferredLanguage('zh-Hans'); // 设置应用偏好语言为zh-Hans +} catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`call System.setAppPreferredLanguage failed, error code: ${err.code}, message: ${err.message}.`); +} +// [End set_preferred_language] + +// [Start get_preferred_language] +let appPreferredLanguage = i18n.System.getAppPreferredLanguage(); // 获取应用偏好语言 +// [End get_preferred_language] + +// [Start clear_preferred_language] +// 清除应用的偏好语言 +try { + i18n.System.setAppPreferredLanguage('default'); // 清除应用偏好语言 +} catch (error) { + let err: BusinessError = error as BusinessError; + console.error(`call System.setAppPreferredLanguage failed, error code: ${err.code}, message: ${err.message}.`); +} +// [End clear_preferred_language] + +// [Start get_user_preference] +// 判断系统当前是否使用本地数字 +let usingLocalDigit: boolean = i18n.System.getUsingLocalDigit(); + +// 判断系统当前是否使用24小时制 +let is24HourClock: boolean = i18n.System.is24HourClock(); + +// 通过监听公共事件COMMON_EVENT_TIME_CHANGED可以感知系统时制变化 +let timeSubscriber: commonEventManager.CommonEventSubscriber; // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作 +let timeSubscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + events: [commonEventManager.Support.COMMON_EVENT_TIME_CHANGED] +}; +// 创建订阅者 +commonEventManager.createSubscriber(timeSubscribeInfo) + .then((commonEventSubscriber: commonEventManager.CommonEventSubscriber) => { + console.info('CreateSubscriber'); + timeSubscriber = commonEventSubscriber; + commonEventManager.subscribe(timeSubscriber, (err, data) => { + if (err) { + console.error(`Failed to subscribe common event. error code: ${err.code}, message: ${err.message}.`); + return; + } + // 用于区分系统时间和系统时制变化 + if (data.data != undefined && data.data == '24HourChange') { + console.info('The subscribed event has occurred.'); // 系统时制变化时执行 + } + }) + }) + .catch((err: BusinessError) => { + console.error(`CreateSubscriber failed, code is ${err.code}, message is ${err.message}`); + }); +// [End get_user_preference] + +const expectedSystemLanguage = 'zh-Hans'; +const expectedSystemRegion = 'CN'; +const expectedSystemLocale = 'zh-Hans-CN'; +const expectedAppPreferredLanguage = 'zh-Hans'; +const expectedResult1 = false; +const expectedResult2 = false; + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 20, left: 30, right: 20 }); +} + +@Entry +@Component +struct LanguagePreferenceSetting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.LanguagePreferenceSetting') }) + Text($r('app.string.ResultsOfSetSystemLanguageAndRegion')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(systemLanguage, expectedSystemLanguage, $r('app.string.SetAndGetSystemLanguage'))) + .textStyle() + Text(assertEqual(systemRegion, expectedSystemRegion, $r('app.string.SetAndGetSystemRegion'))) + .textStyle() + Text(assertEqual(systemLocale.toString(), expectedSystemLocale, $r('app.string.SetAndGetSystemLocale'))) + .textStyle() + Text($r('app.string.ResultsOfSetPreferredLanguage')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(appPreferredLanguage, expectedAppPreferredLanguage, $r('app.string.SetAndGetPreferredLanguage'))) + .textStyle() + Text($r('app.string.ResultsOfSetUserPreference')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(usingLocalDigit, expectedResult1, $r('app.string.SetApplicationInterfaceNumber'))) + .textStyle() + Text(assertEqual(is24HourClock, expectedResult2, $r('app.string.SetFormatTo24Format'))) + .textStyle() + } + .width('100%') + .height('100%') + .alignItems(HorizontalAlign.Start) + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/MultilingualSorting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/MultilingualSorting.ets new file mode 100644 index 0000000000000000000000000000000000000000..665a8ca71c8cd4c67a2fbc950640918e84c80c1c --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/MultilingualSorting.ets @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual, assertEqualLong } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************************************************************************************************** + * 创建索引开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 创建对象 + * let indexUtil = i18n.getInstance(locale?:string); // locale 表示本地化标识符,默认值是系统当前locale + * 3. 以获取索引列表为例 + * let indexList = indexUtil.getIndexList(); + *********************************************************************************************************************/ +// [Start create_index] +// 创建索引 +let indexUtil: i18n.IndexUtil = i18n.getInstance('zh-CN'); +let indexList = indexUtil.getIndexList(); // indexList = ['…', 'A', 'B', 'C', ... 'X', 'Y', 'Z', '…'] + +// 多语言index混排 +indexUtil.addLocale('ru-RU'); +// indexList = ['…', 'A', 'B', 'C', ... 'X', 'Y', 'Z', '…', 'А', 'Б', 'В', ... 'Э', 'Ю', 'Я', '…'] +indexList = indexUtil.getIndexList(); + +// 获取字符串的索引值 +let index = indexUtil.getIndex('你好'); // index = 'N' +// [End create_index] + +const expectedIndexList = ['…', 'A', 'B', 'C', 'Б', 'Г', 'Д', 'Ю', 'Я', '…']; +const expectedIndex = 'N'; + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 20, left: 20, right: 20 }); +} + +@Entry +@Component +struct MultilingualSorting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.MultilingualSorting') }) + Text($r('app.string.ResultsOfCreateIndex')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqualLong(indexList.join(','), expectedIndexList, $r('app.string.ResultOfIndexList'))) + .textStyle(); + Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) { + Text(indexList.join(',')) + .textStyle() + }.direction(Direction.Ltr) + + Text(assertEqual(index, expectedIndex, + $r('app.string.ResultOfIndex'))) + .textStyle() + } + .alignItems(HorizontalAlign.Start) + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/NameLocalization.ets b/International/Internationalization/entry/src/main/ets/i18napplication/NameLocalization.ets new file mode 100644 index 0000000000000000000000000000000000000000..b4bfb48ba5109d936b47ed7ae3045dd52ca77412 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/NameLocalization.ets @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual, assertEqualFuzzy } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************** + * 本地化语言与地区名称开发步骤 + **********************************/ +// [Start localized_language_names] +let displayLanguage = i18n.System.getDisplayLanguage('de', 'zh-Hans-CN'); // displayLanguage = '德文' +// language: 语言两字母代码,如'zh','de','fr'等 +// locale: 表示区域ID的字符串,如'en-GB'、'en-US'、'zh-Hans-CN'等 +// sentenceCase: 返回的语言名称是否需要首字母大写,默认值:true +// [End localized_language_names] + +// [Start localized_country_names] +let displayCountry = i18n.System.getDisplayCountry('SA', 'en-GB'); // displayCountry = 'Saudi Arabia' +// country: 国家/地区两字母代码,如'CN'、'DE'、'SA'等 +// locale: 表示区域ID的字符串,如'en-GB'、'en-US'、'zh-Hans-CN'等 +// sentenceCase: 返回的国家/地区名称是否需要首字母大写,默认值:true +// [End localized_country_names] + +/********************************** + * 本地化时区名称开发步骤 + **********************************/ +// [Start localized_timezone_names] +let timezone = i18n.getTimeZone('America/Sao_Paulo'); +let timeZoneName = timezone.getDisplayName('zh-Hans', true); // 巴西利亚标准时间 +// [End localized_timezone_names] + +const expectedDisplayLanguage = resourceToString($r('app.string.ExpectedDisplayLanguage')); +const expectedDisplayCountry = 'Saudi Arabia'; +const expectedTimeZoneName = [resourceToString($r('app.string.ExpectedTimeZoneName'))]; + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 20, left: 20, right: 20 }); +} + +@Entry +@Component +struct NameLocalization { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.NameLocalization') }) + Text($r('app.string.ResultsOfLanguageRegion')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(displayLanguage, expectedDisplayLanguage, $r('app.string.DisplayLanguage'))) + .textStyle() + Text(assertEqual(displayCountry, expectedDisplayCountry, $r('app.string.DisplayCountry'))) + .textStyle() + Text($r('app.string.ResultsOfDisplayTimezone')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqualFuzzy(timeZoneName, expectedTimeZoneName, $r('app.string.ResultOfTimezoneName'))) + .textStyle() + } + .alignItems(HorizontalAlign.Start) + .width('100%') + .height('100%') + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/NumberMeasurementFormatting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/NumberMeasurementFormatting.ets new file mode 100644 index 0000000000000000000000000000000000000000..79feae4a6595757f438635d5cc0dbc672efca436 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/NumberMeasurementFormatting.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************************************************************************************************** + * 度量衡转换开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 度量衡转换 + * let convertedUnit: string = + * i18n.I18NUtil.unitConvert(fromUnit: UnitInfo, toUnit: UnitInfo, value: number, locale: string, style?: string); + *********************************************************************************************************************/ +// [Start measurement_conversion] +// 设置要转换的单位和目标单位 +let fromUnit: i18n.UnitInfo = {unit: 'cup', measureSystem: 'US'}; +let toUnit: i18n.UnitInfo = {unit: 'liter', measureSystem: 'SI'}; + +// 以en-US区域ID转换度量衡 +let simplifyConvertedUnit = i18n.I18NUtil.unitConvert(fromUnit, toUnit, 1000, 'en-US'); // simplifyConvertedUnit = '236.588 L' + +// 显示完整的度量衡 +let convertedUnit = i18n.I18NUtil.unitConvert(fromUnit, toUnit, 1000, 'en-US', 'long'); // convertedUnit = '236.588 liters' +// [End measurement_conversion] + +const expectedSimplifyConvertedUnit = '236.588 L'; +const expectedConvertedUnit = '236.588 liters'; + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 15, left: 30, right: 30 }); +} + +@Entry +@Component +struct NumberMeasurementFormatting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.NumberMeasurementFormatting') }) + Scroll() { + Column() { + Text($r('app.string.ResultsOfMeasureTrans')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(`${assertEqual(simplifyConvertedUnit, expectedSimplifyConvertedUnit, $r('app.string.MeasureTrans'))}`) + .textStyle() + Text(`${assertEqual(convertedUnit, expectedConvertedUnit, $r('app.string.LongMeasureTrans'))}`) + .textStyle() + Blank().height('10%') + }.alignItems(HorizontalAlign.Start) + } + } + .id('outerScrollInNumberMeasure') + .height('100%') + .width('100%') + .alignItems(HorizontalAlign.Start) + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/PhoneNumberFormatting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/PhoneNumberFormatting.ets new file mode 100644 index 0000000000000000000000000000000000000000..dcd5aad6210ec570de4cc599af4d4997130e7725 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/PhoneNumberFormatting.ets @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual } from '../component/AssertEqual'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************************************************************************************************** + * 电话号码格式化开发参考步骤 + * 1. 导入模块 + * import { i18n } from '@kit.LocalizationKit'; + * 2. 创建PhoneNumberFormat对象 + * let phoneNumberFormat: i18n.PhoneNumberFormat = + * new i18n.PhoneNumberFormat(country: string, options?: PhoneNumberFormatOptions); + * 3. 电话号码格式化 + * let formattedPhoneNumber: string = phoneNumberFormat.format(phoneNumber: string); + * 4. 判断电话号码正确性和号码归属地 + * let isValidNumber: boolean = phoneNumberFormat.isValidNumber(phoneNumber: string); // 判断电话号码正确性 + * let locationName: string = phoneNumberFormat.getLocationName(number: string, locale: string); // 获取号码归属地 + *********************************************************************************************************************/ +// [Start format_phone_numbers] +// 格式化电话号码 +let phoneNumberFormat: i18n.PhoneNumberFormat = new i18n.PhoneNumberFormat('CN'); +let formattedPhoneNumber = phoneNumberFormat.format('158****2312'); // formattedPhoneNumber = '158 **** 2312' + +// RFC3966类型的电话号码 +let rfcFormat: i18n.PhoneNumberFormat = new i18n.PhoneNumberFormat('CN', { type: 'RFC3966' }); +let formattedRFCPhoneNumber = rfcFormat.format('158****2312'); // formattedRFCPhoneNumber = 'tel:+86-158-****-2312' + +// 判断电话号码是否有效 +let isValid = phoneNumberFormat.isValidNumber('158****2312'); // isValid = true + +// 以某种语言显示号码归属地 +let locationName = phoneNumberFormat.getLocationName('158****2312', 'en-GB'); // locationName = 'XiAn, Shanxi' + +// 拨号中的电话号码格式化 +let typingFormat: i18n.PhoneNumberFormat = new i18n.PhoneNumberFormat('CN', { type: 'TYPING' }); +let phoneNumber = '0755453'; +let formatResult = ''; // 通过如下方式对拨号中的号码格式化后,formatResult = '0755 453' +for (let i = 0; i < phoneNumber.length; i++) { + formatResult += phoneNumber.charAt(i); + formatResult = typingFormat.format(formatResult); +} +// [End format_phone_numbers] + +const expectedFormattedPhoneNumber1 = '158 2312'; +const expectedFormattedPhoneNumber2 = 'tel:+86-1582312'; +const expectedIsValid = false; +const expectedLocationName4 = ''; +const expectedFormatResult = '0755 453' + +@Extend(Text) +function textStyle() { + .fontSize(20) + .margin({ top: 30, left: 30, right: 30 }); +} + +@Entry +@Component +struct PhoneNumberFormatting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.PhoneNumberFormatting') }) + Text($r('app.string.ResultsOfPhoneNumberFormat')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(formattedPhoneNumber, expectedFormattedPhoneNumber1, $r('app.string.PhoneNumberFormat'))) + .textStyle() + Text(assertEqual(formattedRFCPhoneNumber, expectedFormattedPhoneNumber2, $r('app.string.RFC3966TypeNumberFormat'))) + .textStyle() + Text(assertEqual(isValid, expectedIsValid, $r('app.string.IsValidNumber'))) + .textStyle() + Text(assertEqual(locationName, expectedLocationName4, $r('app.string.CNLocationName'))) + .textStyle() + Text(assertEqual(formatResult, expectedFormatResult, $r('app.string.FormatResult'))) + .textStyle() + } + .width('100%') + .height('100%') + .alignItems(HorizontalAlign.Start) + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/ets/i18napplication/TimezoneDstSetting.ets b/International/Internationalization/entry/src/main/ets/i18napplication/TimezoneDstSetting.ets new file mode 100644 index 0000000000000000000000000000000000000000..ec8ca9338d0a34a0c9833c4beef4e10ccf70b6a5 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/i18napplication/TimezoneDstSetting.ets @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import { assertEqual, assertEqualFuzzy, assertEqualLong } from '../component/AssertEqual'; +import { resourceToString } from '../component/ResourceToString'; +// [Start import_module] +import { i18n } from '@kit.LocalizationKit'; +// [End import_module] + +/********************************************************************************************************************** + * 时区开发步骤 + *********************************************************************************************************************/ +// [Start get_current_timezone] +// 获取巴西时区 +let timezone: i18n.TimeZone = i18n.getTimeZone('America/Sao_Paulo'); // 传入特定时区,创建时区对象 +let timezoneId = timezone.getID(); // timezoneId = 'America/Sao_Paulo' + +// 获取城市ID对应的时区对象 +let aucklandTimezone: i18n.TimeZone = i18n.TimeZone.getTimezoneFromCity('Auckland'); +timezoneId = aucklandTimezone.getID(); // timezoneId = 'Pacific/Auckland' + +// 获取时区的本地化名称 +let timeZoneName = timezone.getDisplayName('zh-Hans', true); // timeZoneName = '巴西利亚标准时间' + +// 本地化城市名称 +let cityDisplayName = i18n.TimeZone.getCityDisplayName('Auckland', 'zh-Hans'); // cityDisplayName = '奥克兰 (新西兰)' + +// 时区的固定偏移量 +let rawOffset = timezone.getRawOffset(); // rawOffset = -10800000 + +// 时区的实际偏移量(固定偏移量+夏令时) +let offset = timezone.getOffset(1234567890); // offset = -10800000 + +// 系统支持的时区ID列表 +let availableIDs = i18n.TimeZone.getAvailableIDs(); // availableIDs = ['America/Adak', 'Asia/Hovd', ...] + +// 系统支持的时区城市ID列表 +let cityIDs = i18n.TimeZone.getAvailableZoneCityIDs(); // cityIDs = ['Auckland', 'Magadan', ...] + +// 遍历时区城市ID列表 +let timezoneList: object[] = []; // 呈现给用户的时区列表 + +class Item { + public cityDisplayName = ''; + public timezoneId = ''; + public offset = ''; + public cityId = ''; +}; + +for (let i = 0; i < cityIDs.length; i++) { + let cityId = cityIDs[i]; + let timezone: i18n.TimeZone = i18n.TimeZone.getTimezoneFromCity(cityId); // 城市ID对应的时区对象 + let cityDisplayName = i18n.TimeZone.getCityDisplayName(cityId, 'zh-CN'); // 本地化城市名称 + let timestamp = (new Date()).getTime(); + let item: Item = { + cityDisplayName: cityDisplayName, + timezoneId: timezone.getID(), + offset: 'GMT' + (timezone.getOffset(timestamp) / 3600 * 1000), + cityId: cityId + }; + timezoneList.push(item); +} + +// 指定地理坐标所在的时区对象数组 +let timezoneArray: i18n.TimeZone[] = i18n.TimeZone.getTimezonesByLocation(-43.1, -22.5); + +// 获取指定时间的下一个时区跳变点 +let tijuanaTzId = 'America/Tijuana'; +let tijuanaTimeZone: i18n.TimeZone = i18n.getTimeZone(tijuanaTzId); // 获取蒂华纳时区对象 +let zoneRules: i18n.ZoneRules = tijuanaTimeZone.getZoneRules(); // 获取蒂华纳时区的时区跳变规则 +let someTime = new Date(2025, 4, 13); +let zoneOffsetTrans: i18n.ZoneOffsetTransition = zoneRules.nextTransition(someTime.getTime()); +zoneOffsetTrans.getMilliseconds(); // 跳变点的时间戳: 1762074000000 +zoneOffsetTrans.getOffsetAfter(); // 跳变后的偏移量: -28800000 +zoneOffsetTrans.getOffsetBefore(); // 跳变前的偏移量: -25200000 +// 将跳变点时间格式化 +let dateTimeFormat: Intl.DateTimeFormat = new Intl.DateTimeFormat('en-US', { + timeZone: tijuanaTzId, + dateStyle: 'long', + timeStyle: 'long', + hour12: false +}); +let dateFormat = + dateTimeFormat.format(new Date(zoneOffsetTrans.getMilliseconds())); // November 2, 2025, 1:00:00 PST +// [End get_current_timezone] + +// [Start display_dual_timezones] +let pauloTimezone: i18n.TimeZone = i18n.getTimeZone('America/Sao_Paulo'); +let defaultTimezone: i18n.TimeZone = i18n.getTimeZone(); +let appPreferredTimeZoneList: i18n.TimeZone[] = []; // 应用偏好时区列表 +appPreferredTimeZoneList.push(pauloTimezone); +appPreferredTimeZoneList.push(defaultTimezone); + +// [StartExclude display_dual_timezones] +let timeZoneData: string[] = []; // 用于存储每个时区的时间信息 +// [EndExclude display_dual_timezones] +let locale: Intl.Locale = i18n.System.getSystemLocaleInstance(); +for (let i = 0; i < appPreferredTimeZoneList.length; i++) { + let timezone = appPreferredTimeZoneList[i].getID(); + let calendar: i18n.Calendar = i18n.getCalendar(locale.toString()); + calendar.setTimeZone(timezone); // 设置日历对象的时区 + // 获取年月日时分秒 + let year = calendar.get('year'); + let month = calendar.get('month'); + let day = calendar.get('date'); + let hour = calendar.get('hour'); + let minute = calendar.get('minute'); + let second = calendar.get('second'); + // [StartExclude display_dual_timezones] + // 格式化时间信息并存储 + timeZoneData.push( + `${resourceToString($r('app.string.TimeZone'))} ${timezone}, ` + + `${resourceToString($r('app.string.Time'))} ${year}-${month}-${day} ${hour}:${minute}:${second}` + ); + // [EndExclude display_dual_timezones] +} +// [End display_dual_timezones] + +/********************************************************************************************************************** + * 夏令时跳变实现原理 + *********************************************************************************************************************/ +// [Start handle_dst_transition] +let calendar: i18n.Calendar = i18n.getCalendar('zh-Hans'); +calendar.setTimeZone('Europe/London'); +calendar.set(2021, 2, 27, 16, 0, 0); // 夏令时开始前的时间 +let startTime = calendar.getTimeInMillis(); +calendar.set(2021, 2, 28, 16, 0, 0); // 处于夏令时期间的时间 +let finishTime = calendar.getTimeInMillis(); +let hours = (finishTime - startTime) / (3600 * 1000); // hours = 23 +// [End handle_dst_transition] + +const expectedAucklandTzId = 'Pacific/Auckland'; +const expectedTimeZoneName = [resourceToString($r('app.string.ExpectedTimeZoneName'))]; +const expectedCityDisplayName = resourceToString($r('app.string.ExpectedCityDisplayName')); +; +const expectedRawOffset = -10800000; +const expectedOffset = -10800000; +const expectedIds = ['America/Adak', 'Asia/Hovd', 'America/Sao_Paulo', 'Asia/Jerusalem', 'Europe/London']; +const expectedTzId = 'America/Sao_Paulo'; +const expectedTimeZoneData = + [resourceToString($r('app.string.ExpectedTimeZoneData1')), resourceToString($r('app.string.ExpectedTimeZoneData2'))]; +const expectedHours = 23; + +@Extend(Text) +function textStyle() { + .fontSize(18) + .margin({ top: 15, left: 30, right: 20 }); +} + +@Extend(Scroll) +function scrollStyle(scrollIndex: string) { + .height('20%') + .width('80%') + .id(scrollIndex) + .margin({ left: 50 }) + .border({ + width: 2, + color: $r('app.color.Border_Gray'), + style: BorderStyle.Solid, + radius: 10 + }) +} + +@Entry +@Component +struct TimezoneDstSetting { + build() { + Column() { + TitleBar({ hasBackPress: true, title: $r('app.string.TimezoneDstSetting') }) + Scroll() { + Column() { + Text($r('app.string.ResultsOfTimezoneFunction')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual(timezoneId, expectedAucklandTzId, $r('app.string.GetAucklandID'))) + .textStyle() + Text(assertEqualFuzzy(timeZoneName, expectedTimeZoneName, $r('app.string.TimeZoneLocalName'))) + .textStyle() + Text(assertEqual(cityDisplayName, expectedCityDisplayName, $r('app.string.AucklandDisplayName'))) + .textStyle() + Text(assertEqual(rawOffset, expectedRawOffset, $r('app.string.GetRawOffset'))) + .textStyle() + Text(assertEqual(offset, expectedOffset, $r('app.string.GetOffset'))) + .textStyle() + Text(assertEqualLong(availableIDs.join(', '), expectedIds, $r('app.string.GetAvailableIDs'))) + .textStyle() + Scroll() { + Text(availableIDs.join(', ')) + .textStyle() + .id('idsText') + } + .scrollStyle('idsScroll') + + Text($r('app.string.GetAvailableZoneCityIDs')) + .textStyle() + Scroll() { + Text(cityIDs.join(', ')) + .textStyle() + .id('cityIdArrayText') + } + .scrollStyle('cityIdArrayScroll') + + Scroll() { + Column() { + ForEach(timezoneList, (item: Item, index: number) => { + Column() { + Text(`${item.cityDisplayName},${item.timezoneId},${item.offset},${item.cityId}`) + .textStyle() + .id(`timezoneListText${index}`) + Divider().margin({ top: 3, left: 30, right: 30 }); + } + }, (item: Item, index: number) => `${item.cityId}-${index}` + ); + } + } + .scrollStyle('timezoneListScroll') + + Text(assertEqualLong(JSON.stringify(timeZoneData), expectedTimeZoneData, + $r('app.string.ResultsOfDualClockFeature'))) + .textStyle() + ForEach(timeZoneData, (item: string, index: number) => { + Text(item) + .textStyle() + }, (item: string, index: number) => `${index}` // 生成唯一标识符 + ); + + Text($r('app.string.ResultsOfDaylightSavingTimeJumps')) + .textStyle() + .fontWeight(FontWeight.Bold) + Text(assertEqual( + hours, expectedHours, $r('app.string.DaylightSavingTimeHourDifference'))) + .textStyle() + Blank().height('5%') + } + .alignItems(HorizontalAlign.Start) + } + .id('outerScrollInTimezone') + .width('100%') + .height('100%') + } + } +} diff --git a/International/Internationalization/entry/src/main/ets/pages/Index.ets b/International/Internationalization/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..b9edd9fc75ba7976eaeefc0d5b9bd5cf1b449b95 --- /dev/null +++ b/International/Internationalization/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import TitleBar from '../component/TitleBar'; +import router from '@ohos.router'; +import { resourceToString } from '../component/ResourceToString'; + +interface Item { + text: string; +} + +const operationUrls: Array = [ + 'i18napplication/LanguagePreferenceSetting', + 'i18napplication/NumberMeasurementFormatting', + 'i18napplication/PhoneNumberFormatting', + 'i18napplication/CalendarSetting', + 'i18napplication/TimezoneDstSetting', + 'i18napplication/MultilingualSorting', + 'i18napplication/CharacterProcessing', + 'i18napplication/NameLocalization' +]; + +@Entry +@Component +struct Index { + + @State ListItem: Item[] = [ + { text: resourceToString($r('app.string.LanguagePreferenceSetting')) }, + { text: resourceToString($r('app.string.NumberMeasurementFormatting')) }, + { text: resourceToString($r('app.string.PhoneNumberFormatting')) }, + { text: resourceToString($r('app.string.CalendarSetting')) }, + { text: resourceToString($r('app.string.TimezoneDstSetting')) }, + { text: resourceToString($r('app.string.MultilingualSorting')) }, + { text: resourceToString($r('app.string.CharacterProcessing')) }, + { text: resourceToString($r('app.string.NameLocalization')) } + ] + + build() { + Column() { + TitleBar() + List() { + ForEach(this.ListItem, (item: Item, index) => { + ListItem() { + Row() { + Blank().width('4%') + Text(item.text) + .fontSize(16) + .fontColor('black') + .width('90%') + Image($r('app.media.right')) + .height(12) + .width(12) + } + .onClick(() => { + router.pushUrl({ + url: operationUrls[index] + }) + }) + .border({ radius: 20 }) + .width('90%') + .height('8%') + .backgroundColor(Color.White) + .margin({ top: 12, left: 15, right: 8 }) + } + }) + } + .height('90%') + .width('100%') + } + .width('100%') + .height('100%') + .backgroundColor('#F1F3F5') + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/module.json5 b/International/Internationalization/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..59b2ac7da873e1c8a2e21d5406d07072471e1524 --- /dev/null +++ b/International/Internationalization/entry/src/main/module.json5 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + "requestPermissions":[ + ] + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/element/color.json b/International/Internationalization/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..81f76279f4de11bdbe6a85c50232cb5eb3c521b8 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/color.json @@ -0,0 +1,12 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + }, + { + "name": "Border_Gray", + "value": "#F1F3F5" + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/element/strCalendarSetting.json b/International/Internationalization/entry/src/main/resources/base/element/strCalendarSetting.json new file mode 100644 index 0000000000000000000000000000000000000000..c17b15c33e08029d249f8160dd85ad339affa071 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strCalendarSetting.json @@ -0,0 +1,64 @@ +{ + "string": [ + { + "name": "ResultsOfTheGregorianCalendar", + "value": "公历相关用法: " + }, + { + "name": "TheGregorianCalendarSets", + "value": "设置公历时间为2023/5/13 08:00:00" + }, + { + "name": "Timezone", + "value": "该时间时区: " + }, + { + "name": "FirstDayOfWeek", + "value": "该时间第一周的起始日: " + }, + { + "name": "MinimalDaysInFirstWeek", + "value": "该年第一周的最少天数: " + }, + { + "name": "SetMinimalDaysInFirstWeek", + "value": "设置一年中第一周的最少天数为3天: " + }, + { + "name": "YearOfTheFirstWeekIs3", + "value": "第一周最少天数是3的年份: " + }, + { + "name": "LocalizedCalendarName", + "value": "日历的本地化名称: " + }, + { + "name": "IsWeekend", + "value": "2023/10/15是否为周末: " + }, + { + "name": "DateAfterAddingDays", + "value": "2023/10/25加上2天后的日期: " + }, + { + "name": "DaysBetweenDates", + "value": "两个日期之间相差的天数: " + }, + { + "name": "LunarCalendar", + "value": "获取公历2023年6月25日对应的农历日期结果: " + }, + { + "name": "LunarYear", + "value": "2023/6/25对应的农历年: " + }, + { + "name": "LunarMonth", + "value": "2023/6/25对应的农历月: " + }, + { + "name": "LunarDate", + "value": "2023/6/25对应的农历日: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strCharacterProcessing.json b/International/Internationalization/entry/src/main/resources/base/element/strCharacterProcessing.json new file mode 100644 index 0000000000000000000000000000000000000000..44979270f055d4680a3ade34f9f7b1934b2f6ca8 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strCharacterProcessing.json @@ -0,0 +1,92 @@ +{ + "string": [ + { + "name": "ResultsOfCharacterType", + "value": "字符属性测试结果: " + }, + { + "name": "Character1IsNumber", + "value": "判断字符1是否是数字: " + }, + { + "name": "AIsRightToLeft", + "value": "判断字符a是否是从右到左语言的字符: " + }, + { + "name": "HuaIsIdeograph", + "value": "判断字符‘华’是否是表意文字: " + }, + { + "name": "AGetType", + "value": "字符a的一般类别值: " + }, + { + "name": "ResultsOfTrans", + "value": "音译测试结果: " + }, + { + "name": "CharacterTransResult", + "value": "中国音译结果: " + }, + { + "name": "ChinaTrans", + "value": "汉语中国音译去声调: " + }, + { + "name": "TeachersSurnameTrans", + "value": "汉语单老师姓氏音译: " + }, + { + "name": "ZhangSunWuJiTrans", + "value": "汉语长孙无忌姓氏音译: " + }, + { + "name": "ResultsOfSupportedId", + "value": "音译支持的ID列表: " + }, + { + "name": "ResultsOfNormalize", + "value": "字符标准化测试结果: " + }, + { + "name": "NFCNormalizeResult", + "value": "以NFC范式标准化字符: " + }, + { + "name": "ResultsOfBreakIterator", + "value": "断句 Apple is my favorite fruit. 测试结果: " + }, + { + "name": "FirstPos", + "value": "首位置: " + }, + { + "name": "NextPos", + "value": "分割点位置: " + }, + { + "name": "IsBoundary", + "value": "是否是分割点: " + }, + { + "name": "BreakText", + "value": "断句文本: " + }, + { + "name": "ResultsOfMirror", + "value": "文件路径镜像处理测试结果: " + }, + { + "name": "originPath", + "value": "原始路径: " + }, + { + "name": "MirrorLanguage", + "value": "镜像语言: " + }, + { + "name": "UnMirrorLanguage", + "value": "非镜像语言: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strLanguagePreferenceSet.json b/International/Internationalization/entry/src/main/resources/base/element/strLanguagePreferenceSet.json new file mode 100644 index 0000000000000000000000000000000000000000..22ab9c93f56f246b6a1f53ff9a524a210deb65f5 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strLanguagePreferenceSet.json @@ -0,0 +1,40 @@ +{ + "string": [ + { + "name": "ResultsOfSetSystemLanguageAndRegion", + "value": "设置系统语言与区域结果: " + }, + { + "name": "SetAndGetSystemLanguage", + "value": "设置并获取系统语言为: " + }, + { + "name": "SetAndGetSystemRegion", + "value": "设置并获取系统地区为: " + }, + { + "name": "SetAndGetSystemLocale", + "value": "设置并获取系统区域为: " + }, + { + "name": "ResultsOfSetPreferredLanguage", + "value": "设置应用偏好语言结果: " + }, + { + "name": "SetAndGetPreferredLanguage", + "value": "设置并获取应用偏好语言为: " + }, + { + "name": "ResultsOfSetUserPreference", + "value": "设置用户偏好结果: " + }, + { + "name": "SetApplicationInterfaceNumber", + "value": "判断系统是否使用本地数字: " + }, + { + "name": "SetFormatTo24Format", + "value": "获取系统当前时制: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strMultilingualSort.json b/International/Internationalization/entry/src/main/resources/base/element/strMultilingualSort.json new file mode 100644 index 0000000000000000000000000000000000000000..db51a9b1ecb2d2021a460610fe6800be66fd5ffb --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strMultilingualSort.json @@ -0,0 +1,32 @@ +{ + "string": [ + { + "name": "ResultOfEnglishSort", + "value": "英文区分大小写排序结果: \n" + }, + { + "name": "ResultOfChineseSort", + "value": "中文拼音排序结果: \n" + }, + { + "name": "ResultOfChineseStrokeSort", + "value": "中文按笔画排序结果: \n" + }, + { + "name": "ResultOfSearchMatch", + "value": "搜索匹配的字符串结果: " + }, + { + "name": "ResultsOfCreateIndex", + "value": "创建索引结果: " + }, + { + "name": "ResultOfIndexList", + "value": "混合语言索引列表: " + }, + { + "name": "ResultOfIndex", + "value": "\"你好\" 的索引: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strNameLocalization.json b/International/Internationalization/entry/src/main/resources/base/element/strNameLocalization.json new file mode 100644 index 0000000000000000000000000000000000000000..692cd1d410b857f4f06bdae10ce82cd163c635a1 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strNameLocalization.json @@ -0,0 +1,24 @@ +{ + "string": [ + { + "name": "ResultsOfLanguageRegion", + "value": "本地化语言与地区名称测试结果: " + }, + { + "name": "DisplayLanguage", + "value": "显示语言(以德文为例): " + }, + { + "name": "DisplayCountry", + "value": "显示国家/地区(沙特阿拉伯): " + }, + { + "name": "ResultsOfDisplayTimezone", + "value": "本地化时区名称测试结果: " + }, + { + "name": "ResultOfTimezoneName", + "value": "显示时区名称(以美洲/圣保罗为例): " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strNumberMeasure.json b/International/Internationalization/entry/src/main/resources/base/element/strNumberMeasure.json new file mode 100644 index 0000000000000000000000000000000000000000..0e71837bf571a2ccb5bd36bb4fb1266c910060eb --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strNumberMeasure.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "ResultsOfMeasureTrans", + "value": "度量衡转换结果: " + }, + { + "name": "MeasureTrans", + "value": "以en-US区域参数转换度量衡: " + }, + { + "name": "LongMeasureTrans", + "value": "显示完整的度量衡: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strPhoneNumberFormat.json b/International/Internationalization/entry/src/main/resources/base/element/strPhoneNumberFormat.json new file mode 100644 index 0000000000000000000000000000000000000000..4b6f9c1c638ff08ffa3d45489ce4ca520b159194 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strPhoneNumberFormat.json @@ -0,0 +1,28 @@ +{ + "string": [ + { + "name": "ResultsOfPhoneNumberFormat", + "value": "电话号码格式化结果: " + }, + { + "name": "PhoneNumberFormat", + "value": "格式化电话号码: " + }, + { + "name": "RFC3966TypeNumberFormat", + "value": "RFC3966类型的电话号码: " + }, + { + "name": "IsValidNumber", + "value": "判断电话号码是否有效: " + }, + { + "name": "CNLocationName", + "value": "以某种语言(以中文为例)显示号码归属地: " + }, + { + "name": "FormatResult", + "value": "拨号中的电话号码格式化: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strTimezoneDstSetting.json b/International/Internationalization/entry/src/main/resources/base/element/strTimezoneDstSetting.json new file mode 100644 index 0000000000000000000000000000000000000000..edcabc679f2830c38253b3bc4c1dafc460cf0ad5 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strTimezoneDstSetting.json @@ -0,0 +1,48 @@ +{ + "string": [ + { + "name": "ResultsOfTimezoneFunction", + "value": "时区功能结果: " + }, + { + "name": "GetAucklandID", + "value": "获取奥克兰时区ID: " + }, + { + "name": "TimeZoneLocalName", + "value": "时区的本地化名称: " + }, + { + "name": "AucklandDisplayName", + "value": "奥克兰本地化城市名称: " + }, + { + "name": "GetRawOffset", + "value": "巴西时区的固定偏移量: " + }, + { + "name": "GetOffset", + "value": "巴西时区的实际偏移量: " + }, + { + "name": "GetAvailableIDs", + "value": "系统支持的时区ID列表: " + }, + { + "name": "GetAvailableZoneCityIDs", + "value": "系统支持的时区城市ID列表: " + }, + { + "name": "ResultsOfDualClockFeature", + "value": "双时钟应用功能结果: " + }, + { + "name": "ResultsOfDaylightSavingTimeJumps", + "value": "夏令时跳变测试结果: " + }, + { + "name": "DaylightSavingTimeHourDifference", + "value": "2021年2月27日16:00(夏令时前一天)到2021年2月28日16:00(夏令时当天)的小时差: " + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/strValue.json b/International/Internationalization/entry/src/main/resources/base/element/strValue.json new file mode 100644 index 0000000000000000000000000000000000000000..9f376f241b2bab71612b25898016a684baeb9595 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/strValue.json @@ -0,0 +1,76 @@ +{ + "string": [ + { + "name": "Calendar", + "value": "公历" + }, + { + "name": "CountryWordChina", + "value": "中国" + }, + { + "name": "TeacherShan", + "value": "单老师" + }, + { + "name": "ZhangSunWuJi", + "value": "长孙无忌" + }, + { + "name": "NiHao", + "value": "你好" + }, + { + "name": "ExpectedDisplayLanguage", + "value": "德语" + }, + { + "name": "ExpectedTimeZoneName", + "value": "巴西利亚" + }, + { + "name": "ExpectedTimeZone", + "value": "巴西利亚" + }, + { + "name": "ExpectedFormattedNumber2", + "value": "12万" + }, + { + "name": "ExpectedFormattedNumber6", + "value": "123,400.00美元" + }, + { + "name": "TimeZone", + "value": "时区:" + }, + { + "name": "Time", + "value": "时间:" + }, + { + "name": "ExpectedCityDisplayName", + "value": "奥克兰 (新西兰)" + }, + { + "name": "Adak", + "value": "埃达克 (美国)" + }, + { + "name": "Anchorage", + "value": "安克雷奇 (美国)" + }, + { + "name": "ExpectedTimeZoneData1", + "value": "时区: America/Sao_Paulo" + }, + { + "name": "Yerevan", + "value": "埃里温 (亚美尼亚)" + }, + { + "name": "ExpectedTimeZoneData2", + "value": "时区: Asia/Shanghai" + } + ] +} diff --git a/International/Internationalization/entry/src/main/resources/base/element/string.json b/International/Internationalization/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..77c0db3e2bafcc3fb41af75b1ed40e3c9a0cfddb --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/element/string.json @@ -0,0 +1,60 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "国际化" + }, + { + "name": "Internationalization", + "value": "国际化" + }, + { + "name": "LanguagePreferenceSetting", + "value": "设置语言与用户偏好" + }, + { + "name": "DateTimeFormatting", + "value": "时间日期国际化" + }, + { + "name": "NumberMeasurementFormatting", + "value": "度量衡国际化" + }, + { + "name": "PhoneNumberFormatting", + "value": "电话号码格式化" + }, + { + "name": "CalendarSetting", + "value": "设置日历和历法" + }, + { + "name": "TimezoneDstSetting", + "value": "时区与夏令时国际化" + }, + { + "name": "MultilingualSorting", + "value": "多语言排序" + }, + { + "name": "CharacterProcessing", + "value": "字符处理" + }, + { + "name": "NameLocalization", + "value": "本地化名称" + }, + { + "name": "reason", + "value": "用于更新系统配置" + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/media/back.svg b/International/Internationalization/entry/src/main/resources/base/media/back.svg new file mode 100644 index 0000000000000000000000000000000000000000..0614389ee6f66cd188d47ba045aa83b4babee356 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/media/back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/media/background.png b/International/Internationalization/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/International/Internationalization/entry/src/main/resources/base/media/background.png differ diff --git a/International/Internationalization/entry/src/main/resources/base/media/foreground.png b/International/Internationalization/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/International/Internationalization/entry/src/main/resources/base/media/foreground.png differ diff --git a/International/Internationalization/entry/src/main/resources/base/media/layered_image.json b/International/Internationalization/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/media/right.svg b/International/Internationalization/entry/src/main/resources/base/media/right.svg new file mode 100644 index 0000000000000000000000000000000000000000..f743acf7d50d9b4818e54d14596d2f71243ba7f2 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/media/right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/media/startIcon.png b/International/Internationalization/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/International/Internationalization/entry/src/main/resources/base/media/startIcon.png differ diff --git a/International/Internationalization/entry/src/main/resources/base/profile/backup_config.json b/International/Internationalization/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/base/profile/main_pages.json b/International/Internationalization/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..ff8f1f710ae67c72771aeb0889b6d2efe9b52fbe --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,13 @@ +{ + "src": [ + "pages/Index", + "i18napplication/LanguagePreferenceSetting", + "i18napplication/NumberMeasurementFormatting", + "i18napplication/PhoneNumberFormatting", + "i18napplication/CalendarSetting", + "i18napplication/TimezoneDstSetting", + "i18napplication/MultilingualSorting", + "i18napplication/CharacterProcessing", + "i18napplication/NameLocalization" + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/en_US/element/string.json b/International/Internationalization/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..16dc430425de75f443b5e702ee6732f7765b574a --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Internationalization" + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/main/resources/zh_CN/element/string.json b/International/Internationalization/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..4031df99cdc40128c73d9bd8d2085ba86411a1e8 --- /dev/null +++ b/International/Internationalization/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "国际化" + } + ] +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/mock/mock-config.json5 b/International/Internationalization/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b9a78e201535765168a92d3543c690273ecdc019 --- /dev/null +++ b/International/Internationalization/entry/src/mock/mock-config.json5 @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/ohosTest/ets/test/I18n.test.ets b/International/Internationalization/entry/src/ohosTest/ets/test/I18n.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..6b70c78bb26ad816e5e695e5b59c2b14fcb9a022 --- /dev/null +++ b/International/Internationalization/entry/src/ohosTest/ets/test/I18n.test.ets @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, MatchPattern, ON } from '@kit.TestKit'; +import hilog from '@ohos.hilog'; +import { resourceToString } from '../../../main/ets/component/ResourceToString'; + +const BUNDLE = 'I18nTest_'; +const TAG: string = '[Sample_Internationalization]'; +const DOMAIN = 0xF811; +const DELAY_TIME = 500; +const DELAY_TIME_LONG = 3000; +const SCROLL_SPEED = 3000; +const delegator = abilityDelegatorRegistry.getAbilityDelegator(); +const bundleName = abilityDelegatorRegistry.getArguments().bundleName; +let driver: Driver = Driver.create(); + +async function checkAndClickByText(text: string) { + await driver.assertComponentExist(ON.text(text, MatchPattern.CONTAINS)); + let component = await driver.findComponent(ON.text(text)); + await component.click(); + await driver.delayMs(DELAY_TIME); +} + +async function goBack() { + await driver.assertComponentExist(ON.id('btnBack')); + let backButton = await driver.findComponent(ON.id('btnBack')); + await backButton.click(); + await driver.delayMs(DELAY_TIME); +} + +async function scrollView(scrollId: string, textId: string, arr: string[]) { + let scroll = await driver.findComponent(ON.id(scrollId)); + await driver.delayMs(DELAY_TIME); + await scroll.scrollToBottom(SCROLL_SPEED); + let text = await driver.findComponents(ON.id(textId)); + expect(text).not().assertNull(); + if (!Array.isArray(text)) { + throw new Error('Expected an array of components, but received a different type'); + } + expect(text.length).not().assertNull(); + for (let index = 0; index < Math.min(text.length, arr.length); index++) { + const element = text[index]; + const actualText = await element.getText(); + expect(actualText.includes(arr[index].toString())).assertTrue(); + } + await driver.delayMs(DELAY_TIME); +} + +async function scrollOuterBottomView(scrollId: string) { + let scroll = await driver.findComponent(ON.id(scrollId)); + await driver.delayMs(DELAY_TIME); + await scroll.scrollToBottom(SCROLL_SPEED); + await driver.delayMs(DELAY_TIME); +} + +const expectedIdsText = ['America/Adak', 'Asia/Hovd', 'America/Sao_Paulo', 'Asia/Jerusalem', 'Europe/London']; +const expectedCityIdArrayText = ['Auckland', 'Magadan', 'Lord Howe Island']; +const expectedTransIdsText = ['ASCII-Latin', 'Accents-Any']; + +export default function abilityTest() { + describe('I18nTestSuite', () => { + /** + * @tc.number LaunchIndexPage_001 + * @tc.name LaunchIndexPage_001 + * @tc.desc 启动应用 + */ + it(BUNDLE + 'LaunchIndexPage_001', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'LaunchIndexPage_001, begin'); + await delegator.startAbility({ + bundleName: bundleName, + abilityName: 'EntryAbility' + }); + await driver.delayMs(DELAY_TIME_LONG); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.LanguagePreferenceSetting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.NumberMeasurementFormatting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.PhoneNumberFormatting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.CalendarSetting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.TimezoneDstSetting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.MultilingualSorting')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.CharacterProcessing')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.NameLocalization')))); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'LaunchIndexPage_001, end'); + }); + + /** + * @tc.number I18nTestLanguagePreferenceSetting_002 + * @tc.name I18nTestLanguagePreferenceSetting_002 + * @tc.desc 设置语言与用户偏好 + */ + it(BUNDLE + 'LanguagePreferenceSetting_002', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'LanguagePreferenceSetting_002, begin'); + await checkAndClickByText(resourceToString($r('app.string.LanguagePreferenceSetting'))); + await driver.assertComponentExist( + ON.text(resourceToString($r('app.string.ResultsOfSetSystemLanguageAndRegion')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.SetAndGetSystemLanguage'))}zh-Hans --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.SetAndGetSystemRegion'))}CN --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.SetAndGetSystemLocale'))}zh-Hans-CN --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfSetPreferredLanguage')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.SetAndGetPreferredLanguage'))}zh-Hans --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfSetUserPreference')))); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'LanguagePreferenceSetting_002, end'); + }); + + /** + * @tc.number I18nTestNumberMeasurementFormatting_003 + * @tc.name I18nTestNumberMeasurementFormatting_003 + * @tc.desc 数字与度量衡国际化 + */ + it(BUNDLE + 'NumberMeasurementFormatting_003', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'NumberMeasurementFormatting_003, begin'); + await checkAndClickByText(resourceToString($r('app.string.NumberMeasurementFormatting'))); + await scrollOuterBottomView('outerScrollInNumberMeasure'); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfMeasureTrans')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.MeasureTrans'))}236.588 L --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.LongMeasureTrans'))}236.588 liters --passed`)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'NumberMeasurementFormatting_003, end'); + }); + + /** + * @tc.number I18nTestPhoneNumberFormatting_004 + * @tc.name I18nTestPhoneNumberFormatting_004 + * @tc.desc 电话号码格式化 + */ + it(BUNDLE + 'PhoneNumberFormatting_004', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'PhoneNumberFormatting_004, begin'); + await checkAndClickByText(resourceToString($r('app.string.PhoneNumberFormatting'))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfPhoneNumberFormat')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.PhoneNumberFormat'))}158 2312 --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.RFC3966TypeNumberFormat'))}tel:+86-1582312 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.IsValidNumber'))}false --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.CNLocationName'))} --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.FormatResult'))}0755 453 --passed`)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'PhoneNumberFormatting_004, end'); + }); + + /** + * @tc.number I18nTestCalendarFormatting_005 + * @tc.name I18nTestCalendarFormatting_005 + * @tc.desc 设置日历和历法 + */ + it(BUNDLE + 'CalendarFormatting_005', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'CalendarFormatting_005, begin'); + await checkAndClickByText(resourceToString($r('app.string.CalendarSetting'))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfTheGregorianCalendar')))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.TheGregorianCalendarSets')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.Timezone'))}Asia/Shanghai --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.FirstDayOfWeek'))}1 --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.MinimalDaysInFirstWeek'))}1 --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.SetMinimalDaysInFirstWeek')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.YearOfTheFirstWeekIs3'))}2022 --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.LocalizedCalendarName'))}` + + `${resourceToString($r('app.string.Calendar'))} --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.IsWeekend'))}true --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.DateAfterAddingDays'))}17 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.DaysBetweenDates'))}-3 --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.LunarCalendar')))); + await scrollOuterBottomView('outerScrollInCalendar'); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.LunarYear'))}40 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.LunarMonth'))}5 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.LunarDate'))}8 --passed`)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'CalendarFormatting_005, end'); + }); + + /** + * @tc.number I18nTestTimezoneDstSetting_006 + * @tc.name I18nTestTimezoneDstSetting_006 + * @tc.desc 时区与夏令时国际化 + */ + it(BUNDLE + 'TimezoneDstSetting_006', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'TimezoneDstSetting_006, begin'); + await checkAndClickByText(resourceToString($r('app.string.TimezoneDstSetting'))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfTimezoneFunction')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.GetAucklandID'))}Pacific/Auckland --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.TimeZoneLocalName'))}` + + `${resourceToString($r('app.string.ExpectedTimeZone'))}`, MatchPattern.CONTAINS)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.AucklandDisplayName'))}` + + `${resourceToString($r('app.string.ExpectedCityDisplayName'))} --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.GetRawOffset'))}-10800000 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.GetOffset'))}-10800000 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.GetAvailableIDs'))}passed`)); + await scrollView('idsScroll', 'idsText', expectedIdsText) + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.GetAvailableZoneCityIDs')))); + await scrollView('cityIdArrayScroll', 'cityIdArrayText', expectedCityIdArrayText); + await scrollOuterBottomView('outerScrollInTimezone'); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'TimezoneDstSetting_006, end'); + }); + + /** + * @tc.number I18nTestMultilingualSorting_007 + * @tc.name I18nTestMultilingualSorting_007 + * @tc.desc 多语言排序 + */ + it(BUNDLE + 'MultilingualSorting_007', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'MultilingualSorting_007, begin'); + await checkAndClickByText(resourceToString($r('app.string.MultilingualSorting'))); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.ResultOfIndexList'))}passed`)); + await driver.assertComponentExist(ON.text('…,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,' + + '…,А,Б,В,Г,Д,Е,Ж,З,И,Й,К,Л,М,Н,О,П,Р,С,Т,У,Ф,Х,Ц,Ч,Ш,Щ,Ы,Э,Ю,Я,…')); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.ResultOfIndex'))}N --passed`)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'MultilingualSorting_007, end'); + }); + + /** + * @tc.number I18nTestCharacterProcessing_008 + * @tc.name I18nTestCharacterProcessing_008 + * @tc.desc 字符处理 + */ + it(BUNDLE + 'CharacterProcessing_008', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'CharacterProcessing_008, begin'); + await checkAndClickByText(resourceToString($r('app.string.CharacterProcessing'))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfCharacterType')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.Character1IsNumber'))}true --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.AIsRightToLeft'))}false --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.HuaIsIdeograph'))}true --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.AGetType'))}U_LOWERCASE_LETTER --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfTrans')))); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.CharacterTransResult'))}zhōng guó --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.ChinaTrans'))}zhong guo --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.TeachersSurnameTrans'))}shàn lǎo shī --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.ResultsOfSupportedId'))}passed`)); + await scrollView('transIdsScroll', 'transIdsText', expectedTransIdsText); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfNormalize')))); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.NFCNormalizeResult'))}ẛ̣ --passed`)); + await scrollOuterBottomView('outerScrollInCharacter'); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfBreakIterator')))); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.FirstPos'))}0 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.NextPos'))}9 --passed`)); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.IsBoundary'))}true --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.BreakText'))}Apple is my favorite fruit. --passed`)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'CharacterProcessing_008, end'); + }); + + /** + * @tc.number I18nTestNameLocalization_009 + * @tc.name I18nTestNameLocalization_009 + * @tc.desc 本地化名称 + */ + it(BUNDLE + 'NameLocalization_009', 0, async (done: Function) => { + hilog.info(DOMAIN, TAG, BUNDLE + 'NameLocalization_009, begin'); + await checkAndClickByText(resourceToString($r('app.string.NameLocalization'))); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfLanguageRegion')))); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.DisplayLanguage'))}` + + `${resourceToString($r('app.string.ExpectedDisplayLanguage'))} --passed`)); + await driver.assertComponentExist( + ON.text(`${resourceToString($r('app.string.DisplayCountry'))}Saudi Arabia --passed`)); + await driver.assertComponentExist(ON.text(resourceToString($r('app.string.ResultsOfDisplayTimezone')))); + await driver.assertComponentExist(ON.text(`${resourceToString($r('app.string.ResultOfTimezoneName'))}` + + `${resourceToString($r('app.string.ExpectedTimeZone'))}`, MatchPattern.CONTAINS)); + await goBack(); + done(); + hilog.info(DOMAIN, TAG, BUNDLE + 'NameLocalization_009, end'); + }); + }); +} diff --git a/International/Internationalization/entry/src/ohosTest/ets/test/List.test.ets b/International/Internationalization/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..d3eaa18d1646f14944ca7e811c1c711bb5fb1e13 --- /dev/null +++ b/International/Internationalization/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import abilityTest from './I18n.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/ohosTest/module.json5 b/International/Internationalization/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/International/Internationalization/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/International/Internationalization/entry/src/test/List.test.ets b/International/Internationalization/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/International/Internationalization/entry/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/International/Internationalization/entry/src/test/LocalUnit.test.ets b/International/Internationalization/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/International/Internationalization/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/International/Internationalization/hvigor/hvigor-config.json5 b/International/Internationalization/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1697fd431b2114706801dc6a171777ce7bc4049c --- /dev/null +++ b/International/Internationalization/hvigor/hvigor-config.json5 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "modelVersion": "6.0.1", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/International/Internationalization/hvigorfile.ts b/International/Internationalization/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a5e543f190732c159beb574dfc9fa37bc94e156 --- /dev/null +++ b/International/Internationalization/hvigorfile.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/International/Internationalization/oh-package.json5 b/International/Internationalization/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ded5e289634fca6fc3fede0f37ad92005359e501 --- /dev/null +++ b/International/Internationalization/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "modelVersion": "6.0.1", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.24", + "@ohos/hamock": "1.0.1-rc2" + } +} diff --git a/International/Internationalization/ohosTest.md b/International/Internationalization/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..e5fc70ef667fe3070565745ada3c7c4f22a01f04 --- /dev/null +++ b/International/Internationalization/ohosTest.md @@ -0,0 +1,16 @@ +# Internationalization国际化测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +| :------: | :------: | :------: | :------: | :------: | :------: | +| 拉起应用1 | 设备正常运行 | \ | 成功拉起应用,在主页停留3秒,判断主页Text显示正确。 | 是 | Pass | +| 测试实例2 | 位于设置语言与用户偏好页面 | \ | 点击设置语言与用户偏好模块按钮,切换到实例3页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例3 | 位于时间日期国际化页面 | \ | 点击时间日期国际化模块按钮,切换到实例4页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例4 | 位于数字与度量衡国际化页面 | \ | 点击数字与度量衡国际化模块按钮,切换到实例5页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例5 | 位于电话号码格式化页面 | \ | 点击电话号码格式化模块按钮,切换到实例6页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例6 | 位于设置日历和历法页面 | \ | 点击设置日历和历法模块按钮,切换到实例7页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | pass | +| 测试实例7 | 位于时区与夏令时国际化页面 | \ | 点击时区与夏令时国际化模块按钮,切换到实例8页面,并判断输出Text结果正确,测试滑动窗口可以滚动并测试窗口内Text内容输出正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例8 | 位于多语言排序页面 | \ | 点击多语言排序模块按钮,切换到实例9页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例9 | 位于字符处理页面 | \ | 点击字符处理模块按钮,切换到实例10页面,并判断输出Text结果正确,测试滑动窗口可以滚动并测试窗口内Text内容输出正确,并退出当前页面返回主页面。 | 是 | Pass | +| 测试实例10 | 位于字符处理页面 | \ | 点击字符处理模块按钮,切换到实例11页面,并判断输出Text结果正确,并退出当前页面返回主页面。 | 是 | Pass | diff --git a/International/Internationalization/screenshots/CalendarSetting.jpg b/International/Internationalization/screenshots/CalendarSetting.jpg new file mode 100644 index 0000000000000000000000000000000000000000..292367f8c098e6eaf2bcc494d8b013d3bd6a942e Binary files /dev/null and b/International/Internationalization/screenshots/CalendarSetting.jpg differ diff --git a/International/Internationalization/screenshots/DateTimeFormatting.jpg b/International/Internationalization/screenshots/DateTimeFormatting.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7055c483465c69090407bbf162ad2af5b3b44069 Binary files /dev/null and b/International/Internationalization/screenshots/DateTimeFormatting.jpg differ diff --git a/International/Internationalization/screenshots/MainPage.jpg b/International/Internationalization/screenshots/MainPage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..682550074650651525c083f6f52d4254ebe19adc Binary files /dev/null and b/International/Internationalization/screenshots/MainPage.jpg differ diff --git a/International/Internationalization/screenshots/PhoneNumberFormatting.jpg b/International/Internationalization/screenshots/PhoneNumberFormatting.jpg new file mode 100644 index 0000000000000000000000000000000000000000..021872455fcbd52080165a88c1a24eebb5279524 Binary files /dev/null and b/International/Internationalization/screenshots/PhoneNumberFormatting.jpg differ