Skip to content

Commit 9cb8d45

Browse files
bozaigaosunnylqm
andauthored
add logic to support multi language (#11)
* add logic to support multi language * Update en.ts * Update en.ts --------- Co-authored-by: Sunny Luo <sunnylqm@gmail.com>
1 parent d853918 commit 9cb8d45

File tree

8 files changed

+170
-57
lines changed

8 files changed

+170
-57
lines changed

src/bundle.ts

+18-24
Original file line numberDiff line numberDiff line change
@@ -214,17 +214,13 @@ async function runReactNativeBundleCommand({
214214

215215
reactNativeBundleProcess.on('close', async (exitCode) => {
216216
if (exitCode) {
217-
reject(
218-
new Error(
219-
`"react-native bundle" command exited with code ${exitCode}.`,
220-
),
221-
);
217+
reject(new Error(t('bundleCommandError', { code: exitCode })));
222218
} else {
223219
let hermesEnabled: boolean | undefined = false;
224220

225221
if (disableHermes) {
226222
hermesEnabled = false;
227-
console.log('Hermes disabled');
223+
console.log(t('hermesDisabled'));
228224
} else if (platform === 'android') {
229225
const gradlePropeties = await new Promise<{
230226
hermesEnabled?: boolean;
@@ -283,8 +279,8 @@ async function copyHarmonyBundle(outputFolder: string) {
283279
await fs.ensureDir(outputFolder);
284280
await fs.copy(harmonyRawPath, outputFolder);
285281
} catch (error: any) {
286-
console.error('copyHarmonyBundle 错误:', error);
287-
throw new Error(`复制文件失败: ${error.message}`);
282+
console.error(t('copyHarmonyBundleError', { error }));
283+
throw new Error(t('copyFileFailed', { error: error.message }));
288284
}
289285
}
290286

@@ -355,7 +351,7 @@ async function compileHermesByteCode(
355351
);
356352
args.push('-output-source-map');
357353
}
358-
console.log(`Running hermesc: ${hermesCommand} ${args.join(' ')}`);
354+
console.log(t('runningHermesc', { command: hermesCommand, args: args.join(' ') }));
359355
spawnSync(hermesCommand, args, {
360356
stdio: 'ignore',
361357
});
@@ -365,7 +361,7 @@ async function compileHermesByteCode(
365361
if (!fs.existsSync(composerPath)) {
366362
return;
367363
}
368-
console.log('Composing source map');
364+
console.log(t('composingSourceMap'));
369365
spawnSync(
370366
'node',
371367
[
@@ -400,16 +396,14 @@ async function copyDebugidForSentry(
400396
},
401397
);
402398
} catch (error) {
403-
console.error(
404-
'无法找到 Sentry copy-debugid.js 脚本文件,请确保已正确安装 @sentry/react-native',
405-
);
399+
console.error(t('sentryCliNotFound'));
406400
return;
407401
}
408402

409403
if (!fs.existsSync(copyDebugidPath)) {
410404
return;
411405
}
412-
console.log('Copying debugid');
406+
console.log(t('copyingDebugId'));
413407
spawnSync(
414408
'node',
415409
[
@@ -453,9 +447,9 @@ async function uploadSourcemapForSentry(
453447
stdio: 'inherit',
454448
},
455449
);
456-
console.log(`Sentry release created for version: ${version}`);
450+
console.log(t('sentryReleaseCreated', { version }));
457451

458-
console.log('Uploading sourcemap');
452+
console.log(t('uploadingSourcemap'));
459453
spawnSync(
460454
'node',
461455
[
@@ -479,7 +473,7 @@ async function uploadSourcemapForSentry(
479473
const ignorePackingFileNames = ['.', '..', 'index.bundlejs.map'];
480474
const ignorePackingExtensions = ['DS_Store','txt.map'];
481475
async function pack(dir: string, output: string) {
482-
console.log('Packing');
476+
console.log(t('packing'));
483477
fs.ensureDirSync(path.dirname(output));
484478
await new Promise<void>((resolve, reject) => {
485479
const zipfile = new ZipFile();
@@ -516,7 +510,7 @@ async function pack(dir: string, output: string) {
516510
});
517511
zipfile.end();
518512
});
519-
console.log(t('ppkPackageGenerated', { output }));
513+
console.log(t('fileGenerated', { file: output }));
520514
}
521515

522516
export function readEntire(entry: string, zipFile: ZipFile) {
@@ -671,7 +665,7 @@ async function diffFromPPK(origin: string, next: string, output: string) {
671665

672666
for (const k in originEntries) {
673667
if (!newEntries[k]) {
674-
console.log(`Delete ${k}`);
668+
console.log(t('deleteFile', { file: k }));
675669
deletes[k] = 1;
676670
}
677671
}
@@ -844,7 +838,7 @@ export async function enumZipEntries(
844838
await result;
845839
}
846840
} catch (error) {
847-
console.error('处理文件时出错:', error);
841+
console.error(t('processingError', { error }));
848842
}
849843

850844
zipfile.readEntry();
@@ -860,7 +854,7 @@ function diffArgsCheck(args, options, diffFn) {
860854
const [origin, next] = args;
861855

862856
if (!origin || !next) {
863-
console.error(`Usage: pushy ${diffFn} <origin> <next>`);
857+
console.error(t('usageDiff', { command: diffFn }));
864858
process.exit(1);
865859
}
866860

@@ -927,7 +921,7 @@ export const commands = {
927921
const realOutput = output.replace(/\$\{time\}/g, `${Date.now()}`);
928922

929923
if (!platform) {
930-
throw new Error('Platform must be specified.');
924+
throw new Error(t('platformRequired'));
931925
}
932926

933927
console.log(`Bundling with react-native: ${depVersions['react-native']}`);
@@ -972,14 +966,14 @@ export const commands = {
972966
async diff({ args, options }) {
973967
const { origin, next, realOutput } = diffArgsCheck(args, options, 'diff');
974968

975-
await diffFromPPK(origin, next, realOutput, 'index.bundlejs');
969+
await diffFromPPK(origin, next, realOutput);
976970
console.log(`${realOutput} generated.`);
977971
},
978972

979973
async hdiff({ args, options }) {
980974
const { origin, next, realOutput } = diffArgsCheck(args, options, 'hdiff');
981975

982-
await diffFromPPK(origin, next, realOutput, 'index.bundlejs');
976+
await diffFromPPK(origin, next, realOutput);
983977
console.log(`${realOutput} generated.`);
984978
},
985979

src/locales/en.ts

+48
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,52 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
4040
operationSuccess: 'Operation successful',
4141
failedToParseUpdateJson: 'Failed to parse file `update.json`. Try to remove it manually.',
4242
ppkPackageGenerated: 'ppk package generated and saved to: {{- output}}',
43+
Message: 'Welcome to Cresc hot update service, {{name}}.',
44+
loggedOut: 'Logged out',
45+
usageUnderDevelopment: 'Usage is under development now.',
46+
hermesDisabled: 'Hermes disabled',
47+
hermesEnabled: 'Hermes enabled, now compiling to hermes bytecode:\n',
48+
runningHermesc: 'Running hermesc: {{command}} {{args}}',
49+
composingSourceMap: 'Composing source map',
50+
copyingDebugId: 'Copying debugid',
51+
sentryCliNotFound: 'Cannot find Sentry CLI tool, please make sure @sentry/cli is properly installed',
52+
sentryReleaseCreated: 'Sentry release created for version: {{version}}',
53+
uploadingSourcemap: 'Uploading sourcemap',
54+
packing: 'Packing',
55+
deletingFile: 'Delete {{file}}',
56+
bundlingWithRN: 'Bundling with react-native: {{version}}',
57+
fileGenerated: '{{file}} generated.',
58+
processingError: 'Error processing file: {{error}}',
59+
usageDiff: 'Usage: cresc {{command}} <origin> <next>',
60+
pluginDetected: 'detected {{name}} plugin',
61+
pluginDetectionError: 'error while detecting {{name}} plugin: {{error}}',
62+
addedToGitignore: 'Added {{line}} to .gitignore',
63+
processingStringPool: 'Processing the string pool ...',
64+
processingPackage: 'Processing the package {{count}} ...',
65+
typeStrings: 'Type strings:',
66+
keyStrings: 'Key strings:',
67+
failedToParseIcon: '[Warning] failed to parse icon: {{error}}',
68+
errorInHarmonyApp: 'Error in getEntryFromHarmonyApp: {{error}}',
69+
totalPackages: 'Total {{count}} packages',
70+
usageUploadIpa: 'Usage: cresc uploadIpa <ipa file>',
71+
usageUploadApk: 'Usage: cresc uploadApk <apk file>',
72+
usageUploadApp: 'Usage: cresc uploadApp <app file>',
73+
usageParseApp: 'Usage: cresc parseApp <app file>',
74+
usageParseIpa: 'Usage: cresc parseIpa <ipa file>',
75+
usageParseApk: 'Usage: cresc parseApk <apk file>',
76+
offset: 'Offset {{offset}}',
77+
packageUploadSuccess: 'Successfully uploaded new hot update package (id: {{id}})',
78+
rolloutRangeError: 'rollout must be an integer between 1-100',
79+
nativeVersionNotFound: 'No native version found >= {{version}}',
80+
nativeVersionNotFoundLess: 'No native version found <= {{version}}',
81+
nativeVersionNotFoundMatch: 'No matching native version found: {{version}}',
82+
packageIdRequired: 'Please provide packageId or packageVersion parameter',
83+
operationComplete: 'Operation complete, bound to {{count}} native versions',
84+
platformRequired: 'Platform must be specified.',
85+
bundleCommandError: '"react-native bundle" command exited with code {{code}}.',
86+
copyHarmonyBundleError: 'Error copying Harmony bundle: {{error}}',
87+
copyFileFailed: 'Failed to copy file: {{error}}',
88+
deleteFile: 'Delete {{file}}',
89+
rolloutConfigSet: 'Set {{rollout}}% rollout for version {{version}} on native version(s) {{versions}}',
90+
versionBind: 'Bound version {{version}} to native version {{nativeVersion}} (id: {{id}})',
4391
};

src/locales/zh.ts

+48
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,52 @@ export default {
3838
operationSuccess: '操作成功',
3939
failedToParseUpdateJson: '无法解析文件 `update.json`。请手动删除它。',
4040
ppkPackageGenerated: 'ppk 热更包已生成并保存到: {{- output}}',
41+
welcomeMessage: '欢迎使用 pushy 热更新服务,{{name}}。',
42+
loggedOut: '已退出登录',
43+
usageUnderDevelopment: '使用说明正在开发中。',
44+
hermesDisabled: 'Hermes 已禁用',
45+
hermesEnabled: 'Hermes 已启用,正在编译为 hermes 字节码:\n',
46+
runningHermesc: '运行 hermesc:{{command}} {{args}}',
47+
composingSourceMap: '正在生成 source map',
48+
copyingDebugId: '正在复制 debugid',
49+
sentryCliNotFound: '无法找到 Sentry CLI 工具,请确保已正确安装 @sentry/cli',
50+
sentryReleaseCreated: '已为版本 {{version}} 创建 Sentry release',
51+
uploadingSourcemap: '正在上传 sourcemap',
52+
packing: '正在打包',
53+
deletingFile: '删除 {{file}}',
54+
bundlingWithRN: '正在使用 react-native {{version}} 打包',
55+
fileGenerated: '已生成 {{file}}。',
56+
processingError: '处理文件时出错:{{error}}',
57+
usageDiff: '用法:pushy {{command}} <origin> <next>',
58+
pluginDetected: '检测到 {{name}} 插件',
59+
pluginDetectionError: '检测 {{name}} 插件时出错:{{error}}',
60+
addedToGitignore: '已将 {{line}} 添加到 .gitignore',
61+
processingStringPool: '正在处理字符串池...',
62+
processingPackage: '正在处理包 {{count}}...',
63+
typeStrings: '类型字符串:',
64+
keyStrings: '键字符串:',
65+
failedToParseIcon: '[警告] 解析图标失败:{{error}}',
66+
errorInHarmonyApp: '获取 Harmony 应用入口时出错:{{error}}',
67+
totalPackages: '共 {{count}} 个包',
68+
usageUploadIpa: '使用方法: pushy uploadIpa ipa后缀文件',
69+
usageUploadApk: '使用方法: pushy uploadApk apk后缀文件',
70+
usageUploadApp: '使用方法: pushy uploadApp app后缀文件',
71+
usageParseApp: '使用方法: pushy parseApp app后缀文件',
72+
usageParseIpa: '使用方法: pushy parseIpa ipa后缀文件',
73+
usageParseApk: '使用方法: pushy parseApk apk后缀文件',
74+
offset: '偏移量 {{offset}}',
75+
packageUploadSuccess: '已成功上传新热更包(id: {{id}})',
76+
rolloutRangeError: 'rollout 必须是 1-100 的整数',
77+
nativeVersionNotFound: '未查询到 >= {{version}} 的原生版本',
78+
nativeVersionNotFoundLess: '未查询到 <= {{version}} 的原生版本',
79+
nativeVersionNotFoundMatch: '未查询到匹配原生版本:{{version}}',
80+
packageIdRequired: '请提供 packageId 或 packageVersion 参数',
81+
operationComplete: '操作完成,共已绑定 {{count}} 个原生版本',
82+
platformRequired: '必须指定平台。',
83+
bundleCommandError: '"react-native bundle" 命令退出,代码为 {{code}}。',
84+
copyHarmonyBundleError: '复制 Harmony bundle 错误:{{error}}',
85+
copyFileFailed: '复制文件失败:{{error}}',
86+
deleteFile: '删除 {{file}}',
87+
rolloutConfigSet: '已在原生版本 {{versions}} 上设置灰度发布 {{rollout}}% 热更版本 {{version}}',
88+
versionBind: '已将热更版本 {{version}} 绑定到原生版本 {{nativeVersion}} (id: {{id}})',
4189
};

src/package.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { get, post, uploadFile } from './api';
22
import { question, saveToLocal } from './utils';
3+
import { t } from './utils/i18n';
34

45
import { checkPlatform, getSelectedApp } from './app';
56

@@ -34,7 +35,7 @@ export async function listPackage(appId: string) {
3435
}
3536

3637
console.log(Table(header, rows).render());
37-
console.log(`\n共 ${data.length} 个包`);
38+
console.log(t('totalPackages', { count: data.length }));
3839
return data;
3940
}
4041

@@ -54,7 +55,7 @@ export const commands = {
5455
uploadIpa: async ({ args }: { args: string[] }) => {
5556
const fn = args[0];
5657
if (!fn || !fn.endsWith('.ipa')) {
57-
throw new Error('使用方法: pushy uploadIpa ipa后缀文件');
58+
throw new Error(t('usageUploadIpa'));
5859
}
5960
const {
6061
versionName,
@@ -93,7 +94,7 @@ export const commands = {
9394
uploadApk: async ({ args }: { args: string[] }) => {
9495
const fn = args[0];
9596
if (!fn || !fn.endsWith('.apk')) {
96-
throw new Error('使用方法: pushy uploadApk apk后缀文件');
97+
throw new Error(t('usageUploadApk'));
9798
}
9899
const {
99100
versionName,
@@ -132,7 +133,7 @@ export const commands = {
132133
uploadApp: async ({ args }: { args: string[] }) => {
133134
const fn = args[0];
134135
if (!fn || !fn.endsWith('.app')) {
135-
throw new Error('使用方法: pushy uploadApp app后缀文件');
136+
throw new Error(t('usageUploadApp'));
136137
}
137138
const {
138139
versionName,
@@ -171,21 +172,21 @@ export const commands = {
171172
parseApp: async ({ args }: { args: string[] }) => {
172173
const fn = args[0];
173174
if (!fn || !fn.endsWith('.app')) {
174-
throw new Error('使用方法: pushy parseApp app后缀文件');
175+
throw new Error(t('usageParseApp'));
175176
}
176177
console.log(await getAppInfo(fn));
177178
},
178179
parseIpa: async ({ args }: { args: string[] }) => {
179180
const fn = args[0];
180181
if (!fn || !fn.endsWith('.ipa')) {
181-
throw new Error('使用方法: pushy parseIpa ipa后缀文件');
182+
throw new Error(t('usageParseIpa'));
182183
}
183184
console.log(await getIpaInfo(fn));
184185
},
185186
parseApk: async ({ args }: { args: string[] }) => {
186187
const fn = args[0];
187188
if (!fn || !fn.endsWith('.apk')) {
188-
throw new Error('使用方法: pushy parseApk apk后缀文件');
189+
throw new Error(t('usageParseApk'));
189190
}
190191
console.log(await getApkInfo(fn));
191192
},

src/user.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { question } from './utils';
22
import { post, get, replaceSession, saveSession, closeSession } from './api';
33
import crypto from 'node:crypto';
4+
import { t } from './utils/i18n';
45

56
function md5(str: string) {
67
return crypto.createHash('md5').update(str).digest('hex');
@@ -16,11 +17,11 @@ export const commands = {
1617
});
1718
replaceSession({ token });
1819
await saveSession();
19-
console.log(`欢迎使用 pushy 热更新服务, ${info.name}.`);
20+
console.log(t('welcomeMessage', { name: info.name }));
2021
},
2122
logout: async () => {
2223
await closeSession();
23-
console.log('已退出登录');
24+
console.log(t('loggedOut'));
2425
},
2526
me: async () => {
2627
const me = await get('/user/me');

src/utils/add-gitignore.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import fs from 'node:fs';
22
// import path from 'node:path';
33
import { credentialFile, tempDir } from './constants';
4+
import { t } from './i18n';
45

56
export function addGitIgnore() {
67
const shouldIgnore = [credentialFile, tempDir];
@@ -26,7 +27,7 @@ export function addGitIgnore() {
2627
gitignoreLines.push('# react-native-update');
2728
for (const line of shouldIgnore) {
2829
gitignoreLines.push(line);
29-
console.log(`Added ${line} to .gitignore`);
30+
console.log(t('addedToGitignore', { line }));
3031
}
3132

3233
fs.writeFileSync(gitignorePath, gitignoreLines.join('\n'));

src/utils/check-plugin.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { plugins } from './plugin-config';
2+
import { t } from './i18n';
23

34
interface BundleParams {
45
sentry: boolean;
@@ -17,10 +18,10 @@ export async function checkPlugins(): Promise<BundleParams> {
1718
const isEnabled = await plugin.detect();
1819
if (isEnabled && plugin.bundleParams) {
1920
Object.assign(params, plugin.bundleParams);
20-
console.log(`detected ${plugin.name} plugin`);
21+
console.log(t('pluginDetected', { name: plugin.name }));
2122
}
2223
} catch (err) {
23-
console.warn(`error while detecting ${plugin.name} plugin:`, err);
24+
console.warn(t('pluginDetectionError', { name: plugin.name, error: err }));
2425
}
2526
}
2627

0 commit comments

Comments
 (0)