如何在Ionic中预加载依赖内JSON翻译文件以支持离线使用
解决Ionic应用离线时无法加载Transloco翻译JSON的问题
问题背景
- 基于Transloco封装了共享翻译库,内置所有JSON翻译文件,为多个Angular应用提供服务
- Angular Web应用运行完全正常;Ionic应用在线时正常,但离线状态下无法加载库内的JSON翻译资源
- 拒绝采用将JSON转为JS函数的方案,需实现在线时预加载翻译文件并缓存,离线时直接使用缓存内容的逻辑
解决方案思路
利用Ionic本地存储(@ionic/storage-angular)缓存翻译数据,结合网络状态判断实现逻辑:
- 在线状态:优先从翻译库加载最新JSON,加载成功后存入本地缓存
- 离线状态:直接从本地缓存读取翻译内容
- 完整保留原有API自定义翻译的逻辑,不影响现有功能
修改后的TranslocoHttpLoader实现
先安装依赖:
npm install @ionic/storage-angular
修改翻译加载器代码:
import { Injectable, Inject } from '@angular/core'; import { TranslocoLoader, Translation, TRANSLOCO_LOADER_CONFIG, TranslocoLoaderConfig } from '@ngneat/transloco'; import { from, Observable, of, combineLatest } from 'rxjs'; import { map, switchMap, catchError } from 'rxjs/operators'; import { Storage } from '@ionic/storage-angular'; @Injectable({ providedIn: 'root' }) export class TranslocoHttpLoaderService implements TranslocoLoader { // 缓存键前缀,避免与其他存储内容冲突 private readonly CACHE_KEY_PREFIX = 'transloco_'; constructor( @Inject(TRANSLOCO_LOADER_CONFIG) private translocoLoaderConfig: TranslocoLoaderConfig, private storage: Storage ) { // 初始化Ionic Storage this.initStorage(); } private async initStorage() { await this.storage.create(); } getTranslation(lang: string): Observable<Translation> { const cacheKey = `${this.CACHE_KEY_PREFIX}${lang}`; const isOnline = navigator.onLine; // 先尝试获取缓存的翻译内容 const cachedTranslation$ = from(this.storage.get(cacheKey)).pipe( map(cached => cached || null) ); // 在线时加载最新翻译并更新缓存;离线时直接返回空(后续用缓存) const freshTranslation$ = isOnline ? this.loadFreshTranslation(lang, cacheKey) : of(null); // 合并缓存与最新翻译流 return combineLatest([cachedTranslation$, freshTranslation$]).pipe( switchMap(([cached, fresh]) => { // 优先用最新翻译,没有则用缓存;极端情况(离线无缓存)返回默认语言 const baseTranslation = fresh || cached || (await this.getLanguage('en').then(x => x.default)); if (this.translocoLoaderConfig.disableCustomLiterals) { return of(this.transformTranslations(baseTranslation)); } // 结合API自定义翻译逻辑 return this.getCustomLiterals().pipe( map(customLiterals => ({ ...baseTranslation, ...customLiterals })), map(translations => this.transformTranslations(translations)) ); }) ); } private loadFreshTranslation(lang: string, cacheKey: string): Observable<Translation> { return from(this.getLanguage(lang).then(x => x.default)).pipe( switchMap(translation => { // 将最新翻译存入缓存 return from(this.storage.set(cacheKey, translation)).pipe( map(() => translation) ); }), catchError(() => { // 加载失败时回退到缓存内容 return from(this.storage.get(cacheKey)); }) ); } private getCustomLiterals(): Observable<Translation> { return API_LOGIC_CALL.pipe( map((el: any) => { if (!el.value) return {}; let customLiterals = JSON.parse(el.value); // 扁平化翻译对象 customLiterals = customLiterals.reduce( (acc: Translation, curr: any) => ({ ...acc, ...curr }), {} as Translation ); return customLiterals; }), catchError(() => of({})) ); } private transformTranslations(translations: Translation): Translation { // 转换@:语法为{{}}格式 return transformValuesRecursively(translations, (value) => { if (value.startsWith('@:')) { return '{{' + value.slice(2) + '}}'; } return value; }); } // 原有加载语言文件的方法,保持不变 getLanguage(lang: string) { switch (lang) { case 'es': return import('./i18n/es'); case 'fr': return import('./i18n/fr/fr.json'); default: // 'en' return import('./i18n/en/en.json'); } } } // 原有递归转换工具函数,保持不变 const transformValuesRecursively = ( object: any, tfunction: (value: string) => string ) => { const newObject = {} as any; Object.entries(object).forEach(([key, value]) => { if (typeof value === 'string') { newObject[key] = tfunction(value); } else { newObject[key] = transformValuesRecursively(value, tfunction); } }); return newObject; };
关键修改说明
- 引入Ionic Storage:持久化缓存翻译数据,确保离线时可读取
- 网络状态判断:通过
navigator.onLine切换加载源,在线取最新、离线取缓存 - 缓存逻辑优化:在线加载成功后更新缓存,加载失败时回退到缓存
- 兼容原有逻辑:完整保留API自定义翻译和语法转换流程,无功能断档
- 异常处理:添加错误捕获,避免因网络或加载失败导致翻译失效
额外优化建议
- 如需更精准的网络状态判断,可替换
navigator.onLine为@ionic-native/network插件 - 可添加缓存过期逻辑,定期更新缓存的翻译内容
- 在Ionic应用的
app.module.ts中导入Storage模块:
import { IonicStorageModule } from '@ionic/storage-angular'; @NgModule({ imports: [ // ...其他模块 IonicStorageModule.forRoot() ] }) export class AppModule {}
内容的提问来源于stack exchange,提问作者Pato
相关产品推荐
相关产品推荐

