1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-13 19:51:37 +01:00

converted i18nservice properly

This commit is contained in:
Kyle Spearrin 2018-04-11 14:52:49 -04:00
parent 222b58f440
commit 3ebb09fa8d
6 changed files with 133 additions and 104 deletions

View File

@ -60,8 +60,7 @@ import AutofillService from '../services/autofill.service';
import BrowserMessagingService from '../services/browserMessaging.service'; import BrowserMessagingService from '../services/browserMessaging.service';
import BrowserPlatformUtilsService from '../services/browserPlatformUtils.service'; import BrowserPlatformUtilsService from '../services/browserPlatformUtils.service';
import BrowserStorageService from '../services/browserStorage.service'; import BrowserStorageService from '../services/browserStorage.service';
import i18nService from '../services/i18n.service'; import I18nService from '../services/i18n.service';
import I18n2Service from '../services/i18n2.service';
import { AutofillService as AutofillServiceAbstraction } from '../services/abstractions/autofill.service'; import { AutofillService as AutofillServiceAbstraction } from '../services/abstractions/autofill.service';
@ -69,8 +68,7 @@ export default class MainBackground {
messagingService: MessagingServiceAbstraction; messagingService: MessagingServiceAbstraction;
storageService: StorageServiceAbstraction; storageService: StorageServiceAbstraction;
secureStorageService: StorageServiceAbstraction; secureStorageService: StorageServiceAbstraction;
i18nService: any; i18nService: I18nServiceAbstraction;
i18n2Service: I18nServiceAbstraction;
platformUtilsService: PlatformUtilsServiceAbstraction; platformUtilsService: PlatformUtilsServiceAbstraction;
utilsService: UtilsServiceAbstraction; utilsService: UtilsServiceAbstraction;
constantsService: ConstantsService; constantsService: ConstantsService;
@ -117,12 +115,10 @@ export default class MainBackground {
this.utilsService = new UtilsService(); this.utilsService = new UtilsService();
this.messagingService = new BrowserMessagingService(); this.messagingService = new BrowserMessagingService();
this.platformUtilsService = new BrowserPlatformUtilsService(this.messagingService); this.platformUtilsService = new BrowserPlatformUtilsService(this.messagingService);
const delayi18nLoad = this.platformUtilsService.isEdge() || this.platformUtilsService.isSafari() ? 1000 : 0;
this.storageService = new BrowserStorageService(this.platformUtilsService, false); this.storageService = new BrowserStorageService(this.platformUtilsService, false);
this.secureStorageService = new BrowserStorageService(this.platformUtilsService, true); this.secureStorageService = new BrowserStorageService(this.platformUtilsService, true);
this.i18nService = i18nService(this.platformUtilsService); this.i18nService = new I18nService(BrowserApi.getUILanguage(window),
this.i18n2Service = new I18n2Service(window.navigator.language, this.i18nService); BrowserApi.isSafariApi ? './_locales/' : null);
this.constantsService = new ConstantsService(this.i18nService, delayi18nLoad);
this.cryptoService = new CryptoService(this.storageService, this.secureStorageService); this.cryptoService = new CryptoService(this.storageService, this.secureStorageService);
this.tokenService = new TokenService(this.storageService); this.tokenService = new TokenService(this.storageService);
this.appIdService = new AppIdService(this.storageService); this.appIdService = new AppIdService(this.storageService);
@ -132,11 +128,11 @@ export default class MainBackground {
this.userService = new UserService(this.tokenService, this.storageService); this.userService = new UserService(this.tokenService, this.storageService);
this.settingsService = new SettingsService(this.userService, this.storageService); this.settingsService = new SettingsService(this.userService, this.storageService);
this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService, this.cipherService = new CipherService(this.cryptoService, this.userService, this.settingsService,
this.apiService, this.storageService, this.i18n2Service, this.platformUtilsService, this.utilsService); this.apiService, this.storageService, this.i18nService, this.platformUtilsService, this.utilsService);
this.folderService = new FolderService(this.cryptoService, this.userService, this.folderService = new FolderService(this.cryptoService, this.userService,
() => this.i18nService.noneFolder, this.apiService, this.storageService, this.i18n2Service); () => this.i18nService.t('noneFolder'), this.apiService, this.storageService, this.i18nService);
this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService, this.collectionService = new CollectionService(this.cryptoService, this.userService, this.storageService,
this.i18n2Service); this.i18nService);
this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService, this.lockService = new LockService(this.cipherService, this.folderService, this.collectionService,
this.cryptoService, this.platformUtilsService, this.storageService, this.messagingService, async () => { this.cryptoService, this.platformUtilsService, this.storageService, this.messagingService, async () => {
await this.setIcon(); await this.setIcon();
@ -342,7 +338,7 @@ export default class MainBackground {
id: 'autofill', id: 'autofill',
parentId: 'root', parentId: 'root',
contexts: ['all'], contexts: ['all'],
title: this.i18nService.autoFill, title: this.i18nService.t('autoFill'),
}); });
// Firefox & Edge do not support writing to the clipboard from background // Firefox & Edge do not support writing to the clipboard from background
@ -352,7 +348,7 @@ export default class MainBackground {
id: 'copy-username', id: 'copy-username',
parentId: 'root', parentId: 'root',
contexts: ['all'], contexts: ['all'],
title: this.i18nService.copyUsername, title: this.i18nService.t('copyUsername'),
}); });
await this.contextMenusCreate({ await this.contextMenusCreate({
@ -360,7 +356,7 @@ export default class MainBackground {
id: 'copy-password', id: 'copy-password',
parentId: 'root', parentId: 'root',
contexts: ['all'], contexts: ['all'],
title: this.i18nService.copyPassword, title: this.i18nService.t('copyPassword'),
}); });
await this.contextMenusCreate({ await this.contextMenusCreate({
@ -373,7 +369,7 @@ export default class MainBackground {
id: 'generate-password', id: 'generate-password',
parentId: 'root', parentId: 'root',
contexts: ['all'], contexts: ['all'],
title: this.i18nService.generatePasswordCopied, title: this.i18nService.t('generatePasswordCopied'),
}); });
} }
@ -411,7 +407,7 @@ export default class MainBackground {
theText = '9+'; theText = '9+';
} else { } else {
if (contextMenuEnabled) { if (contextMenuEnabled) {
await this.loadNoLoginsContextMenuOptions(this.i18nService.noMatchingLogins); await this.loadNoLoginsContextMenuOptions(this.i18nService.t('noMatchingLogins'));
} }
} }
@ -424,7 +420,7 @@ export default class MainBackground {
private async loadMenuAndUpdateBadgeForLockedState(contextMenuEnabled: boolean) { private async loadMenuAndUpdateBadgeForLockedState(contextMenuEnabled: boolean) {
if (contextMenuEnabled) { if (contextMenuEnabled) {
await this.loadNoLoginsContextMenuOptions(this.i18nService.vaultLocked); await this.loadNoLoginsContextMenuOptions(this.i18nService.t('vaultLocked'));
} }
const tabs = await BrowserApi.getActiveTabs(); const tabs = await BrowserApi.getActiveTabs();

View File

@ -7,6 +7,8 @@ import { LoginView } from 'jslib/models/view/loginView';
import { ConstantsService } from 'jslib/services/constants.service'; import { ConstantsService } from 'jslib/services/constants.service';
import { UtilsService } from 'jslib/services/utils.service'; import { UtilsService } from 'jslib/services/utils.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { Analytics } from 'jslib/misc'; import { Analytics } from 'jslib/misc';
import { import {
@ -30,7 +32,7 @@ export default class RuntimeBackground {
constructor(private main: MainBackground, private autofillService: AutofillService, constructor(private main: MainBackground, private autofillService: AutofillService,
private cipherService: CipherService, private platformUtilsService: BrowserPlatformUtilsService, private cipherService: CipherService, private platformUtilsService: BrowserPlatformUtilsService,
private storageService: StorageService, private i18nService: any, private analytics: Analytics) { private storageService: StorageService, private i18nService: I18nService, private analytics: Analytics) {
this.isSafari = this.platformUtilsService.isSafari(); this.isSafari = this.platformUtilsService.isSafari();
this.runtime = this.isSafari ? safari.application : chrome.runtime; this.runtime = this.isSafari ? safari.application : chrome.runtime;
@ -357,13 +359,13 @@ export default class RuntimeBackground {
ConstantsService.enableAutoFillOnPageLoadKey); ConstantsService.enableAutoFillOnPageLoadKey);
} else if (responseCommand === 'notificationBarFrameDataResponse') { } else if (responseCommand === 'notificationBarFrameDataResponse') {
responseData.i18n = { responseData.i18n = {
appName: this.i18nService.appName, appName: this.i18nService.t('appName'),
close: this.i18nService.close, close: this.i18nService.t('close'),
yes: this.i18nService.yes, yes: this.i18nService.t('yes'),
never: this.i18nService.never, never: this.i18nService.t('never'),
notificationAddSave: this.i18nService.notificationAddSave, notificationAddSave: this.i18nService.t('notificationAddSave'),
notificationNeverSave: this.i18nService.notificationNeverSave, notificationNeverSave: this.i18nService.t('notificationNeverSave'),
notificationAddDesc: this.i18nService.notificationAddDesc, notificationAddDesc: this.i18nService.t('notificationAddDesc'),
}; };
} }

View File

@ -1,6 +1,6 @@
export class BrowserApi { export class BrowserApi {
static isSafariApi: boolean = (typeof safari !== 'undefined') && static isSafariApi: boolean = (typeof safari !== 'undefined') &&
navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1; navigator.userAgent.indexOf(' Safari/') !== -1 && navigator.userAgent.indexOf('Chrome') === -1;
static isChromeApi: boolean = !BrowserApi.isSafariApi && (typeof chrome !== 'undefined'); static isChromeApi: boolean = !BrowserApi.isSafariApi && (typeof chrome !== 'undefined');
static async getTabFromCurrentWindowId(): Promise<any> { static async getTabFromCurrentWindowId(): Promise<any> {
@ -264,4 +264,12 @@ export class BrowserApi {
static gaFilter() { static gaFilter() {
return BrowserApi.isSafariApi && safari.application.activeBrowserWindow.activeTab.private; return BrowserApi.isSafariApi && safari.application.activeBrowserWindow.activeTab.private;
} }
static getUILanguage(win: Window) {
if (BrowserApi.isSafariApi) {
return win.navigator.language;
} else {
return chrome.i18n.getUILanguage();
}
}
} }

View File

@ -57,7 +57,7 @@ export const messagingService = new BrowserMessagingService();
export const authService = new AuthService(getBgService<CryptoService>('cryptoService')(), export const authService = new AuthService(getBgService<CryptoService>('cryptoService')(),
getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(), getBgService<ApiService>('apiService')(), getBgService<UserService>('userService')(),
getBgService<TokenService>('tokenService')(), getBgService<AppIdService>('appIdService')(), getBgService<TokenService>('tokenService')(), getBgService<AppIdService>('appIdService')(),
getBgService<I18nService>('i18n2Service')(), getBgService<PlatformUtilsService>('platformUtilsService')(), getBgService<I18nService>('i18nService')(), getBgService<PlatformUtilsService>('platformUtilsService')(),
getBgService<ConstantsService>('constantsService')(), messagingService); getBgService<ConstantsService>('constantsService')(), messagingService);
export function initFactory(i18nService: I18nService, storageService: StorageService, export function initFactory(i18nService: I18nService, storageService: StorageService,
@ -102,7 +102,7 @@ export function initFactory(i18nService: I18nService, storageService: StorageSer
{ provide: EnvironmentService, useFactory: getBgService<EnvironmentService>('environmentService'), deps: [] }, { provide: EnvironmentService, useFactory: getBgService<EnvironmentService>('environmentService'), deps: [] },
{ provide: TotpService, useFactory: getBgService<TotpService>('totpService'), deps: [] }, { provide: TotpService, useFactory: getBgService<TotpService>('totpService'), deps: [] },
{ provide: TokenService, useFactory: getBgService<TokenService>('tokenService'), deps: [] }, { provide: TokenService, useFactory: getBgService<TokenService>('tokenService'), deps: [] },
{ provide: I18nService, useFactory: getBgService<I18nService>('i18n2Service'), deps: [] }, { provide: I18nService, useFactory: getBgService<I18nService>('i18nService'), deps: [] },
{ provide: UtilsService, useFactory: getBgService<UtilsService>('utilsService'), deps: [] }, { provide: UtilsService, useFactory: getBgService<UtilsService>('utilsService'), deps: [] },
{ provide: CryptoService, useFactory: getBgService<CryptoService>('cryptoService'), deps: [] }, { provide: CryptoService, useFactory: getBgService<CryptoService>('cryptoService'), deps: [] },
{ {

View File

@ -1,69 +1,121 @@
import { PlatformUtilsService } from 'jslib/abstractions'; import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
// First locale is the default (English) // First locale is the default (English)
const SupportedLocales = [ const SupportedTranslationLocales = [
'en', 'cs', 'da', 'de', 'es', 'et', 'fi', 'fr', 'hr', 'hu', 'id', 'it', 'ja', 'en', 'cs', 'da', 'de', 'es', 'et', 'fi', 'fr', 'hr', 'hu', 'id', 'it', 'ja',
'nb', 'nl', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sv', 'tr', 'uk', 'vi', 'nb', 'nl', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', 'sk', 'sv', 'tr', 'uk', 'vi',
'zh-CN', 'zh-TW', 'zh-CN', 'zh-TW',
]; ];
export default function i18nService(platformUtilsService: PlatformUtilsService) { export default class I18nService implements I18nServiceAbstraction {
const defaultMessages: any = {}; defaultMessages: any = {};
const localeMessages: any = {}; localeMessages: any = {};
locale: string;
translationLocale: string;
collator: Intl.Collator;
inited: boolean;
if (platformUtilsService.isEdge()) { constructor(private systemLanguage: string, private localesDirectory: string) { }
loadMessages('../_locales/', 'en', localeMessages,
(prop: string, message: string) => chrome.i18n.getMessage(prop));
return localeMessages;
}
if (platformUtilsService.isSafari()) { async init(locale?: string) {
let lang = navigator.language; if (this.inited) {
if (SupportedLocales.indexOf(lang) === -1) { throw new Error('i18n already initialized.');
lang = lang.slice(0, 2); }
if (SupportedLocales.indexOf(lang) === -1) { this.inited = true;
lang = SupportedLocales[0]; this.locale = this.translationLocale = locale != null ? locale : this.systemLanguage;
this.collator = new Intl.Collator(this.locale);
if (SupportedTranslationLocales.indexOf(this.translationLocale) === -1) {
this.translationLocale = this.translationLocale.slice(0, 2);
if (SupportedTranslationLocales.indexOf(this.translationLocale) === -1) {
this.translationLocale = SupportedTranslationLocales[0];
} }
} }
const dir = './_locales/'; if (this.localesDirectory != null) {
loadMessages(dir, lang, localeMessages, (prop: string, message: string) => message); await this.loadMessages(this.localesDirectory, this.translationLocale, this.localeMessages);
if (lang !== SupportedLocales[0]) { if (this.translationLocale !== SupportedTranslationLocales[0]) {
loadMessages(dir, SupportedLocales[0], defaultMessages, await this.loadMessages(this.localesDirectory, SupportedTranslationLocales[0], this.defaultMessages);
(prop: string, message: string) => message); }
} }
} }
return new Proxy({}, { t(id: string, p1?: string, p2?: string, p3?: string): string {
get: (target, name) => { return this.translate(id, p1, p2, p3);
const id = name.toString(); }
if (platformUtilsService.isSafari()) { translate(id: string, p1?: string, p2?: string, p3?: string): string {
if (localeMessages.hasOwnProperty(id) && localeMessages[id]) { if (this.localesDirectory == null) {
return localeMessages[id]; const placeholders: string[] = [];
} else if (defaultMessages.hasOwnProperty(id) && defaultMessages[id]) { if (p1 != null) {
return defaultMessages[id]; placeholders.push(p1);
}
if (p2 != null) {
placeholders.push(p2);
}
if (p3 != null) {
placeholders.push(p3);
}
if (placeholders.length) {
return chrome.i18n.getMessage(id, placeholders);
} else {
return chrome.i18n.getMessage(id);
}
}
let result: string;
if (this.localeMessages.hasOwnProperty(id) && this.localeMessages[id]) {
result = this.localeMessages[id];
} else if (this.defaultMessages.hasOwnProperty(id) && this.defaultMessages[id]) {
result = this.defaultMessages[id];
} else {
result = '';
}
if (result !== '') {
if (p1 != null) {
result = result.split('__$1__').join(p1);
}
if (p2 != null) {
result = result.split('__$2__').join(p2);
}
if (p3 != null) {
result = result.split('__$3__').join(p3);
}
}
return result;
}
private async loadMessages(localesDir: string, locale: string, messagesObj: any): Promise<any> {
const formattedLocale = locale.replace('-', '_');
const file = await fetch(localesDir + formattedLocale + '/messages.json');
const locales = await file.json();
for (const prop in locales) {
if (!locales.hasOwnProperty(prop)) {
continue;
}
messagesObj[prop] = locales[prop].message;
if (locales[prop].placeholders) {
for (const placeProp in locales[prop].placeholders) {
if (!locales[prop].placeholders.hasOwnProperty(placeProp) ||
!locales[prop].placeholders[placeProp].content) {
continue;
}
const replaceToken = '\\$' + placeProp.toUpperCase() + '\\$';
let replaceContent = locales[prop].placeholders[placeProp].content;
if (replaceContent === '$1' || replaceContent === '$2' || replaceContent === '$3') {
replaceContent = '__' + replaceContent + '__';
}
messagesObj[prop] = messagesObj[prop].replace(new RegExp(replaceToken, 'g'), replaceContent);
} }
return '';
} }
return chrome.i18n.getMessage(id);
},
set: (target, name, value) => {
return false;
},
});
}
async function loadMessages(localesDir: string, locale: string, messagesObj: any,
messageCallback: (prop: string, message: string) => string): Promise<any> {
const formattedLocale = locale.replace('-', '_');
const file = await fetch(localesDir + formattedLocale + '/messages.json');
const locales = await file.json();
for (const prop in locales) {
if (locales.hasOwnProperty(prop)) {
messagesObj[prop] = messageCallback(prop, locales[prop].message);
} }
} }
} }

View File

@ -1,29 +0,0 @@
import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service';
export default class I18n2Service implements I18nServiceAbstraction {
locale: string;
translationLocale: string;
collator: Intl.Collator;
inited: boolean;
constructor(private systemLanguage: string, private i18nService: any) {
}
async init(locale?: string) {
if (this.inited) {
throw new Error('i18n already initialized.');
}
this.inited = true;
this.locale = this.translationLocale = locale != null ? locale : this.systemLanguage;
this.collator = new Intl.Collator(this.locale);
}
t(id: string, p1?: string, p2?: string, p3?: string): string {
return this.translate(id);
}
translate(id: string, p1?: string, p2?: string, p3?: string): string {
return this.i18nService[id];
}
}