Browse Source

fix(i18n): fix two char locales not being auto-detected properly

master
jamaljsr 5 years ago
parent
commit
ec45fa6b1b
  1. 3
      src/components/layouts/AppLayout.spec.tsx
  2. 6
      src/components/layouts/LocaleSwitch.tsx
  3. 55
      src/i18n/index.ts
  4. 25
      src/lib/settings/settingsService.ts
  5. 3
      src/store/models/app.ts
  6. 7
      src/types/index.ts

3
src/components/layouts/AppLayout.spec.tsx

@ -1,7 +1,6 @@
import React from 'react';
import { getI18n, useTranslation } from 'react-i18next';
import { fireEvent } from '@testing-library/react';
import { localeConfig } from 'i18n';
import { renderWithProviders } from 'utils/tests';
import AppLayout from './AppLayout';
@ -21,7 +20,7 @@ describe('AppLayout component', () => {
};
beforeEach(async () => {
await getI18n().changeLanguage(localeConfig.fallbackLng);
await getI18n().changeLanguage('en-US');
});
it('should contain the text of child components', () => {

6
src/components/layouts/LocaleSwitch.tsx

@ -3,7 +3,7 @@ import styled from '@emotion/styled';
import { Button, Dropdown, Icon, Menu } from 'antd';
import { ClickParam } from 'antd/lib/menu';
import { useStoreState } from 'easy-peasy';
import { localeConfig } from 'i18n';
import { languages } from 'i18n';
import { useStoreActions } from 'store';
const Styled = {
@ -21,7 +21,7 @@ const LocaleSwitch: React.FC = () => {
const menu = (
<Menu onClick={changeLanguage} selectedKeys={[settings.lang]}>
{Object.entries(localeConfig.languages).map(([key, lang]) => (
{Object.entries(languages).map(([key, lang]) => (
<Menu.Item key={key}>
{lang} ({key})
</Menu.Item>
@ -34,7 +34,7 @@ const LocaleSwitch: React.FC = () => {
<Dropdown overlay={menu} placement="topRight">
<Styled.Button type="link">
<Icon type="global" />
{localeConfig.languages[settings.lang]}
{languages[settings.lang]}
</Styled.Button>
</Dropdown>
</>

55
src/i18n/index.ts

@ -1,50 +1,43 @@
import { initReactI18next } from 'react-i18next';
import { app, remote } from 'electron';
import i18n from 'i18next';
import { LocaleConfig } from 'types';
export const localeConfig: LocaleConfig = {
fallbackLng: 'en-US',
languages: {
'en-US': 'English',
'es-ES': 'Español',
'fr-FR': 'French',
'de-DE': 'German',
'ru-RU': 'Russian',
'it-IT': 'Italian',
'zh-CN': 'Chinese Simplified',
'pt-BR': 'Portuguese, Brazilian',
'ja-JP': 'Japanese',
'ko-KR': 'Korean',
},
const defaultLanguage = 'en-US';
export const languages: { [index: string]: string } = {
'en-US': 'English',
'es-ES': 'Español',
'fr-FR': 'French',
'de-DE': 'German',
'ru-RU': 'Russian',
'it-IT': 'Italian',
'zh-CN': 'Chinese Simplified',
'pt-BR': 'Portuguese, Brazilian',
'ja-JP': 'Japanese',
'ko-KR': 'Korean',
};
const resources = Object.keys(localeConfig.languages).reduce(
(acc: { [key: string]: any }, lang) => {
acc[lang] = {
translation: require(`./locales/${lang}.json`),
};
return acc;
},
Object,
);
const resources = Object.keys(languages).reduce((acc: { [key: string]: any }, lang) => {
acc[lang] = {
translation: require(`./locales/${lang}.json`),
};
return acc;
}, Object);
const detectLang = () => {
const lang = (app || remote.app).getLocale();
// look for an exact match
const exact = localeConfig.languages[lang] && lang;
const exact = languages[lang] && lang;
if (exact) return exact;
// look for a match of the first two chars
const prefix = lang.slice(0, 2);
const partial = Object.keys(localeConfig.languages)
.map(l => l.slice(0, 2))
.find(l => l === prefix);
const partial = Object.keys(languages).find(l => l.slice(0, 2) === prefix);
if (partial) return partial;
// return the fallback language for no matches
return localeConfig.fallbackLng;
return defaultLanguage;
};
const whitelist = Object.keys(localeConfig.languages).reduce((acc: string[], lang) => {
const whitelist = Object.keys(languages).reduce((acc: string[], lang) => {
acc.push(lang);
if (lang.includes('-')) {
@ -58,7 +51,7 @@ i18n.use(initReactI18next).init({
lng: detectLang(),
resources,
whitelist,
fallbackLng: localeConfig.fallbackLng,
fallbackLng: defaultLanguage,
keySeparator: false,
interpolation: {
escapeValue: false,

25
src/lib/settings/settingsService.ts

@ -1,33 +1,38 @@
import { info } from 'electron-log';
import { debug } from 'electron-log';
import { join } from 'path';
import { AppSettings, SettingsInjection } from 'types';
import { dataPath } from 'utils/config';
import { exists, read, write } from 'utils/files';
class SettingsService implements SettingsInjection {
/**
* The path to the settings file
*/
filePath = join(dataPath, 'settings.json');
/**
* Saves the given settings to the file system
* @param settings the list of settings to save
*/
async save(data: AppSettings) {
const json = JSON.stringify(data, null, 2);
const path = join(dataPath, 'settings.json');
await write(path, json);
info(`saved settings to '${path}'`, json);
await write(this.filePath, json);
debug(`saved settings to '${this.filePath}'`, json);
}
/**
* Loads a list of settings from the file system
* Loads settings from the file system
*/
async load(): Promise<AppSettings | undefined> {
const path = join(dataPath, 'settings.json');
if (await exists(path)) {
const json = await read(path);
if (await exists(this.filePath)) {
const json = await read(this.filePath);
const data = JSON.parse(json);
info(`loaded app settings from '${path}'`, data);
debug(`loaded app settings from '${this.filePath}'`, data);
return data;
} else {
info(`skipped loading app settings because the file '${path}' doesn't exist`);
debug(
`skipped loading app settings because the file '${this.filePath}' doesn't exist`,
);
}
}
}

3
src/store/models/app.ts

@ -5,7 +5,6 @@ import { ArgsProps } from 'antd/lib/notification';
import { push } from 'connected-react-router';
import { Action, action, Thunk, thunk } from 'easy-peasy';
import { ipcChannels } from 'shared';
import { localeConfig } from 'i18n';
import { AppSettings, DockerVersions, StoreInjections } from 'types';
import { RootModel } from './';
@ -40,7 +39,7 @@ const appModel: AppModel = {
// state properties
initialized: false,
settings: {
lang: localeConfig.fallbackLng,
lang: getI18n().language,
showAllNodeVersions: false,
},
dockerVersions: { docker: '', compose: '' },

7
src/types/index.ts

@ -4,13 +4,6 @@ import { BitcoinNode, CommonNode, LightningNode, Status } from 'shared/types';
import { IpcSender } from 'lib/ipc/ipcService';
import * as PLN from 'lib/lightning/types';
export interface LocaleConfig {
fallbackLng: string;
languages: {
[key: string]: string;
};
}
export interface Network {
id: number;
name: string;

Loading…
Cancel
Save