Skip to content
This repository was archived by the owner on Feb 6, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion studio/scripts/i18n.types.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const generate = async () => {
};
});

const lang = `lang: 'en';`;
const lang = `lang: Languages;`;

const main = `\n\ninterface I18n {${lang}${data.map((i) => `${i.key}: ${i.name};`).join('')}}`;
const interfaces = data.map((i) => `\n\ninterface ${i.name} {${i.properties.join('')}}`).join('');
Expand Down
4 changes: 4 additions & 0 deletions studio/src/app/app-root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {OfflineService} from './services/editor/offline/offline.service';
import {NavDirection, NavParams} from './stores/nav.store';
import {ColorService} from './services/color/color.service';
import {SettingsService} from './services/settings/settings.service';
import {LangService} from './services/lang/lang.service';

@Component({
tag: 'app-root',
Expand All @@ -27,6 +28,7 @@ export class AppRoot {
private readonly themeService: ThemeService;
private readonly colorService: ColorService;
private readonly settingsService: SettingsService;
private readonly langService: LangService;

@State()
private loading: boolean = true;
Expand All @@ -43,6 +45,7 @@ export class AppRoot {
this.themeService = ThemeService.getInstance();
this.colorService = ColorService.getInstance();
this.settingsService = SettingsService.getInstance();
this.langService = LangService.getInstance();
}

async componentWillLoad() {
Expand All @@ -53,6 +56,7 @@ export class AppRoot {
this.themeService.initDarkModePreference(),
this.colorService.init(),
this.settingsService.init(),
this.langService.init(),
];

await Promise.all(promises);
Expand Down
2 changes: 1 addition & 1 deletion studio/src/app/definitions/i18.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ interface I18nPoll {
}

interface I18n {
lang: 'en';
lang: Languages;
core: I18nCore;
nav: I18nNav;
menu: I18nMenu;
Expand Down
1 change: 1 addition & 0 deletions studio/src/app/definitions/languages.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
type Languages = 'en' | 'es';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Component, Fragment, h} from '@stencil/core';
import {Component, h} from '@stencil/core';

import themeStore from '../../../../stores/theme.store';
import settingsStore from '../../../../stores/settings.store';
Expand Down Expand Up @@ -29,6 +29,10 @@ export class AppCustomization {
settingsStore.state.editMode = settingsStore.state.editMode === 'css' ? 'properties' : 'css';
}

private toggleLang($event: CustomEvent) {
i18n.state.lang = $event.detail.value;
}

render() {
return [
<app-navigation></app-navigation>,
Expand All @@ -39,6 +43,8 @@ export class AppCustomization {
<ion-list class="inputs-list dark-light-list">
{this.renderDarkLightToggle()}

{this.renderLang()}

{this.renderEditMode()}
</ion-list>
</main>
Expand All @@ -57,25 +63,40 @@ export class AppCustomization {
);
}

private renderLang() {
return (
<ion-item class="select">
<ion-label>{i18n.state.editor.language}</ion-label>
<ion-select
slot="end"
value={i18n.state.lang}
onIonChange={($event: CustomEvent) => this.toggleLang($event)}
interface="popover"
mode="md"
class="ion-padding-start ion-padding-end">
<ion-select-option value="en">English</ion-select-option>
<ion-select-option value="es">Español</ion-select-option>
</ion-select>
</ion-item>
);
}

private renderEditMode() {
return (
<Fragment>
<ion-item-divider class="ion-padding-top">
<ion-label>{i18n.state.settings.edit_mode}</ion-label>
</ion-item-divider>

<ion-radio-group value={this.editMode} onIonChange={() => this.toggleEditMode()}>
<ion-item>
<ion-radio value="properties" mode="md" slot="start"></ion-radio>
<ion-label>{i18n.state.settings.properties}</ion-label>
</ion-item>

<ion-item>
<ion-radio value="css" mode="md" slot="start"></ion-radio>
<ion-label>CSS</ion-label>
</ion-item>
</ion-radio-group>
</Fragment>
<ion-item>
<ion-label>{i18n.state.settings.edit_mode}</ion-label>

<ion-select
slot="end"
value={this.editMode}
onIonChange={() => this.toggleEditMode()}
interface="popover"
mode="md"
class="ion-padding-start ion-padding-end">
<ion-select-option value="properties">{i18n.state.settings.properties}</ion-select-option>
<ion-select-option value="css">CSS</ion-select-option>
</ion-select>
</ion-item>
);
}
}
67 changes: 67 additions & 0 deletions studio/src/app/services/lang/lang.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import i18n from '../../stores/i18n.store';

import {get} from 'idb-keyval';

export class LangService {
private static instance: LangService;

private constructor() {
// Private constructor, singleton
}

static getInstance() {
if (!LangService.instance) {
LangService.instance = new LangService();
}
return LangService.instance;
}

async init() {
try {
const lang: Languages | null = await get<Languages>('deckdeckgo_lang');

if (lang) {
i18n.state.lang = lang;
return;
}

this.initDefaultLang();
} catch (err) {
console.warn(`Couldn't find lang. Proceeding with default`);
}
}

private initDefaultLang() {
const browserLang: string | undefined = this.getBrowserLang();
i18n.state.lang = /(es|en)/gi.test(browserLang) ? (browserLang as Languages) : 'en';
}

/**
* From ngx-translate
* https://github.com/ngx-translate/core/blob/efcb4f43a645d9ac630aae8e50b60cc883e675fd/projects/ngx-translate/core/src/lib/translate.service.ts
* @private
*/
private getBrowserLang(): string | undefined {
if (typeof window === 'undefined' || typeof window.navigator === 'undefined') {
return undefined;
}

let browserLang: string | null = window.navigator.languages ? window.navigator.languages[0] : null;
// @ts-ignore
browserLang = browserLang || window.navigator.language || window.navigator.browserLanguage || window.navigator.userLanguage;

if (typeof browserLang === 'undefined') {
return undefined;
}

if (browserLang.indexOf('-') !== -1) {
browserLang = browserLang.split('-')[0];
}

if (browserLang.indexOf('_') !== -1) {
browserLang = browserLang.split('_')[0];
}

return browserLang;
}
}
39 changes: 37 additions & 2 deletions studio/src/app/stores/i18n.store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,45 @@
import {createStore} from '@stencil/store';

import en from '../../assets/i18n/en.json';
import {set} from 'idb-keyval';

const {state} = createStore<I18n>({
lang: en,
const {state, onChange} = createStore<I18n>({
lang: 'en',
...(en as Partial<I18n>),
} as I18n);

const esI18n = async (): Promise<I18n> => {
return {
lang: 'es',
...(await import(`../../assets/i18n/es.json`)),
};
};

const enI18n = (): I18n => {
return {
lang: 'en',
...(en as Partial<I18n>),
} as I18n;
};

onChange('lang', async (lang: Languages) => {
let bundle: I18n;

switch (lang) {
case 'es':
bundle = await esI18n();
break;
default:
bundle = enI18n();
}

Object.assign(state, bundle);
});

onChange('lang', (lang: Languages) => {
set('deckdeckgo_lang', lang).catch((err) => {
console.error('Failed to update IDB with new language', err);
});
});

export default {state};