@volcengine/i18n

starling火山引擎文案拉取和替换工具

Usage no npm install needed!

<script type="module">
  import volcengineI18n from 'https://cdn.skypack.dev/@volcengine/i18n';
</script>

README

简介

@volcengine/i18n是针对node、web等泛前端场景推出的国际化运行时sdk,可帮助解决远程拉取国际化翻译平台数据并在运行时实时进行文案替换,快速接入翻译平台帮助应用国际化

使用场景

该 sdk 默认使用 i18next 框架为底座并基于开源社区进行功能扩展,实现多个场景下的国际化能力。如需更多的能力可查询官方网站进行判断

并且多种场景下只需安装一次,默认情况下是React

npm install @volcengine/i18n

以下多场景中 backend 参数一致,统一说明

字段名称 必填 类型 默认值 说明
namespace number|number[] - 国际化翻译平台空间 id
apiKey string - 国际化翻译平台apikey,用于鉴权
projectId number - 国际化翻译平台项目 id,用于鉴权
operatorId number - 国际化翻译平台用户id,必须是主账号,用户鉴权
mode 'normal' | 'gray' | 'test' normal 选择拉取文案的环境,支持正式,测试,灰度环境。normal: 拉取正式环境 test: 拉取测试环境 gray: 拉取灰度环境
host string https://starling-public.snssdk.com 文案数据获取的域名,默认国内
fallbackLangs string[] [] 文案拉取兜底语种
version string|string[] [] 按版本获取文案,与namespace一一对应
expiredTime number 60 * 1000 本地缓存过期时间,默认一分钟
timeout number 10000 接口请求超时时间,默认10s
retry number 5 失败后重试次数,每隔1s重试一次,默认5次
enableWatch boolean false 针对node,开启自动定时更新本地缓存
cron string */20 * * * * * 针对node,每隔20分钟刷新一次,格式参考:https://crontab.guru/

React

React 场景下基于 react-i18next 进行处理,api一致,如需更多功能可查询官方网站

新建 i18n.js

import reactI18n from '@volcengine/i18n'
reactI18n.init({
  lng: 'zh',
  backend: {
    namespace: 3174,
    operatorId: 210041130,
    apiKey: '704dbe7057f510ec8e4aedf71dc34d4f',
    projectId: 4168,
  }
})
export default reactI18n

入口index.js

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './i18n';

ReactDOM.render(
  <Suspense fallback="Loading...">
    <App />
  </Suspense>,
  document.getElementById('root')
);

页面使用-类组件

import React, { Component } from 'react';
import { withTranslation, Trans } from '@volcengine/i18n';

class Page extends Component {
  render() {
    const { t, i18n } = this.props;

    const changeLanguage = (lng) => {
      i18n.changeLanguage(lng);
    };

    return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <button onClick={() => changeLanguage('zh')}>zh</button>
          <button onClick={() => changeLanguage('en')}>en</button>
        </div>
        <h1>文案</h1>
        <div>{t('normal')}</div>
        
              <h1>文案,带插值</h1>
        <div>{t('placeholder', { name: 'xx' })}</div>
        
        <h1>文案,富文本</h1>
        <Trans i18nKey="richText">
          To get started, edit <code>src/App.js</code> and save to reload.
        </Trans>

        <h1>文案,带富文本插值 </h1>
        <Trans
          i18nKey="Welcome, <0>{name}</0>!"
          components={[<code>src/App.js</code>]}
          values={{ name: 3232 }}
        />
      </div>
    );
  }
}

const PageWithTranslation = withTranslation('translations')(Page);
export default PageWithTranslation

页面使用-函数组件

import React, { Component } from 'react';
import { useTranslation, Trans } from '@volcengine/i18n';
function Page() {
  const { t, i18n } = useTranslation();
  const changeLanguage = (lng) => {
    i18n.changeLanguage(lng);
  };

  return (
    <div className="App">
      <div className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <button onClick={() => changeLanguage('zh')}>zh</button>
        <button onClick={() => changeLanguage('en')}>en</button>
      </div>
      <h1>文案</h1>
      <div>{t('normal')}</div>
      <h1>文案,带插值</h1>
      <div>{t('placeholder', { name: 'xx' })}</div>
      
      <h1>文案,富文本</h1>
      <Trans i18nKey="richText">
        To get started, edit <code>src/App.js</code> and save to reload.
      </Trans>

      <h1>文案,带富文本插值 </h1>
      <Trans
        i18nKey="Welcome, <0>{name}</0>!"
        components={[<code>src/App.js</code>]}
        values={{ name: 3232 }}
      />
    </div>
  );
}
export default Page

Vue

vue 场景下基于 vue-i18next 进行处理,api一致,如需更多功能可查询官方网站

入口main.js

import Vue from 'vue'
import App from './App.vue'
import vueI18n from '@volcengine/i18n/dist/vue';

const i18n = vueI18n.use(ICU).init({
  lng: 'en',
  backend: {
    namespace: 39174,
    operatorId: 2100041130,
    apiKey: '704dbe7057f511ec8e4aedf71dc34d4f',
    projectId: 4568,
  }
}, Vue)

new Vue({
  render: h => h(App),
  i18n: i18n
}).$mount('#app')

使用

// 当注入语言如下
const locales = {
  en: {
    tos: "Term of Service",
    term: "I accept {{0}}. {{1}}.",
    promise: "I promise",
    hello: "Hello {{name}}"
  }
};

<template>
  <div id="app">
    <h1>文案</h1>
    {{$t('tos')}}

    <h1>文案带插值</h1>
    <p ref="text" v-t="{ path: 'hello', args: { name: 'Hans' } }"></p>

    <h1>文案带富文本</h1>
    <i18next path="term" tag="label">
      <a href="#" target="_blank">{{ $t("tos") }}</a>
      <strong>{{ $t("promise") }}</strong>
    </i18next>
  </div>
</template>

node

node端如果有使用koa之类的框架,可以直接写一个中间件进行扩展:

const nodeI18n = require('@volcengine/i18n/dist/node')
module.exports = (options) => {
  const i18n = nodeI18n.init({
    lng: 'en',
    backend: {
      namespace: 3174,
      operatorId: 210041130,
      apiKey: '704dbe7057f510ec8e4aedf71dc34d4f',
      projectId: 4168,
      enableWatch: true
    }
  })
  
  return async (ctx, next) => {
    ctx.t = i18n.t
    await next()
  }
}

然后在代码中直接 ctx.t 进行调用

扩展功能

扩展功能都是基于i18next的生态,如想继续扩展可以在 i18next 插件自行寻找

浏览器语言检测

import LanguageDetector from 'i18next-browser-languagedetector';
import reactI18n from '@volcengine/i18n';
reactI18n.use(LanguageDetector).init({
  ...
})

ICU

文档:https://github.com/i18next/i18next-icu

import ICU from 'i18next-icu';
import reactI18n from '@volcengine/i18n';
reactI18n.use(ICU).init({
  ...
})

文案拉取

如果使用的不是 i18next 框架,但依旧要用到我们的国际化平台托管文案,我们也支持单独的文案拉取功能

web端

import WebBackend from '@volcengine/i18n/dist/backend/web'
// 这里的options同顶部backend参数
const web = new WebBackend({...})
web.load('zh', (err, data) => {
  // data 数据结构:{ key: value }
})
// 然后注入到对应的intl框架中

node端

import NodeBackend from '@volcengine/i18n/dist/backend/node'
// 这里的options同顶部backend参数
const node = new WebBackend({...})
node.load('zh', (err, data) => {
  // data 数据结构:{ key: value }
})
// 然后注入到对应的intl框架中

自定义场景扩展

自定义扩展必须实现以下四个方法

auth

获取签名接口

method: POST

参数

参数名 描述
url 鉴权文案接口地址
callback (err, data) => void,有错误传err,正常请求传data,data结构: string

fetch

拉取文案核心方案

method: GET

参数

参数名 描述
url 拉取文案接口地址
headers 主要是 { Authorization: '' } 用于接口鉴权,需要放于请求头中
callback (err, data) => void,有错误传err,正常请求传data,data结构: { key: value }

setCache

保存文案进本地缓存,可以同时存入一个过期时间

参数

参数名 描述
key 缓存key
value value结构: { key: value }

getCache

获取本地缓存

参数

参数名 描述
key 缓存key
callback (err, data) => void,有错误传err,正常请求传data,data结构: { key: value }
import BaseBackend from '@volcengine/i18n/dist/backend/base'

class OtherBackend extends BaseBackend {
  fetch(url, headers, callback) {
    throw new StarlingError('fetch() needs to be implemented.')
  }

  auth(url, callback) {
    throw new StarlingError('auth() needs to be implemented.')
  }

  getCache(key: string, callback) {
    try {
      const { expiredTime, value } = JSON.parse(localStorage.getItem(key) || '{}')
      if (Date.now() - expiredTime < this.options.expiredTime && Object.keys(value).length) 			{
        return callback(null, value)
      }
      callback(null, undefined)
    } catch (error) {
      callback(error, undefined)
    }
  }

  setCache(key, value) {
    try {
      const data = { expiredTime: Date.now(), value }
      localStorage.setItem(key, JSON.stringify(data))
    } catch (error) {
      //
    }
  }
}