1
0
mirror of https://gitcode.com/gh_mirrors/re/react-native-pushy.git synced 2025-10-23 01:36:43 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

bump version to 10.31.0-beta.0 and integrate i18n for improved localization support

This commit is contained in:
sunnylqm
2025-09-04 10:24:52 +08:00
parent e58903a634
commit 584f698329
10 changed files with 335 additions and 85 deletions

View File

@@ -12,7 +12,7 @@
"react-native-paper": "^5.14.5",
"react-native-safe-area-context": "^5.5.0",
"react-native-svg": "^15.12.0",
"react-native-update": "^10.29.4",
"react-native-update": "^10.31.0-beta.0",
"react-native-vector-icons": "^10.2.0",
},
"devDependencies": {
@@ -1420,7 +1420,7 @@
"react-native-svg": ["react-native-svg@15.12.0", "", { "dependencies": { "css-select": "^5.1.0", "css-tree": "^1.1.3", "warn-once": "0.1.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-iE25PxIJ6V0C6krReLquVw6R0QTsRTmEQc4K2Co3P6zsimU/jltcDBKYDy1h/5j9S/fqmMeXnpM+9LEWKJKI6A=="],
"react-native-update": ["react-native-update@10.29.4", "", { "dependencies": { "nanoid": "^3.3.3", "react-native-url-polyfill": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-native": ">=0.59.0" } }, "sha512-leQX3dq4yBi/oFn0l06nXd7OOFnZnlcMIrAXH7vgTRsqXCdYLoSsZXXkcSYxncn8tBqzh02w4880mlqouve6Sg=="],
"react-native-update": ["react-native-update@10.31.0-beta.0", "", { "dependencies": { "nanoid": "^3.3.3", "react-native-url-polyfill": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-native": ">=0.59.0" } }, "sha512-kQJxvc0q+acBfdBfeSjs8tq+0B79c+9QZUrHgLX9lWhnIl4qZ17QZ/WJOdkpPq6asPjUiCrUxdEhfp0lr50p4w=="],
"react-native-url-polyfill": ["react-native-url-polyfill@2.0.0", "", { "dependencies": { "whatwg-url-without-unicode": "8.0.0-3" }, "peerDependencies": { "react-native": "*" } }, "sha512-My330Do7/DvKnEvwQc0WdcBnFPploYKp9CYlefDXzIdEaA+PAhDYllkvGeEroEzvc4Kzzj2O4yVdz8v6fjRvhA=="],

View File

@@ -1407,7 +1407,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-update (10.29.4):
- react-native-update (10.31.0-beta.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1423,8 +1423,7 @@ PODS:
- React-hermes
- React-ImageManager
- React-jsi
- react-native-update/HDiffPatch (= 10.29.4)
- react-native-update/RCTPushy (= 10.29.4)
- react-native-update/RCTPushy (= 10.31.0-beta.0)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
@@ -1435,33 +1434,7 @@ PODS:
- ReactCommon/turbomodule/core
- SSZipArchive
- Yoga
- react-native-update/HDiffPatch (10.29.4):
- DoubleConversion
- glog
- hermes-engine
- RCT-Folly (= 2024.11.18.00)
- RCTRequired
- RCTTypeSafety
- React
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-hermes
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SSZipArchive
- Yoga
- react-native-update/RCTPushy (10.29.4):
- react-native-update/RCTPushy (10.31.0-beta.0):
- DoubleConversion
- glog
- hermes-engine
@@ -2187,7 +2160,7 @@ SPEC CHECKSUMS:
React-Mapbuffer: c3f4b608e4a59dd2f6a416ef4d47a14400194468
React-microtasksnativemodule: 054f34e9b82f02bd40f09cebd4083828b5b2beb6
react-native-safe-area-context: 11d29ae675265669f498d7d9de2341087e8fe162
react-native-update: 6d3a3eb322cbc382ad78853cb52e44e8c93e8072
react-native-update: 25c349c4edf9dc895beeb1281cccc9d93f1cb3be
React-NativeModulesApple: 2c4377e139522c3d73f5df582e4f051a838ff25e
React-oscompat: ef5df1c734f19b8003e149317d041b8ce1f7d29c
React-perflogger: 9a151e0b4c933c9205fd648c246506a83f31395d

View File

@@ -22,7 +22,7 @@
"react-native-paper": "^5.14.5",
"react-native-safe-area-context": "^5.5.0",
"react-native-svg": "^15.12.0",
"react-native-update": "^10.29.4",
"react-native-update": "^10.31.0-beta.0",
"react-native-vector-icons": "^10.2.0"
},
"devDependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "react-native-update",
"version": "10.30.4",
"version": "10.31.0-beta.0",
"description": "react-native hot update",
"main": "src/index",
"scripts": {

View File

@@ -28,6 +28,7 @@ import {
promiseAny,
testUrls,
} from './utils';
import i18n from './i18n';
const SERVER_PRESETS = {
// cn
@@ -107,13 +108,18 @@ export class Pushy {
})();
constructor(options: ClientOptions, clientType?: 'Pushy' | 'Cresc') {
if (Platform.OS === 'ios' || Platform.OS === 'android') {
if (!options.appKey) {
throw new Error('appKey is required');
}
}
this.clientType = clientType || 'Pushy';
this.options.server = SERVER_PRESETS[this.clientType];
// Initialize i18n based on clientType
i18n.setLocale(this.clientType === 'Pushy' ? 'zh' : 'en');
if (Platform.OS === 'ios' || Platform.OS === 'android') {
if (!options.appKey) {
throw new Error(i18n.t('error_appkey_required'));
}
}
this.setOptions(options);
if (isRolledBack) {
this.report({
@@ -136,6 +142,16 @@ export class Pushy {
}
};
/**
* Get translated text based on current clientType
* @param key - Translation key
* @param values - Values for interpolation (optional)
* @returns Translated string
*/
t = (key: string, values?: Record<string, string | number>) => {
return i18n.t(key as any, values);
};
report = async ({
type,
message = '',
@@ -175,11 +191,7 @@ export class Pushy {
};
assertDebug = (matter: string) => {
if (__DEV__ && !this.options.debug) {
console.info(
`You are currently in the development environment and have not enabled debug mode.
${matter} will not be performed.
If you need to debug ${matter} in the development environment, please set debug to true in the client.`,
);
console.info(this.t('dev_debug_disabled', { matter }));
return false;
}
return true;
@@ -270,7 +282,7 @@ export class Pushy {
} catch (e: any) {
this.report({
type: 'errorChecking',
message: `Can not connect to update server: ${e.message}. Trying backup endpoints.`,
message: this.t('error_cannot_connect_backup', { message: e.message }),
});
const backupEndpoints = await this.getBackupEndpoints();
if (backupEndpoints) {
@@ -290,14 +302,17 @@ export class Pushy {
if (!resp) {
this.report({
type: 'errorChecking',
message: 'Can not connect to update server. Please check your network.',
message: this.t('error_cannot_connect_server'),
});
this.throwIfEnabled(new Error('errorChecking'));
return this.lastRespJson ? await this.lastRespJson : emptyObj;
}
if (resp.status !== 200) {
const errorMessage = `${resp.status}: ${resp.statusText}`;
const errorMessage = this.t('error_http_status', {
status: resp.status,
statusText: resp.statusText,
});
this.report({
type: 'errorChecking',
message: errorMessage,
@@ -416,7 +431,9 @@ export class Pushy {
});
succeeded = 'diff';
} catch (e: any) {
const errorMessage = `diff error: ${e.message}`;
const errorMessage = this.t('error_diff_failed', {
message: e.message,
});
errorMessages.push(errorMessage);
lastError = new Error(errorMessage);
log(errorMessage);
@@ -433,7 +450,9 @@ export class Pushy {
});
succeeded = 'pdiff';
} catch (e: any) {
const errorMessage = `pdiff error: ${e.message}`;
const errorMessage = this.t('error_pdiff_failed', {
message: e.message,
});
errorMessages.push(errorMessage);
lastError = new Error(errorMessage);
log(errorMessage);
@@ -451,7 +470,9 @@ export class Pushy {
});
succeeded = 'full';
} catch (e: any) {
const errorMessage = `full patch error: ${e.message}`;
const errorMessage = this.t('error_full_patch_failed', {
message: e.message,
});
errorMessages.push(errorMessage);
lastError = new Error(errorMessage);
log(errorMessage);

108
src/i18n.ts Normal file
View File

@@ -0,0 +1,108 @@
import zhTranslations from './locales/zh';
import enTranslations from './locales/en';
type TranslationKey = keyof typeof zhTranslations | keyof typeof enTranslations;
type TranslationValues = Record<string, string | number>;
class I18n {
private currentLocale: 'zh' | 'en' = 'en';
private translations = {
zh: zhTranslations,
en: enTranslations,
};
/**
* Set locale directly
* @param locale - 'zh' or 'en'
*/
setLocale(locale: 'zh' | 'en') {
this.currentLocale = locale;
}
/**
* Get current locale
*/
getLocale(): 'zh' | 'en' {
return this.currentLocale;
}
/**
* Translate a key with optional interpolation
* @param key - Translation key
* @param values - Values for interpolation (optional)
* @returns Translated string with interpolated values
*/
t(key: TranslationKey, values?: TranslationValues): string {
const translation =
this.translations[this.currentLocale][
key as keyof (typeof this.translations)[typeof this.currentLocale]
];
if (!translation) {
// Fallback to the other locale if key not found
const fallbackLocale = this.currentLocale === 'zh' ? 'en' : 'zh';
const fallbackTranslation =
this.translations[fallbackLocale][
key as keyof (typeof this.translations)[typeof fallbackLocale]
];
if (!fallbackTranslation) {
// If still not found, return the key itself
return String(key);
}
return this.interpolate(fallbackTranslation, values);
}
return this.interpolate(translation, values);
}
/**
* Interpolate values into a string template
* Supports {{key}} syntax
* @param template - String template with {{key}} placeholders
* @param values - Values to interpolate
* @returns Interpolated string
*/
private interpolate(template: string, values?: TranslationValues): string {
if (!values) {
return template;
}
return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
const value = values[key];
return value !== undefined ? String(value) : match;
});
}
/**
* Add or update translations for a specific locale
* @param locale - Target locale
* @param translations - Translation object to merge
*/
addTranslations(locale: 'zh' | 'en', translations: Record<string, string>) {
this.translations[locale] = {
...this.translations[locale],
...translations,
};
}
}
// Create singleton instance
const i18n = new I18n();
// Export both the instance and the class for flexibility
export { i18n, I18n };
export default i18n;
/**
* Usage examples:
*
* // Direct locale setting (new preferred method)
* i18n.setLocale('zh'); // Chinese
* i18n.setLocale('en'); // English
*
* // Get translations
* i18n.t('checking_update'); // Based on current locale
* i18n.t('download_progress', { progress: 50 }); // With interpolation
*/

70
src/locales/en.ts Normal file
View File

@@ -0,0 +1,70 @@
export default {
// Common messages
checking_update: 'Checking for updates...',
downloading_update: 'Downloading update package...',
installing_update: 'Installing update...',
update_available: 'Update available',
update_downloaded: 'Update downloaded successfully',
update_installed: 'Update installed successfully',
no_update_available: 'You are up to date',
update_failed: 'Update failed',
network_error: 'Network connection error',
download_failed: 'Download failed',
install_failed: 'Installation failed',
// Progress messages with interpolation
download_progress: 'Download progress: {{progress}}%',
download_speed: 'Download speed: {{speed}}/s',
file_size: 'File size: {{size}}',
time_remaining: 'Time remaining: {{time}}',
// Error messages
error_code: 'Error code: {{code}}',
error_message: 'Error message: {{message}}',
retry_count: 'Retry attempt: {{count}}/{{max}}',
// Update info
version_info: 'Version {{version}} ({{build}})',
release_notes: 'Release notes: {{notes}}',
update_size: 'Update size: {{size}}MB',
// Alert messages
alert_title: 'Notice',
alert_update_ready: 'Download completed. Update now?',
alert_next_time: 'Later',
alert_update_now: 'Update Now',
alert_app_updated:
'Your app version has been updated. Click update to download and install the new version',
alert_update_button: 'Update',
alert_cancel: 'Cancel',
alert_confirm: 'OK',
alert_info: 'Info',
alert_no_update_wait:
'No update found, please wait 10s for the server to generate the patch package',
// Error messages
error_appkey_required: 'appKey is required',
error_update_check_failed: 'Update check failed',
error_cannot_connect_server:
'Can not connect to update server. Please check your network.',
error_cannot_connect_backup:
'Can not connect to update server: {{message}}. Trying backup endpoints.',
error_diff_failed: 'diff error: {{message}}',
error_pdiff_failed: 'pdiff error: {{message}}',
error_full_patch_failed: 'full patch error: {{message}}',
error_all_promises_rejected: 'All promises were rejected',
error_ping_failed: 'Ping failed',
error_ping_timeout: 'Ping timeout',
error_http_status: '{{status}} {{statusText}}',
// Development messages
dev_debug_disabled:
'You are currently in the development environment and have not enabled debug mode. {{matter}} will not be performed. If you need to debug {{matter}} in the development environment, please set debug to true in the client.',
dev_log_prefix: 'react-native-update: ',
dev_web_not_supported:
'react-native-update does not support the Web platform and will not perform any operations',
// More alert messages
alert_new_version_found:
'New version {{name}} found. Download now?\n{{description}}',
};

67
src/locales/zh.ts Normal file
View File

@@ -0,0 +1,67 @@
export default {
// Common messages
checking_update: '正在检查更新...',
downloading_update: '正在下载更新包...',
installing_update: '正在安装更新...',
update_available: '发现新版本',
update_downloaded: '更新包下载完成',
update_installed: '更新安装完成',
no_update_available: '已是最新版本',
update_failed: '更新失败',
network_error: '网络连接错误',
download_failed: '下载失败',
install_failed: '安装失败',
// Progress messages with interpolation
download_progress: '下载进度: {{progress}}%',
download_speed: '下载速度: {{speed}}/s',
file_size: '文件大小: {{size}}',
time_remaining: '剩余时间: {{time}}',
// Error messages
error_code: '错误代码: {{code}}',
error_message: '错误信息: {{message}}',
retry_count: '重试次数: {{count}}/{{max}}',
// Update info
version_info: '版本 {{version}} ({{build}})',
release_notes: '更新说明: {{notes}}',
update_size: '更新包大小: {{size}}MB',
// Alert messages
alert_title: '提示',
alert_update_ready: '下载完毕,是否立即更新?',
alert_next_time: '下次再说',
alert_update_now: '立即更新',
alert_app_updated: '您的应用版本已更新,点击更新下载安装新版本',
alert_update_button: '更新',
alert_cancel: '取消',
alert_confirm: '确定',
alert_info: '信息',
alert_no_update_wait: '未发现更新请等待10秒让服务器生成补丁包',
// Error messages
error_appkey_required: '需要提供 appKey',
error_update_check_failed: '更新检查失败',
error_cannot_connect_server: '无法连接到更新服务器。请检查网络连接。',
error_cannot_connect_backup:
'无法连接到更新服务器: {{message}}。正在尝试备用端点。',
error_diff_failed: 'diff 错误: {{message}}',
error_pdiff_failed: 'pdiff 错误: {{message}}',
error_full_patch_failed: '完整补丁错误: {{message}}',
error_all_promises_rejected: '所有请求都被拒绝',
error_ping_failed: 'Ping 失败',
error_ping_timeout: 'Ping 超时',
error_http_status: '{{status}} {{statusText}}',
// Development messages
dev_debug_disabled:
'您当前处于开发环境且未启用调试模式。{{matter}} 将不会执行。如需在开发环境中调试 {{matter}},请在客户端中将 debug 设为 true。',
dev_log_prefix: 'react-native-update: ',
dev_web_not_supported:
'react-native-update 不支持 Web 平台,不会执行任何操作',
// More alert messages
alert_new_version_found:
'检查到新的版本{{name}},是否下载?\n{{description}}',
};

View File

@@ -120,16 +120,16 @@ export const UpdateProvider = ({
client.switchVersionLater(hash);
return true;
}
alertUpdate('提示', '下载完毕,是否立即更新?', [
alertUpdate(client.t('alert_title'), client.t('alert_update_ready'), [
{
text: '下次再说',
text: client.t('alert_next_time'),
style: 'cancel',
onPress: () => {
client.switchVersionLater(hash);
},
},
{
text: '立即更新',
text: client.t('alert_update_now'),
style: 'default',
onPress: () => {
client.switchVersion(hash);
@@ -139,7 +139,7 @@ export const UpdateProvider = ({
return true;
} catch (e: any) {
setLastError(e);
alertError('更新失败', e.message);
alertError(client.t('update_failed'), e.message);
throwErrorIfEnabled(e);
return false;
}
@@ -168,7 +168,7 @@ export const UpdateProvider = ({
rootInfo = await client.checkUpdate(extra);
} catch (e: any) {
setLastError(e);
alertError('更新检查失败', e.message);
alertError(client.t('error_update_check_failed'), e.message);
throwErrorIfEnabled(e);
return;
}
@@ -211,21 +211,25 @@ export const UpdateProvider = ({
}
return info;
}
alertUpdate('提示', '您的应用版本已更新,点击更新下载安装新版本', [
{
text: '更新',
onPress: () => {
if (
Platform.OS === 'android' &&
downloadUrl.endsWith('.apk')
) {
downloadAndInstallApk(downloadUrl);
} else {
Linking.openURL(downloadUrl);
}
alertUpdate(
client.t('alert_title'),
client.t('alert_app_updated'),
[
{
text: client.t('alert_update_button'),
onPress: () => {
if (
Platform.OS === 'android' &&
downloadUrl.endsWith('.apk')
) {
downloadAndInstallApk(downloadUrl);
} else {
Linking.openURL(downloadUrl);
}
},
},
},
]);
],
);
}
} else if (info.update) {
if (
@@ -236,12 +240,15 @@ export const UpdateProvider = ({
return info;
}
alertUpdate(
'提示',
'检查到新的版本' + info.name + ',是否下载?\n' + info.description,
client.t('alert_title'),
client.t('alert_new_version_found', {
name: info.name,
description: info.description,
}),
[
{ text: '取消', style: 'cancel' },
{ text: client.t('alert_cancel'), style: 'cancel' },
{
text: '确定',
text: client.t('alert_confirm'),
style: 'default',
onPress: () => {
downloadUpdate();
@@ -313,8 +320,8 @@ export const UpdateProvider = ({
checkUpdate({ extra: { toHash: payload.data } }).then(() => {
if (updateInfoRef.current && updateInfoRef.current.upToDate) {
Alert.alert(
'Info',
'No update found, please wait 10s for the server to generate the patch package',
client.t('alert_info'),
client.t('alert_no_update_wait'),
);
}
options.logger = logger;
@@ -324,7 +331,7 @@ export const UpdateProvider = ({
}
return false;
},
[checkUpdate, options],
[checkUpdate, options, client],
);
const parseTestQrCode = useCallback(

View File

@@ -1,7 +1,8 @@
import { Platform } from 'react-native';
import i18n from './i18n';
export function log(...args: any[]) {
console.log('react-native-update: ', ...args);
console.log(i18n.t('dev_log_prefix'), ...args);
}
export function promiseAny<T>(promises: Promise<T>[]) {
@@ -14,7 +15,7 @@ export function promiseAny<T>(promises: Promise<T>[]) {
.catch(() => {
count++;
if (count === promises.length) {
reject(new Error('All promises were rejected'));
reject(new Error(i18n.t('error_all_promises_rejected')));
}
});
});
@@ -49,7 +50,7 @@ const ping =
return finalUrl;
}
log('ping failed', url, status, statusText);
throw new Error('Ping failed');
throw new Error(i18n.t('error_ping_failed'));
})
.catch(e => {
pingFinished = true;
@@ -58,7 +59,7 @@ const ping =
}),
new Promise((_, reject) =>
setTimeout(() => {
reject(new Error('Ping timeout'));
reject(new Error(i18n.t('error_ping_timeout')));
if (!pingFinished) {
log('ping timeout', url);
}
@@ -91,9 +92,7 @@ export const testUrls = async (urls?: string[]) => {
export const assertWeb = () => {
if (Platform.OS === 'web') {
console.warn(
'react-native-update does not support the Web platform and will not perform any operations',
);
console.warn(i18n.t('dev_web_not_supported'));
return false;
}
return true;
@@ -115,7 +114,12 @@ export const enhancedFetch = async (
if (r.ok) {
return r;
}
throw new Error(`${r.status} ${r.statusText}`);
throw new Error(
i18n.t('error_http_status', {
status: r.status,
statusText: r.statusText,
}),
);
})
.catch(e => {
log('fetch error', url, e);