Skip to content

Commit

Permalink
translations: refactor cli
Browse files Browse the repository at this point in the history
  • Loading branch information
zzacharo committed Jul 5, 2021
1 parent 7003810 commit 49a90f9
Show file tree
Hide file tree
Showing 20 changed files with 177 additions and 153 deletions.
25 changes: 14 additions & 11 deletions .tx/config
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@
# React-Invenio-Deposit is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.

#
# 1) Extract translation keys/values,
# $ npm run i18n-scan
# 2) Update .pot file, this file is pushed to transifex
# $ npm run i18n-conv-json
# 3) Convert/update .json from .po
# $ npm run i18n-conv-po-all
# 4) Install the transifex-client
#
# 1) Add a new language
# npm run init_catalog lang <lang>
# 2) Extract translation keys/values
# $ npm run extract_messages
# 3) Install the transifex-client
# $ pip install transifex-client
# 5) Push source (.pot) and translations (.po) to Transifex
# 4) Push source (.pot) and translations (.po) to Transifex
# $ tx push -s -t
# 6) Pull translations for a single language from Transifex
# 5) Pull translations for a single language from Transifex
# $ tx pull -l <lang>
# 7) Pull translations for all languages from Transifex
# 6) Pull translations for all languages from Transifex
# $ tx pull -a
# 7) Compile .po files for all languages
# $ npm run compile_catalog
# 8) Convert .po file for a single language
# $ npm run compile_catalog lang <lang>


[main]
host = https://www.transifex.com
Expand Down
24 changes: 0 additions & 24 deletions getTextConverter.js

This file was deleted.

93 changes: 40 additions & 53 deletions i18next-scanner.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,66 +4,53 @@
// React-Invenio-Deposit is free software; you can redistribute it and/or modify it
// under the terms of the MIT License; see LICENSE file for more details.

const fs = require('fs');
const chalk = require('chalk');
const { languages } = require('./package').config;

// list of func used to
// list of func used to
// mark the strings for translation
const funcList = ['i18next.t', 'i18n.t', 't'];
const funcList = ['i18next.t'];

module.exports = {
options: {
debug: true,
browserLanguageDetection: true,
func: {
list: funcList,
extensions: ['.js', '.jsx']
},
trans: false, // Enable for using Trans component
lngs: languages,
ns: [
// file name (.json)
'translations',
],
defaultLng: 'en',
defaultNs: 'translations',
resource: {
// The path where resources get loaded from. Relative to current working directory.
loadPath: 'src/lib/translations/{{lng}}/{{ns}}.json',

// The path to store resources.
savePath: 'src/lib/translations/{{lng}}/{{ns}}.json',

jsonIndent: 2,
lineEnding: '\n'
},
nsSeparator: false, // namespace separator

//Set to false to disable key separator
// if you prefer having keys as the fallback for translation (e.g. gettext).
keySeparator: false,
options: {
debug: true,
removeUnusedKeys: true,
browserLanguageDetection: true,
func: {
list: funcList,
extensions: ['.js', '.jsx'],
},
// trans: false, // Enable for using Trans component
lngs: languages,
ns: [
// file name (.json)
'translations',
],
defaultLng: 'en',
defaultNs: 'translations',
// @param {string} lng The language currently used.
// @param {string} ns The namespace currently used.
// @param {string} key The translation key.
// @return {string} Returns a default value for the translation key.
defaultValue: function (lng, ns, key) {
if (lng === 'en') {
// Return key as the default value for English language
return key;
}
},
resource: {
// The path where resources get loaded from. Relative to current working directory.
loadPath: 'src/lib/translations/messages/{{lng}}/{{ns}}.json',

// func is provided
// (if needed)for more custom extractions.
transform: function customTransform(file, enc, done) {
"use strict";
const parser = this.parser;
const content = fs.readFileSync(file.path, enc);
let count = 0;
// The path to store resources.
savePath: 'src/lib/translations/messages/{{lng}}/{{ns}}.json',

parser.parseFuncFromString(content, { list: funcList }, (key, options) => {
parser.set(key, Object.assign({}, options, {
nsSeparator: false,
keySeparator: false
}));
++count;
});
jsonIndent: 2,
lineEnding: '\n',
},
nsSeparator: false, // namespace separator

if (count > 0) {
console.log(`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(JSON.stringify(file.relative))}`);
}
done();
}
//Set to false to disable key separator
// if you prefer having keys as the fallback for translation (e.g. gettext).
keySeparator: false,
},
};
12 changes: 7 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@
"postlink-dist": "cd dist && rm -rf node_modules",
"unlink-dist": "cd dist && npm unlink && rm package*",
"watch": "NODE_ENV=development rollup --watch -c",
"i18n-scan": "i18next-scanner --config i18next-scanner.config.js src/**/*.{js,jsx}",
"i18n-conv-json": "i18next-conv -l en -s ./src/lib/translations/en/translations.json -t ./src/lib/translations/translations.pot",
"i18n-conv-po": "i18next-conv -l $npm_config_lang -s ./src/lib/translations/$npm_config_lang/messages.po -t ./src/lib/translations/$npm_config_lang/translations.json",
"i18n-conv-po-all": "node ./getTextConverter.js",
"extract_messages": "i18next-scanner --config i18next-scanner.config.js 'src/**/*.{js,jsx}'",
"postextract_messages": "i18next-conv -l en -s ./src/lib/translations/messages/en/translations.json -t ./src/lib/translations/translations.pot",
"compile_catalog": "node ./src/lib/translations/scripts/compileCatalog.js",
"init_catalog": "node ./src/lib/translations/scripts/initCatalog",
"test": "react-scripts test",
"eject": "react-scripts eject",
"lint": "eslint src/**/*.js",
"format": "prettier --config ./.prettierrc --ignore-path ./.prettierignore --write \"src/**/*.js\""
},
"dependencies": {},
"peerDependencies": {
"@ckeditor/ckeditor5-build-classic": "^16.0.0",
"@ckeditor/ckeditor5-react": "^2.1.0",
"axios": "^0.21.1",
"formik": "^2.1.4",
"react": "^16.13.1",
Expand Down Expand Up @@ -136,4 +138,4 @@
"enzyme-to-json/serializer"
]
}
}
}
6 changes: 4 additions & 2 deletions src/lib/components/DepositFormTitle.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Header } from 'semantic-ui-react';
import i18next from '../i18next';
import { i18next } from '../i18next';

class DepositFormTitleComponent extends Component {
render() {
let content = '';
if (!this.props.isPublished) {
content = this.props.isVersion ? i18next.t('New version') : i18next.t('New upload');
content = this.props.isVersion
? i18next.t('New version')
: i18next.t('New upload');
} else {
content = i18next.t('Edit upload');
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/FileUploader/FileUploaderToolbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useFormikContext } from 'formik';
import React from 'react';
import { Checkbox, Grid, Icon, Label, List, Popup } from 'semantic-ui-react';
import { humanReadableBytes } from './utils';
import i18next from '../../i18next';
import { i18next } from '../../i18next';

// NOTE: This component has to be a function component to allow
// the `useFormikContext` hook.
Expand All @@ -30,7 +30,7 @@ export const FileUploaderToolbar = ({
<List.Item>
<Checkbox
label={'Metadata-only record'}
onChange={() => setFieldValue("files.enabled", !filesEnabled)}
onChange={() => setFieldValue('files.enabled', !filesEnabled)}
disabled={filesList.length > 0}
checked={!filesEnabled}
/>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Identifiers/IdentifiersField.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
TextField,
} from 'react-invenio-forms';
import { Button, Form } from 'semantic-ui-react';
import { i18next } from '../../i18next';
import { emptyIdentifier } from '../../record';

/** Identifiers array component */
Expand All @@ -24,7 +25,7 @@ export class IdentifiersField extends Component {
return (
<>
<ArrayField
addButtonLabel={'Add identifier'}
addButtonLabel={i18next.t('Add identifier')}
defaultNewValue={emptyIdentifier}
fieldPath={fieldPath}
label={
Expand Down
54 changes: 23 additions & 31 deletions src/lib/i18next.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,36 @@

import i18n from 'i18next';

import TRANSLATIONS_DE from './translations/de/translations.json';
import TRANSLATIONS_EN from './translations/en/translations.json';
import TRANSLATIONS_TR from './translations/tr/translations.json';

import LanguageDetector from 'i18next-browser-languagedetector';
const { languages } = require('./package').config;

const options = {
fallbackLng: 'en',
returnEmptyString: false, // fallback keys
debug: true,
resources: {
en: {
translation: TRANSLATIONS_EN,
},
de: {
translation: TRANSLATIONS_DE,
},
tr: {
translation: TRANSLATIONS_TR,
},
},
// specify language detection order
detection: {
order: ['htmlTag'],
// cache user language off
caches: [],
}
}
fallbackLng: 'en', // fallback keys
returnEmptyString: false,
debug: true,
resources: languages.reduce(async (obj, lang) => {
const translationFile = await import(
`./translations/messages/${lang}/translations.json`
);
return {
...obj,
[lang]: translationFile,
};
}, {}),
// specify language detection order
detection: {
order: ['htmlTag'],
// cache user language off
caches: [],
},
};
// i18next instance creation
// https://www.i18next.com/overview/api#instance-creation
// this is required in order to keep the resources seperate
// if there is going to be another package
// if there is going to be another package
// which requires translation this is the way to create a new instance.
//
// We can use this particular instance for this particular package
// to mark strings for translation.
const i18next = i18n.createInstance();
i18next
.use(LanguageDetector)
.init(options);
export default i18next;
export const i18next = i18n.createInstance();
i18next.use(LanguageDetector).init(options);
6 changes: 0 additions & 6 deletions src/lib/translations/de/translations.json

This file was deleted.

6 changes: 0 additions & 6 deletions src/lib/translations/en/translations.json

This file was deleted.

File renamed without changes.
6 changes: 6 additions & 0 deletions src/lib/translations/messages/de/translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"New version": "Neue Version",
"New upload": "Neuer Upload",
"Edit upload": "Upload bearbeiten",
"Storage available": "Verfügbarer Speicherplatz"
}
File renamed without changes.
7 changes: 7 additions & 0 deletions src/lib/translations/messages/en/translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"New version": "New version",
"New upload": "New upload",
"Edit upload": "Edit upload",
"Storage available": "Storage available",
"Add identifier": "Add identifier"
}
File renamed without changes.
6 changes: 6 additions & 0 deletions src/lib/translations/messages/tr/translations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"New version": "",
"New upload": "",
"Edit upload": "",
"Storage available": ""
}
38 changes: 38 additions & 0 deletions src/lib/translations/scripts/compileCatalog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// This file is part of React-Invenio-Deposit
// Copyright (C) 2021 Graz University of Technology.
//
// React-Invenio-Deposit is free software; you can redistribute it and/or modify it
// under the terms of the MIT License; see LICENSE file for more details.

const { readFileSync, writeFileSync } = require('fs');
const { gettextToI18next } = require('i18next-conv');
const { languages } = require('./package').config;

// it accepts the same options as the cli.
// https://github.com/i18next/i18next-gettext-converter#options
const options = {
/* you options here */
};

function save(target) {
return (result) => {
writeFileSync(target, result);
};
}

if ('lang' === process.argv[2]) {
const lang = process.argv[3];
gettextToI18next(
lang,
readFileSync(`src/lib/translations/${lang}/messages.po`),
options
).then(save(`src/lib/translations/${lang}/translations.json`));
} else {
for (const lang of languages) {
gettextToI18next(
lang,
readFileSync(`src/lib/translations/${lang}/messages.po`),
options
).then(save(`src/lib/translations/${lang}/translations.json`));
}
}
Loading

0 comments on commit 49a90f9

Please sign in to comment.