Merge pull request #396 from toeverything/develop

Sync develop to master
This commit is contained in:
DarkSky
2022-09-09 19:01:47 +08:00
committed by GitHub
13 changed files with 204 additions and 80 deletions

View File

@@ -1,10 +1,17 @@
name: Lint name: Languages Sync
on: on:
push: push:
branches: [master] branches: [ "develop", "master" ]
# pull_request: paths:
# branches: [master] - 'libs/datasource/i18n/**'
- '.github/workflows/languages-sync.yml'
pull_request:
branches: [ "develop", "master" ]
paths:
- 'libs/datasource/i18n/**'
- '.github/workflows/languages-sync.yml'
workflow_dispatch:
# Cancels all previous workflow runs for pull requests that have not completed. # Cancels all previous workflow runs for pull requests that have not completed.
# See https://docs.github.com/en/actions/using-jobs/using-concurrency # See https://docs.github.com/en/actions/using-jobs/using-concurrency
@@ -18,11 +25,9 @@ jobs:
main: main:
strategy: strategy:
matrix: matrix:
node-version: [16] node-version: [18]
os: [ubuntu-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
# TODO Remove the next line after cleaning all errors
continue-on-error: true
steps: steps:
- name: Checkout - name: Checkout
@@ -43,20 +48,16 @@ jobs:
- name: Install node modules - name: Install node modules
run: pnpm install run: pnpm install
- name: Lint - name: Check Language Key
if: always() if: github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/master'
run: pnpm run lint:with-cache working-directory: ./libs/datasource/i18n
run: pnpm run sync-languages:check
env:
TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}
# - name: Check - name: Sync Languages
# if: always() if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master'
# run: pnpm run check working-directory: ./libs/datasource/i18n
run: pnpm run sync-languages
# - name: Format Check env:
# if: always() TOLGEE_API_KEY: ${{ secrets.TOLGEE_API_KEY }}
# run: pnpm run format:ci
# - name: Build
# run: pnpm run build
# - name: Test
# run: pnpm run test

87
.github/workflows/livedemo.yml vendored Normal file
View File

@@ -0,0 +1,87 @@
name: Build AFFiNE-Local
on:
push:
branches: [master]
# pull_request:
# branches: [master]
# Cancels all previous workflow runs for pull requests that have not completed.
# See https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
# The concurrency group contains the workflow name and the branch name for
# pull requests or the commit hash for any other events.
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }}
cancel-in-progress: true
env:
REGISTRY: ghcr.io
NAMESPACE: toeverything
AFFINE_IMAGE_NAME: LIVEDEMO
IMAGE_TAG_LATEST: nightly-latest
LOCAL_CACHE: localhost:5000/toeverything/relocate:latest
jobs:
ligo-virgo:
runs-on: self-hosted
environment: development
permissions:
contents: read
packages: write
services:
registry:
image: registry:2
ports:
- 5000:5000
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
driver-opts: network=host
- name: Extract metadata (tags, labels) for Docker (AFFiNE-Local)
id: meta_affine
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.NAMESPACE }}/${{ env.AFFINE_IMAGE_NAME }}
tags: ${{ env.IMAGE_TAG_LATEST }}
- name: Build and push Docker image (AFFINE-Local)
uses: docker/build-push-action@v3
env:
HUBSPOT_API_SECRET: ${{ secrets.SuperSecret }}
with:
context: .
push: true
file: ./.github/deployment/Dockerfile-affine
tags: ${{ env.LOCAL_CACHE }}
target: AFFiNE
- name: Build and push Docker image (AFFINE-Local)
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm64/v8
file: ./.github/deployment/Dockerfile-affine-multiplatform
push: ${{ github.ref == 'refs/heads/master' && true || false }}
tags: ${{ steps.meta_affine.outputs.tags }}
labels: ${{ steps.meta_affine.outputs.labels }}
target: AFFiNE
build-args: |
BASE_IMAGE=${{ env.LOCAL_CACHE }}

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title><%= htmlWebpackPlugin.options.title %></title> <title><%= htmlWebpackPlugin.options.title %></title>
<%= htmlWebpackPlugin.options.hubspotScript %>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@@ -12,6 +12,13 @@ const Style9Plugin = require('style9/webpack');
const enableBundleAnalyzer = process.env.BUNDLE_ANALYZER; const enableBundleAnalyzer = process.env.BUNDLE_ANALYZER;
function generateHubspotScript() {
const hubspotApiSecret = process.env.HUBSPOT_API_SECRET;
if (hubspotApiSecret) {
return `<script type="text/javascript" id="hs-script-loader" async defer src="//js-na1.hs-scripts.com/${hubspotApiSecret}.js"></script>`;
}
}
module.exports = function (webpackConfig) { module.exports = function (webpackConfig) {
const config = getNxWebpackConfig(webpackConfig); const config = getNxWebpackConfig(webpackConfig);
@@ -175,6 +182,7 @@ module.exports = function (webpackConfig) {
), //favicon path ), //favicon path
template: path.resolve(__dirname, './src/template.html'), template: path.resolve(__dirname, './src/template.html'),
publicPath: '/', publicPath: '/',
hubspotScript: generateHubspotScript(),
}), }),
new Style9Plugin(), new Style9Plugin(),
isProd && isProd &&

View File

@@ -7,7 +7,7 @@ import { useMediaQuery } from '@mui/material';
const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({ const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({
position: 'relative', position: 'relative',
width: '20em', width: '24em',
height: '128px', height: '128px',
transform: 'translateY(-8px)', transform: 'translateY(-8px)',
overflowY: 'hidden', overflowY: 'hidden',
@@ -21,7 +21,7 @@ const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({
height: 'inherit', height: 'inherit',
position: 'absolute', position: 'absolute',
left: '0', left: '0',
top: '0', top: '-23px',
paddingTop: '22px', paddingTop: '22px',
lineHeight: '96px', lineHeight: '96px',
'@media (max-width: 1024px)': { '@media (max-width: 1024px)': {
@@ -105,7 +105,7 @@ export const AlternativesProduct = () => {
)} )}
> >
<Typography <Typography
fontSize="64px" fontSize="96px"
fontWeight={900} fontWeight={900}
sx={{ sx={{
color: '#06449d', color: '#06449d',
@@ -135,7 +135,7 @@ export const AlternativesProduct = () => {
}} }}
> >
<Typography <Typography
fontSize="64px" fontSize="96px"
fontWeight={900} fontWeight={900}
sx={{ sx={{
color: '#06449d', color: '#06449d',

View File

@@ -64,7 +64,7 @@ export function IndexPage() {
return ( return (
<> <>
<AFFiNEHeader /> <AFFiNEHeader />
<Grid xs={12} sx={{ display: 'flex', marginTop: '12vh!important' }}> <Grid xs={12} sx={{ display: 'flex', marginTop: '20vh!important' }}>
<Box <Box
sx={{ sx={{
display: 'inline-flex', display: 'inline-flex',
@@ -76,7 +76,7 @@ export function IndexPage() {
}} }}
> >
<Typography <Typography
fontSize="64px" fontSize="96px"
fontWeight={900} fontWeight={900}
sx={{ sx={{
marginRight: '0.25em', marginRight: '0.25em',
@@ -89,7 +89,7 @@ export function IndexPage() {
{t('Open Source')}, {t('Open Source')},
</Typography> </Typography>
<Typography <Typography
fontSize="64px" fontSize="96px"
fontWeight={900} fontWeight={900}
sx={{ sx={{
'@media (max-width: 1024px)': { '@media (max-width: 1024px)': {
@@ -120,7 +120,7 @@ export function IndexPage() {
> >
<AlternativesProduct /> <AlternativesProduct />
<Typography <Typography
fontSize="64px" fontSize="96px"
fontWeight={900} fontWeight={900}
sx={{ sx={{
color: '#06449d', color: '#06449d',

View File

@@ -139,34 +139,6 @@ export const AFFiNEHeader = () => {
window.open(href); window.open(href);
}} }}
/> />
<HoverMenu
title={t('ContactUs')}
options={[
{
title: 'Discord',
value: 'https://discord.gg/Arn7TqJBvG',
},
{
title: 'Telegram',
value: 'https://t.me/affineworkos',
},
{
title: 'Reddit',
value: 'https://www.reddit.com/r/Affine/',
},
{
title: 'Medium',
value: 'https://medium.com/@affineworkos',
},
{
title: 'Email',
value: 'mailto:contact@toeverything.info',
},
]}
onSelect={href => {
window.open(href);
}}
/>
<Select <Select
defaultValue="en" defaultValue="en"
sx={{ display: matchesIPAD ? 'none' : 'intial' }} sx={{ display: matchesIPAD ? 'none' : 'intial' }}
@@ -192,6 +164,7 @@ const Container = styled(Grid)({
transform: 'translateX(-50%)', transform: 'translateX(-50%)',
width: '100%', width: '100%',
paddingTop: '1em', paddingTop: '1em',
paddingBottom: '1em',
backgroundColor: '#fff', backgroundColor: '#fff',
zIndex: 1500, zIndex: 1500,
maxWidth: '1440px', maxWidth: '1440px',

View File

@@ -8,7 +8,7 @@ import { useTranslation } from '@toeverything/datasource/i18n';
// base.json // base.json
// { // {
// 'Text': 'some text', // 'Text': 'some text',
// 'Switch to language': 'Switch to {language}', // 'Switch to language': 'Switch to {{language}}', // <- you can interpolation by curly brackets
// }; // };
const App = () => { const App = () => {
@@ -41,10 +41,12 @@ const App = () => {
export TOLGEE_API_KEY=tgpak_XXXXXXX export TOLGEE_API_KEY=tgpak_XXXXXXX
``` ```
- Run the `sync` script from package script - Run the `sync-languages:check` to check all languages
- Run the `sync-languages` script to add new keys to the tolgee platform
## References ## References
- [AFFiNE | Tolgee](https://i18n.affine.pro/)
- [Tolgee Documentation](https://tolgee.io/docs/)
- [i18next](https://www.i18next.com/) - [i18next](https://www.i18next.com/)
- [react-i18next](https://react.i18next.com/) - [react-i18next](https://react.i18next.com/)
- [Tolgee](https://tolgee.io/docs/)

View File

@@ -2,11 +2,11 @@
"name": "@toeverything/datasource/i18n", "name": "@toeverything/datasource/i18n",
"version": "0.0.1", "version": "0.0.1",
"scripts": { "scripts": {
"sync": "NODE_OPTIONS=--experimental-fetch ts-node src/scripts/sync.ts" "sync-languages": "NODE_OPTIONS=--experimental-fetch ts-node src/scripts/sync.ts",
"sync-languages:check": "pnpm run sync-languages --check"
}, },
"dependencies": { "dependencies": {
"i18next": "^21.9.1", "i18next": "^21.9.1",
"jotai": "^1.8.1",
"react-i18next": "^11.18.4" "react-i18next": "^11.18.4"
} }
} }

View File

@@ -19,6 +19,8 @@ declare module 'react-i18next' {
} }
} }
const STORAGE_KEY = 'i18n_lng';
const LOCALES = [ const LOCALES = [
{ value: 'en', text: 'English', res: en_US }, { value: 'en', text: 'English', res: en_US },
{ value: 'zh', text: '简体中文', res: zh_CN }, { value: 'zh', text: '简体中文', res: zh_CN },
@@ -29,11 +31,26 @@ const resources = LOCALES.reduce<Resource>(
{} {}
); );
const fallbackLng = LOCALES[0].value;
const standardizeLocale = (language: string) => {
if (LOCALES.find(locale => locale.value === language)) return language;
if (
LOCALES.find(
locale => locale.value === language.slice(0, 2).toLowerCase()
)
)
return language;
return fallbackLng;
};
const language = standardizeLocale(
localStorage.getItem(STORAGE_KEY) ?? navigator.language
);
const i18n = i18next.createInstance(); const i18n = i18next.createInstance();
i18n.use(initReactI18next).init({ i18n.use(initReactI18next).init({
// TODO auto detect lng: language,
lng: LOCALES[0].value, fallbackLng,
fallbackLng: LOCALES[0].value,
debug: process.env['NODE_ENV'] === 'development', debug: process.env['NODE_ENV'] === 'development',
resources, resources,
@@ -42,6 +59,10 @@ i18n.use(initReactI18next).init({
}, },
}); });
i18n.on('languageChanged', lng => {
localStorage.setItem(STORAGE_KEY, lng);
});
const I18nProvider = I18nextProvider; const I18nProvider = I18nextProvider;
export { i18n, useTranslation, I18nProvider, LOCALES }; export { i18n, useTranslation, I18nProvider, LOCALES };

View File

@@ -9,6 +9,7 @@ if (!TOLGEE_API_KEY) {
const withTolgee = ( const withTolgee = (
fetch: typeof globalThis.fetch fetch: typeof globalThis.fetch
): typeof globalThis.fetch => { ): typeof globalThis.fetch => {
const baseUrl = `${TOLGEE_API_URL}/v2/projects`;
const headers = new Headers({ const headers = new Headers({
'X-API-Key': TOLGEE_API_KEY, 'X-API-Key': TOLGEE_API_KEY,
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@@ -29,14 +30,14 @@ const withTolgee = (
if (!argArray[0].headers) { if (!argArray[0].headers) {
argArray[0] = { argArray[0] = {
...argArray[0], ...argArray[0],
url: `${TOLGEE_API_URL}/v2/projects${argArray[0].url}`, url: `${baseUrl}${argArray[0].url}`,
headers, headers,
}; };
} }
} else { } else {
// URL or URLLike + ?RequestInit // URL or URLLike + ?RequestInit
if (typeof argArray[0] === 'string') { if (typeof argArray[0] === 'string') {
argArray[0] = TOLGEE_API_URL + argArray[0]; argArray[0] = `${baseUrl}${argArray[0]}`;
} }
if (!argArray[1]) { if (!argArray[1]) {
argArray[1] = {}; argArray[1] = {};

View File

@@ -78,6 +78,41 @@ const differenceObject = (
return { add, remove, modify }; return { add, remove, modify };
}; };
function warnDiff(diff: { add: string[]; remove: string[]; modify: string[] }) {
if (diff.add.length) {
console.log('New keys found:', diff.add.join(', '));
//See https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-a-notice-message
process.env['CI'] &&
console.log(
`::notice file=${BASE_JSON_PATH},line=1,title=New keys::${diff.add.join(
', '
)}`
);
}
if (diff.remove.length) {
console.warn('[WARN]', 'Unused keys found:', diff.remove.join(', '));
process.env['CI'] &&
console.warn(
`::notice file=${BASE_JSON_PATH},line=1,title=Unused keys::${diff.remove.join(
', '
)}`
);
}
if (diff.modify.length) {
console.warn(
'[WARN]',
'Inconsistent keys found:',
diff.modify.join(', ')
);
process.env['CI'] &&
console.warn(
`::warning file=${BASE_JSON_PATH},line=1,title=Inconsistent keys::${diff.modify.join(
', '
)}`
);
}
}
const main = async () => { const main = async () => {
console.log('Loading local base translations...'); console.log('Loading local base translations...');
const baseLocalTranslations = JSON.parse( const baseLocalTranslations = JSON.parse(
@@ -107,17 +142,14 @@ const main = async () => {
); );
console.log(''); // new line console.log(''); // new line
diff.add.length && console.log('New keys found:', diff.add.join(', ')); warnDiff(diff);
diff.remove.length &&
console.warn('[WARN]', 'Unused keys found:', diff.remove.join(', '));
diff.modify.length &&
console.warn(
'[WARN]',
'Inconsistent keys found:',
diff.modify.join(', ')
);
console.log(''); // new line console.log(''); // new line
if (process.argv.slice(2).includes('--check')) {
// check mode
return;
}
diff.add.forEach(async key => { diff.add.forEach(async key => {
const val = flatLocalTranslations[key]; const val = flatLocalTranslations[key];
console.log(`Creating new key: ${key} -> ${val}`); console.log(`Creating new key: ${key} -> ${val}`);

2
pnpm-lock.yaml generated
View File

@@ -580,11 +580,9 @@ importers:
libs/datasource/i18n: libs/datasource/i18n:
specifiers: specifiers:
i18next: ^21.9.1 i18next: ^21.9.1
jotai: ^1.8.1
react-i18next: ^11.18.4 react-i18next: ^11.18.4
dependencies: dependencies:
i18next: 21.9.1 i18next: 21.9.1
jotai: 1.8.1
react-i18next: 11.18.4_i18next@21.9.1 react-i18next: 11.18.4_i18next@21.9.1
libs/datasource/jwt: libs/datasource/jwt: