Browse Source

Merge pull request #631 from wingkwong/feat/i18n

init: noco-i18n
pull/634/head
աɨռɢӄաօռɢ 3 years ago committed by GitHub
parent
commit
938b994ead
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      packages/noco-i18n/.gitignore
  2. 53
      packages/noco-i18n/README.md
  3. 5
      packages/noco-i18n/babel.config.js
  4. 12230
      packages/noco-i18n/package-lock.json
  5. 43
      packages/noco-i18n/package.json
  6. BIN
      packages/noco-i18n/public/favicon.ico
  7. 17
      packages/noco-i18n/public/index.html
  8. 26
      packages/noco-i18n/src/App.vue
  9. BIN
      packages/noco-i18n/src/assets/logo.png
  10. 211
      packages/noco-i18n/src/components/Landing.vue
  11. 68
      packages/noco-i18n/src/index.js
  12. 4
      packages/noco-i18n/src/main.js

23
packages/noco-i18n/.gitignore vendored

@ -0,0 +1,23 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

53
packages/noco-i18n/README.md

@ -0,0 +1,53 @@
# noco-i18n
![image](https://user-images.githubusercontent.com/35857179/136654196-162a316c-adde-431b-8316-139168298278.png)
## Prerequisites
You need vue cli in order to start the application
```
npm install -g @vue/cli
```
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```
## Contribution Guide
1. Our i18n translations are in google spreadsheet - make a [copy of it](https://docs.google.com/spreadsheets/d/1kGp92yLwhs1l7lwwgeor3oN1dFl7JZWuQOa4WSeZ0TE/edit?usp=sharing). Create a shareable link from your spreadsheet.
2. Make necessary changes for the required language column (eg. fr)
3. Download the sheet as .csv file
4. Visit [NocoDB i18n CSV Converter](https://i18n.nocodb.com/)
5. Upload the CSV file.
6. Select the language for which you are translating (same as in step 2)
NocoDB i18n CSV Converter will
- automatically copy the translated values to clipboard.
- automatically show the respective file to open and edit in our github. Example: [fr.json](https://github.com/nocodb/nocodb/edit/master/packages/nc-gui/lang/fr.json).
- Github will ask you to fork the repo - please do so if you haven't forked the repository and then paste the values from clipboard to the file. Alternatively you can just paste the updated JSON value to corresponing files
7. Submit PR with a link to your spreadsheet from step 1

5
packages/noco-i18n/babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

12230
packages/noco-i18n/package-lock.json generated

File diff suppressed because it is too large Load Diff

43
packages/noco-i18n/package.json

@ -0,0 +1,43 @@
{
"name": "noco-i18n",
"version": "1.0.0",
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.5",
"csv-parser": "^3.0.0",
"papaparse": "^5.3.1",
"vue": "^3.0.0"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"@vue/compiler-sfc": "^3.0.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^7.0.0"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

BIN
packages/noco-i18n/public/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

17
packages/noco-i18n/public/index.html

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

26
packages/noco-i18n/src/App.vue

@ -0,0 +1,26 @@
<template>
<img alt="Vue logo" src="./assets/logo.png">
<Landing />
</template>
<script>
import Landing from './components/Landing.vue'
export default {
name: 'App',
components: {
Landing
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

BIN
packages/noco-i18n/src/assets/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

211
packages/noco-i18n/src/components/Landing.vue

@ -0,0 +1,211 @@
<template>
<div class="wrapper">
<h1>Welcome to NocoDB i18n CSV Converter</h1>
<h2>
For the guideline, please check out <a href="https://github.com/nocodb/nocodb/tree/master/packages/noco-i18n#contribution-guide" target="_blank" rel="noopener">NocoDB i18n Contribution Guide</a>.
</h2>
<table>
<tr>
<td>Upload your CSV file</td>
<td>
<div>
<input type="file" id="file" ref="csvFile" class="file-input" @change="uploadFile">
<label for="file">
<span>Select your CSV</span>
</label>
</div>
</td>
</tr>
<tr v-if="completeStep1">
<td>Select the language you translated</td>
<td>
<div class="select-wrapper">
<select v-on:change="changeItem($event)">
<option value="-">Select the language</option>
<option value="en">English</option>
<option value="fr">Français</option>
<option value="de">Deutsch</option>
<option value="es">Española</option>
<option value="pt">Portuguese</option>
<option value="it_IT">Italiano</option>
<option value="nl">Nederlandse</option>
<option value="ja">日本語</option>
<option value="ko">한국인</option>
<option value="ru">Pусский</option>
<option value="id">Bahasa Indonesia</option>
<option value="zh_CN">大陆简体</option>
<option value="zh_HK">香港繁體</option>
<option value="zh_TW">臺灣正體</option>
<option value="sv">Svenska</option>
<option value="da">Dansk</option>
<option value="vi">Tiếng Việt</option>
<option value="fi">Norsk</option>
<option value="hr">עִברִית</option>
<option value="iw">Suomalainen</option>
<option value="no">Українська</option>
<option value="th">Hrvatski</option>
<option value="uk">ไทย</option>
</select>
</div>
</td>
</tr>
</table>
<h3 class="success-msg" v-if="completeStep2">The generated JSON file has been copied to your clipboard.</h3>
</div>
</template>
<script>
const generate = require('../index');
const papa = require('papaparse');
export default {
name: 'Landing',
props: {
msg: String
},
data() {
return {
completeStep1: false,
completeStep2: false,
selectedLanguage: "-",
}
},
methods: {
async changeItem(event) {
let file = this.$refs.csvFile.files[0]
if (!file || file.type != 'text/csv') {
this.completeStep2 = false
alert('No csv is selected')
throw new Error('No csv is selected')
} else {
let targetLanguage = event.target.value;
if(targetLanguage != '-') {
papa.parse(file, {
worker: true,
// step: function(result) {
// // console.log(result)
// },
complete: function(results) {
generate(results.data, targetLanguage)
}
});
this.completeStep2 = true
} else {
this.completeStep2 = false
}
}
},
uploadFile() {
this.completeStep1 = true
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
[type="file"] {
height: 0;
overflow: hidden;
width: 0;
}
[type="file"] + label {
width: 128px;
border: none;
border-radius: 5px;
color: #fff;
cursor: pointer;
display: inline-block;
font-family: 'Rubik', sans-serif;
font-size: inherit;
font-weight: 500;
outline: none;
padding: 20px 50px;
position: relative;
vertical-align: middle;
background-color: #0180ff;
}
.select-wrapper {
position: relative;
display: block;
width: 230px;
height: 60px;
line-height: 3;
background: #2c3e50;
overflow: hidden;
border-radius: 5px;
margin-top: 20px;
margin-left: auto;
margin-right: auto;
}
select {
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
outline: 0;
box-shadow: none;
border: 0 !important;
background: #0180ff;
background-image: none;
}
select {
width: 100%;
height: 100%;
margin: 0;
text-align: center;
cursor: pointer;
color: #fff;
font-family: 'Rubik', sans-serif;
font-weight: 500;
font-size: inherit;
}
select::-ms-expand {
display: none;
}
.select-wrapper::after {
content: '\25BC';
position: absolute;
top: 0;
right: 0;
bottom: 0;
padding: 5px 15px;
background: #0180ff;
color: #fff;
pointer-events: none;
}
table {
margin-left: auto;
margin-right: auto;
}
td {
padding: 10px 40px;
}
.success-msg {
color: #4657eb;
}
</style>

68
packages/noco-i18n/src/index.js

@ -0,0 +1,68 @@
module.exports = async (csvDatas, targetLanguage) => {
// function for mapping nested property
const mapPropToObject = (obj, prop, val) => {
const keys = prop.split('.');
for (let i = 0, prev = obj; i < keys.length; i++) {
// if last keys assign or overwrite value
if (i === keys.length - 1) {
prev[keys[i]] = val
} else {
// define or re-assign prev value
prev = prev[keys[i]] = prev[keys[i]] || {}
}
}
}
// const downloadJson = (exportObj, exportName) => {
// var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj, null, 2))
// var downloadAnchorNode = document.createElement('a')
// downloadAnchorNode.setAttribute("href", dataStr)
// downloadAnchorNode.setAttribute("download", exportName + ".json")
// document.body.appendChild(downloadAnchorNode)
// downloadAnchorNode.click()
// downloadAnchorNode.remove()
// }
const copyJsonToClipboard = (str, targetLanguage) => {
var el = document.createElement('textarea')
el.value = str
el.setAttribute('readonly', '')
el.style = {position: 'absolute', left: '-9999px'}
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
// alert("The target JSON has been copied to your clipboard")
popEditPage(targetLanguage)
}
const popEditPage = (targetLanguage) => {
var editAnchorNode = document.createElement('a')
editAnchorNode.setAttribute("href", `https://github.com/nocodb/nocodb/edit/master/packages/nc-gui/lang/${targetLanguage}.json`)
editAnchorNode.setAttribute("target", "_blank")
document.body.appendChild(editAnchorNode)
editAnchorNode.click()
editAnchorNode.remove()
}
if (!csvDatas.length) throw new Error('Empty csv')
const languageObjs = {};
const languages = Object.keys(csvDatas[0]).filter(k => k !== 'String Key' && k !== 'String');
for (const data of csvDatas) {
for (const lan of languages) {
languageObjs[lan] = languageObjs[lan] || {};
mapPropToObject(languageObjs[lan], data[0], data[lan])
}
}
for (const [ln, obj] of Object.entries(languageObjs)) {
if(languageObjs[ln]['String Key'] == targetLanguage) {
delete languageObjs[ln]['String Key'];
// downloadJson(obj, targetLanguage)
copyJsonToClipboard(JSON.stringify(obj, null, 2), targetLanguage)
return
}
}
}

4
packages/noco-i18n/src/main.js

@ -0,0 +1,4 @@
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
Loading…
Cancel
Save