Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@picgo/i18n

Package Overview
Dependencies
Maintainers
3
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@picgo/i18n - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

.github/workflows/ci.yml

14

dist/i18n.d.ts

@@ -1,16 +0,10 @@

import { II18nConstructorOptions } from './types/index';
import { II18nConstructorOptions } from './types';
export declare class I18n {
private language;
private locales;
private localeFileName;
private readonly localesBaseDir;
private readonly logger;
private adapter;
private currentLanguage;
constructor(options: II18nConstructorOptions);
getLanguage(): string;
setLanguage(language: string): void;
translate(phrase: string, args: any): any;
translate(phrase: string, args: any): string | undefined;
private postProcess;
private loadLocale;
private guessLocaleFileName;
private watch;
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const fs = tslib_1.__importStar(require("fs"));
const path = tslib_1.__importStar(require("path"));
const index_1 = require("./types/index");
const logger_1 = require("./logger");
const utils_1 = require("./utils");
const DOTNOTATION = '.';
class I18n {
constructor(options) {
this.localeFileName = {}; // localeName -> the name of localeFile
this.logger = new logger_1.Logger();
this.locales = {};
const { language, localesBaseDir, localeFileName } = options;
this.localesBaseDir = localesBaseDir;
if (localeFileName) {
this.localeFileName = localeFileName;
}
else {
this.guessLocaleFileName(localesBaseDir);
}
this.language = language;
this.loadLocale(language); // load locale
if (options.autoReoload) {
this.watch(this.localesBaseDir);
}
const { adapter, defaultLanguage } = options;
this.adapter = adapter;
this.currentLanguage = defaultLanguage.trim().toLowerCase();
}
getLanguage() {
return this.language;
return this.currentLanguage;
}
setLanguage(language) {
this.language = language;
if (!this.locales[language]) {
this.loadLocale(language);
}
this.currentLanguage = language.trim().toLowerCase();
}
translate(phrase, args) {
const currentLocale = this.locales[this.language];
const currentLocale = this.adapter.getLocale(this.currentLanguage);
if (!currentLocale) {
this.logger.error('current locale is null');
utils_1.logger.error('current locale is null');
return;

@@ -45,3 +25,3 @@ }

if (!object || !object.hasOwnProperty(key)) {
this.logger.warn(`current locale does\'t contain ${phrase}`);
utils_1.logger.warn(`current locale does\'t contain ${phrase}`);
return;

@@ -57,55 +37,8 @@ }

}
const keys = Object.keys(args);
const values = keys.map((key) => args[key]);
return new Function(keys.join(','), `return \`${template}\``)(values);
// see benchmark
return Object.keys(args).reduce((res, key) => {
return res.replace('${' + key + '}', args[key]);
}, template);
}
loadLocale(language) {
if (!this.localeFileName[language]) {
this.logger.error(`can\'t locate the locale file of language ${language}`);
return;
}
const filePath = path.join(this.localesBaseDir, this.localeFileName[language]);
const fileContent = fs.readFileSync(filePath, {
encoding: 'utf-8',
});
try {
const locale = JSON.parse(fileContent);
this.locales[language] = locale; // load locale content
}
catch (err) {
this.logger.error(`unable to parse locales from file (maybe ${filePath} is empty or invalid json?)`);
this.logger.error(`raw error info: ${err}`);
}
}
// TODO change the name of this method
guessLocaleFileName(dir) {
const files = fs.readdirSync(dir);
const localeFileName = {};
files.forEach((fileName) => {
const localeName = fileName.replace(path.extname(fileName), '');
localeFileName[localeName] = fileName;
});
this.logger.log(`guess locale file path from ${dir}`);
this.logger.log(`localeFileName: ${JSON.stringify(localeFileName)}`);
this.localeFileName = localeFileName;
}
// useless ?
watch(dir) {
fs.watch(dir, (eventType, fileName) => {
let language = '';
const { localeFileName } = this;
for (const lan in localeFileName) {
if (localeFileName.hasOwnProperty(lan)) {
if (localeFileName[lan] === fileName) {
language = lan;
}
}
}
if (language && eventType === index_1.EFileChangeType.change) {
this.loadLocale(language); // update locale
this.logger.log(`${fileName} has updated`);
}
});
}
}
exports.I18n = I18n;

@@ -1,1 +0,3 @@

export { I18n } from "./i18n";
export * from './adapters';
export * from './utils';
export * from './i18n';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var i18n_1 = require("./i18n");
exports.I18n = i18n_1.I18n;
const tslib_1 = require("tslib");
tslib_1.__exportStar(require("./adapters"), exports);
tslib_1.__exportStar(require("./utils"), exports);
tslib_1.__exportStar(require("./i18n"), exports);

@@ -0,7 +1,16 @@

import { BaseAdapter } from '../adapters';
export interface ILocaleFileName {
[locale: string]: string;
}
export interface ILocale {
[key: string]: any;
}
export interface ILocaleMap {
[language: string]: ILocale;
}
export interface II18nConstructorOptions {
autoReoload?: boolean;
language: string;
adapter: BaseAdapter;
defaultLanguage: string;
}
export interface IFileSyncAdapterConstructorOptions {
localesBaseDir: string;

@@ -13,1 +22,4 @@ localeFileName?: ILocaleFileName;

}
export declare enum ERUN_ENV {
dev = "development"
}

@@ -7,1 +7,5 @@ "use strict";

})(EFileChangeType = exports.EFileChangeType || (exports.EFileChangeType = {}));
var ERUN_ENV;
(function (ERUN_ENV) {
ERUN_ENV["dev"] = "development";
})(ERUN_ENV = exports.ERUN_ENV || (exports.ERUN_ENV = {}));
{
"name": "@picgo/i18n",
"version": "0.0.1",
"version": "0.0.2",
"description": "i18n tool",

@@ -9,8 +9,8 @@ "main": "dist/index.js",

"dev": "tsc -w -p .",
"build": "rollup -c",
"lint": "eslint src/**/*.ts",
"codestyle:check": "prettier --check ./src/**/*.ts **/**.json",
"codestyle:fix": "prettier --write ./src/**/*.ts **/**.json",
"build": "tsc -p . && rollup -c",
"lint": "eslint ./src{/,/**/}*.ts",
"codestyle:check": "prettier --check ./src{/,/**/}*.ts **/**.json",
"codestyle:fix": "prettier --write ./src{/,/**/}*.ts **/**.json",
"test": "mocha ./test/index.js",
"cz": "npm run codestyle:check && git-cz",
"cz": "npm run codestyle:check && npm run lint && git-cz",
"release": "bump-version"

@@ -17,0 +17,0 @@ },

## i18n
## Usage
![CI Test](https://github.com/PicGo/i18n/workflows/CI%20Test/badge.svg)
i18n 工具
### 用法
i18n 默认提供 FileSyncAdapter、ObjectAdapter 两个适配器,适用的场景分别为: locales 信息保存在文件中和 locales 信息保存在对象中。
```js
import { I18n } from '@picgo/i18n';
import { I18n, FileSyncAdapter, ObjectAdapter } from '@picgo/i18n';
// use FileSyncAdapter
const fileSyncAdapter = new FileSyncAdapter({
localesBaseDir: path.resolve(__dirname, './locales'), // locales文件目录
});
const i18n = new I18n({
autoReoload: true,
language: DEFAULT_LANGUAGE,
localesBaseDir: YOUR_LOCALES_DIR,
adapter: fileSyncAdapter,
defaultLanguage: 'zh',
});
// use ObjectAdapter
const objectAdapter = new ObjectAdapter({
zh: {
user: {
name: 'PicGo',
country: '中国',
},
report: {
singular: ' ${cnt}个报告',
plural: '${cnt}个报告',
},
},
en: {
user: {
name: 'PicGo',
country: 'China',
},
report: {
singular: 'only ${cnt} report',
plural: '${cnt} reports',
},
},
});
const i18n = new I18n({
adapter: objectAdapter,
defaultLanguage: 'zh',
});
```
### 自定义 Adapter
```js
import { BaseAdapter } from 'i18n';
class CustomAdapter extends BaseAdapter {
getLocale(language) {}
}
```
### API
#### I18n
- 构造函数 I18n

@@ -19,6 +71,4 @@

{
"autoReload": boolean, // true -> 自动监听 locales文件变化
"lauguage": string, // 默认语言类型
"localesBaseDir": string, // locales 文件所在路径,绝对路径
"localeFileName": { "language": 对应的locales文件名 } // localeFileName存储语言类型到locales文件的映射,该项可选,当不传入时,将自动扫描localesBaseDir目录下文件,并将各个locale文件名作为该文件对应的语言
"adater": BaseAdapter, // 适配器
"defaultLanguage": string // 默认语言
}

@@ -28,12 +78,13 @@ ```

- setLanguage
* i18n.setLanguage
- 参数: language
- 参数: language, 语言类型
- 无返回值
- getLauguage
* i18n.getLauguage
- 无参数
- 返回当前语言类型
- translate
* i18n.translate

@@ -52,10 +103,77 @@ - 参数 phrase, args

```
```js
i18n.translate('report.singular', {cnt: 1}); // only 1 report
i18n.translate('report.singular', { cnt: 1 }); // only 1 report
```
## License
#### FileSyncAdapter
- 构造函数 FileSyncAdapter
- 参数: options
```json
{
"localesBaseDir": string, // locales 文件所在路径,绝对路径
"localeFileName": { "language": 对应的locales文件名 } // localeFileName存储语言类型到locales文件的映射,该项可选,当不传入时,将自动扫描localesBaseDir目录下文件,并将各个locale文件名作为该文件对应的语言
}
```
- 返回 FileSyncAdapter 实例
- fileSyncAdapter.getLocale
- 参数 languag, 语言类型
- 返回 language 对应的 locale 数据
#### ObjectAdapter
- 构造函数 ObjectAdapter
- 参数 locales, 保存 locales 信息的对象
```json
{
"zh": {
"user": {
"name": "PicGo",
"country": "China"
},
"report": {
"singular": " ${cnt}个报告",
"plural": "${cnt}个报告"
}
},
"en": {
"user": {
"name": "PicGo",
"country": "China"
},
"report": {
"singular": "only ${cnt} report",
"plural": "${cnt} reports"
}
}
}
```
- 返回 ObjectAdapter 实例
- objectAdapter.getLocale
- 参数 languag, 语言类型
- 返回 language 对应的 locale 数据
* objectAdapter.setLocales 用于动态修改 objectAdapter 上的 locales 数据
- 参数 locales, locales 数据
- 无返回值
```js
objectAdapter.setLocales({
zh: {
newData: 'this is new Data',
},
});
```
### License
[MIT](http://opensource.org/licenses/MIT)
Copyright (c) 2020 PicGo Group

@@ -1,3 +0,3 @@

import { terser } from 'rollup-plugin-terser'
import typescript from 'rollup-plugin-typescript2'
import { terser } from 'rollup-plugin-terser';
import typescript from 'rollup-plugin-typescript2';
export default {

@@ -17,5 +17,5 @@ input: './src/index.ts',

format: 'cjs',
file: 'dist/index.js',
file: 'dist/i18n_cjs.js',
sourcemap: false
}]
}
};

@@ -1,5 +0,4 @@

import * as fs from 'fs';
import * as path from 'path';
import { EFileChangeType, II18nConstructorOptions, ILocaleFileName } from './types/index';
import { Logger } from './logger';
import { BaseAdapter } from './adapters';
import { II18nConstructorOptions } from './types';
import { logger } from './utils';

@@ -9,111 +8,45 @@ const DOTNOTATION = '.';

export class I18n {
private language: string;
private locales: { [locale: string]: any }; // cache locale content
private localeFileName: ILocaleFileName = {}; // localeName -> the name of localeFile
private readonly localesBaseDir: string;
private readonly logger: Logger;
constructor(options: II18nConstructorOptions) {
this.logger = new Logger();
this.locales = {};
const { language, localesBaseDir, localeFileName } = options;
this.localesBaseDir = localesBaseDir;
if (localeFileName) {
this.localeFileName = localeFileName;
} else {
this.guessLocaleFileName(localesBaseDir);
}
this.language = language;
this.loadLocale(language); // load locale
if (options.autoReoload) {
this.watch(this.localesBaseDir);
}
}
private adapter: BaseAdapter;
private currentLanguage: string;
constructor(options: II18nConstructorOptions) {
const { adapter, defaultLanguage } = options;
this.adapter = adapter;
this.currentLanguage = defaultLanguage.trim().toLowerCase();
}
getLanguage(): string {
return this.language;
}
getLanguage(): string {
return this.currentLanguage;
}
setLanguage(language: string) {
this.language = language;
if (!this.locales[language]) {
this.loadLocale(language);
}
}
setLanguage(language: string) {
this.currentLanguage = language.trim().toLowerCase();
}
translate(phrase: string, args: any) {
const currentLocale = this.locales[this.language];
if (!currentLocale) {
this.logger.error('current locale is null');
return;
}
translate(phrase: string, args: any) {
const currentLocale = this.adapter.getLocale(this.currentLanguage);
if (!currentLocale) {
logger.error('current locale is null');
return;
}
const template = phrase.split(DOTNOTATION).reduce((object: any, key: string) => {
if (!object || !object.hasOwnProperty(key)) {
this.logger.warn(`current locale does\'t contain ${phrase}`);
return;
}
return object[key];
}, currentLocale);
const template = phrase.split(DOTNOTATION).reduce((object: any, key: string) => {
if (!object || !object.hasOwnProperty(key)) {
logger.warn(`current locale does\'t contain ${phrase}`);
return;
}
return object[key];
}, currentLocale);
return this.postProcess(template, args);
}
return this.postProcess(template, args);
}
private postProcess(template: string, args: any) {
if (!template) {
return;
}
const keys = Object.keys(args);
const values = keys.map((key) => args[key]);
return new Function(keys.join(','), `return \`${template}\``)(values);
}
private loadLocale(language: string) {
if (!this.localeFileName[language]) {
this.logger.error(`can\'t locate the locale file of language ${language}`);
return;
}
const filePath = path.join(this.localesBaseDir, this.localeFileName[language]);
const fileContent = fs.readFileSync(filePath, {
encoding: 'utf-8',
});
try {
const locale = JSON.parse(fileContent);
this.locales[language] = locale; // load locale content
} catch (err) {
this.logger.error(`unable to parse locales from file (maybe ${filePath} is empty or invalid json?)`);
this.logger.error(`raw error info: ${err}`);
}
}
// TODO change the name of this method
private guessLocaleFileName(dir: string) {
const files = fs.readdirSync(dir);
const localeFileName: ILocaleFileName = {};
files.forEach((fileName: string) => {
const localeName = fileName.replace(path.extname(fileName), '');
localeFileName[localeName] = fileName;
});
this.logger.log(`guess locale file path from ${dir}`);
this.logger.log(`localeFileName: ${JSON.stringify(localeFileName)}`);
this.localeFileName = localeFileName;
}
// useless ?
private watch(dir: string) {
fs.watch(dir, (eventType: string, fileName: string) => {
let language = '';
const { localeFileName } = this;
for (const lan in localeFileName) {
if (localeFileName.hasOwnProperty(lan)) {
if (localeFileName[lan] === fileName) {
language = lan;
}
}
}
if (language && eventType === EFileChangeType.change) {
this.loadLocale(language); // update locale
this.logger.log(`${fileName} has updated`);
}
});
}
private postProcess(template: string, args: any) {
if (!template) {
return;
}
// see benchmark
return Object.keys(args).reduce((res, key) => {
return res.replace('${' + key + '}', args[key]);
}, template);
}
}

@@ -1,2 +0,3 @@

export { I18n } from "./i18n";
export * from './adapters';
export * from './utils';
export * from './i18n';

@@ -0,1 +1,2 @@

import { BaseAdapter } from '../adapters';
export interface ILocaleFileName {

@@ -5,5 +6,16 @@ [locale: string]: string;

export interface ILocale {
[key: string]: any;
}
export interface ILocaleMap {
[language: string]: ILocale;
}
export interface II18nConstructorOptions {
autoReoload?: boolean;
language: string;
adapter: BaseAdapter;
defaultLanguage: string;
}
export interface IFileSyncAdapterConstructorOptions {
localesBaseDir: string;

@@ -16,1 +28,5 @@ localeFileName?: ILocaleFileName;

}
export enum ERUN_ENV {
dev = 'development',
}
const assert = require('assert');
const path = require('path');
const I18n = require('../dist/index').I18n;
const { I18n, FileSyncAdapter, ObjectAdapter } = require('../dist/index');
const i18n = new I18n({
autoReoload: true,
language: 'zh',
const fileSyncAdapter = new FileSyncAdapter({
localesBaseDir: path.resolve(__dirname, './locales'),
});
describe('i18n', function(){
const objectAdapter = new ObjectAdapter({
zh: {
user: {
name: 'PicGo',
country: '中国',
},
report: {
singular: ' ${cnt}个报告',
plural: '${cnt}个报告',
},
},
en: {
user: {
name: 'PicGo',
country: 'China',
},
report: {
singular: 'only ${cnt} report',
plural: '${cnt} reports',
},
},
});
it('translate', () => {
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2个报告');
describe('i18n', () => {
describe('fileSyncAdapter', () => {
const i18n = new I18n({
adapter: fileSyncAdapter,
defaultLanguage: 'zh',
});
it('translate', () => {
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2个报告');
});
it('setLanguage', () => {
i18n.setLanguage('en');
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2 reports');
});
});
it('setLanguate', () => {
i18n.setLanguage('en');
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2 reports');
describe('objectAdapter', () => {
const i18n = new I18n({
adapter: objectAdapter,
defaultLanguage: 'zh',
});
it('translate', () => {
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2个报告');
});
it('setLanguage', () => {
i18n.setLanguage('en');
assert.equal(i18n.translate('report.plural', { cnt: 2 }), '2 reports');
});
it('setLocales', () => {
objectAdapter.setLocales({
en: {
user: {
name: 'PicGo',
country: 'China',
},
post: {
singular: 'only ${cnt} post',
plural: '${cnt} posts',
},
},
});
assert.equal(i18n.translate('post.plural', { cnt: 2 }), '2 posts');
});
});
});
});
{
"user": {
"name": "PicGo",
"counter": "China"
"country": "China"
},

@@ -6,0 +6,0 @@ "report": {

{
"user": {
"name": "PicGo",
"counter": "China"
"country": "China"
},

@@ -6,0 +6,0 @@ "report": {

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc