Browse Source

init: noco-i18n

Signed-off-by: Wing-Kam Wong <wingkwong.code@gmail.com>
pull/631/head
Wing-Kam Wong 3 years ago
parent
commit
07e90d5335
  1. 53
      packages/noco-i18n/README.md
  2. 5
      packages/noco-i18n/babel.config.js
  3. 12230
      packages/noco-i18n/package-lock.json
  4. 43
      packages/noco-i18n/package.json
  5. BIN
      packages/noco-i18n/public/favicon.ico
  6. 17
      packages/noco-i18n/public/index.html
  7. 26
      packages/noco-i18n/src/App.vue
  8. BIN
      packages/noco-i18n/src/assets/logo.png
  9. 211
      packages/noco-i18n/src/components/Landing.vue
  10. 68
      packages/noco-i18n/src/index.js
  11. 4
      packages/noco-i18n/src/main.js

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