feat(docs): add translation status (#1689)
Co-authored-by: Michel EDIGHOFFER <edimitchel@gmail.com>
This commit is contained in:
parent
0eefcfa281
commit
972a13499f
13 changed files with 705 additions and 3 deletions
15
docs/components/global/ClipboardIcon.vue
Normal file
15
docs/components/global/ClipboardIcon.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'ClipboardIcon',
|
||||
props: { copy: Boolean },
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg v-if="copy" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-7 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M7 7h10V5h2v14H5V5h2v2Z" />
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M19 3h-4.18C14.4 1.84 13.3 1 12 1c-1.3 0-2.4.84-2.82 2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-7 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M7 7h10V5h2v14H5V5h2v2m.5 6.5L9 12l2 2l4.5-4.5L17 11l-6 6l-3.5-3.5Z" />
|
||||
</svg>
|
||||
</template>
|
15
docs/components/global/ToogleIcon.vue
Normal file
15
docs/components/global/ToogleIcon.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'ToogleIcon',
|
||||
props: { up: Boolean },
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<svg v-if="up" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="m12 10.828l-4.95 4.95l-1.414-1.414L12 8l6.364 6.364l-1.414 1.414z" />
|
||||
</svg>
|
||||
<svg v-else xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="m12 13.172l4.95-4.95l1.414 1.414L12 16L5.636 9.636L7.05 8.222z" />
|
||||
</svg>
|
||||
</template>
|
338
docs/components/global/TranslationState.vue
Normal file
338
docs/components/global/TranslationState.vue
Normal file
|
@ -0,0 +1,338 @@
|
|||
<script setup lang="ts">
|
||||
import type { TranslationStatus } from '../../types'
|
||||
|
||||
const localesStatuses: TranslationStatus = await import('../../translation-status.json').then(m => m.default)
|
||||
|
||||
const totalReference = localesStatuses.en.total
|
||||
|
||||
type Tab = 'missing' | 'outdated'
|
||||
|
||||
const hidden = ref(true)
|
||||
const locale = ref()
|
||||
const localeTab = ref<Tab>('missing')
|
||||
const copied = ref(false)
|
||||
|
||||
const currentLocale = computed(() => {
|
||||
if (hidden.value || !locale.value)
|
||||
return undefined
|
||||
|
||||
return localesStatuses as Record<string, any>
|
||||
})
|
||||
|
||||
const localeTitle = computed(() => {
|
||||
if (hidden.value || !locale.value)
|
||||
return undefined
|
||||
|
||||
return localeTab.value === 'missing'
|
||||
? `Missing keys in ${locale.value.file}`
|
||||
: `Outdated keys in ${locale.value.file}`
|
||||
})
|
||||
|
||||
const missingEntries = computed<string[]>(() => {
|
||||
if (hidden.value || !currentLocale.value || localeTab.value !== 'missing')
|
||||
return []
|
||||
|
||||
return localesStatuses[locale.value].missing
|
||||
})
|
||||
|
||||
const outdatedEntries = computed<string[]>(() => {
|
||||
if (hidden.value || !currentLocale.value || localeTab.value !== 'outdated')
|
||||
return []
|
||||
|
||||
return localesStatuses[locale.value]!.outdated
|
||||
})
|
||||
|
||||
const showDetail = (key: string, tab: Tab = 'missing', fromTab = false) => {
|
||||
if (key === locale.value && tab === localeTab.value) {
|
||||
if (fromTab)
|
||||
return
|
||||
|
||||
nextTick().then(() => hidden.value = !hidden.value)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
locale.value = key
|
||||
localeTab.value = tab
|
||||
nextTick().then(() => hidden.value = false)
|
||||
}
|
||||
|
||||
const copyToClipboard = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText([
|
||||
`# ${localeTitle.value}`,
|
||||
(localeTab.value === 'missing' ? missingEntries.value : outdatedEntries.value).join('\n')].join('\n'),
|
||||
)
|
||||
copied.value = true
|
||||
setTimeout(() => copied.value = false, 750)
|
||||
}
|
||||
catch {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<table class="w-full">
|
||||
<caption>
|
||||
<div>You can see the detail (missing and outdated keys) by clicking on the corresponding row.</div>
|
||||
<div>
|
||||
If you want to send a PR, click on <strong>Edit</strong> link on the corresponding translation file, it will open <strong>Codeflow</strong>:
|
||||
<NuxtLink
|
||||
target="_blank"
|
||||
href="https://developer.stackblitz.com/codeflow/working-in-codeflow-ide#making-a-pr-with-codeflow-ide"
|
||||
title="How to make a PR with Codeflow IDE (opens in new window)"
|
||||
>
|
||||
read the following guide
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Language</th>
|
||||
<th title="Keys correctly translated">
|
||||
Translated
|
||||
</th>
|
||||
<th title="Keys missing from source which need translation for the language">
|
||||
Missing
|
||||
</th>
|
||||
<th title="Keys which could be safely removed">
|
||||
Outdated
|
||||
</th>
|
||||
<th>Total</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="({ title, file, translated, missing, outdated, total, isSource }, key) in localesStatuses" :key="key">
|
||||
<tr
|
||||
v-if="totalReference > 0"
|
||||
:class="[{ expandable: !isSource }]"
|
||||
:title="!isSource ? 'Click to show detail' : undefined"
|
||||
@click="!isSource && showDetail(key, 'missing')"
|
||||
>
|
||||
<td :class="[{ expandable: !isSource }]">
|
||||
<div>
|
||||
<ToogleIcon v-if="!isSource" :up="hidden || key !== locale" />
|
||||
{{ title }}
|
||||
</div>
|
||||
</td>
|
||||
<template v-if="isSource">
|
||||
<td colspan="5" class="source-text">
|
||||
<div>
|
||||
{{ total }} keys as source
|
||||
</div>
|
||||
</td>
|
||||
</template>
|
||||
<template v-else>
|
||||
<td>
|
||||
<strong>{{ `${translated?.length ?? 0}` }}</strong> {{ `(${(100 * (translated?.length ?? 0) / totalReference).toFixed(1)}%)` }}
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ `${missing?.length ?? 0}` }}</strong> {{ `(${(100 * (missing?.length ?? 0) / totalReference).toFixed(1)}%)` }}
|
||||
</td>
|
||||
<td>
|
||||
<strong>{{ `${outdated?.length ?? 0}` }}</strong> {{ `(${(100 * (outdated?.length ?? 0) / totalReference).toFixed(1)}%)` }}
|
||||
</td>
|
||||
<td><strong>{{ `${total}` }}</strong></td>
|
||||
<td>
|
||||
<NuxtLink
|
||||
v-if="outdated.length > 0 || missing.length > 0"
|
||||
:href="`https://pr.new/github.com/elk-zone/elk/tree/main/locales/${file}`"
|
||||
target="_blank"
|
||||
class="codeflow"
|
||||
title="Raise a PR with Codeflow (opens in new window)"
|
||||
@click.stop
|
||||
>
|
||||
Edit
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M5 21q-.825 0-1.413-.587Q3 19.825 3 19V5q0-.825.587-1.413Q4.175 3 5 3h7v2H5v14h14v-7h2v7q0 .825-.587 1.413Q19.825 21 19 21Zm4.7-5.3l-1.4-1.4L17.6 5H14V3h7v7h-2V6.4Z" />
|
||||
</svg>
|
||||
</NuxtLink>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
<template v-if="key === locale && !hidden">
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<div class="detail">
|
||||
<header>
|
||||
<h2 class="tabs">
|
||||
<button
|
||||
:class="localeTab === 'missing' ? 'current' : null"
|
||||
@click="showDetail(key, 'missing', true)"
|
||||
>
|
||||
Missing keys
|
||||
</button>
|
||||
<button
|
||||
:class="localeTab === 'outdated' ? 'current' : null"
|
||||
@click="showDetail(key, 'outdated', true)"
|
||||
>
|
||||
Outdated keys
|
||||
</button>
|
||||
</h2>
|
||||
</header>
|
||||
<ul v-if="localeTab === 'missing'">
|
||||
<li v-for="entry in missingEntries" :key="entry">
|
||||
<pre>{{ entry }}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-else>
|
||||
<li v-for="entry in outdatedEntries" :key="entry">
|
||||
<pre>{{ entry }}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
<button @click="copyToClipboard()">
|
||||
<ClipboardIcon :copy="!copied" />
|
||||
Copy to clipboard
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
font-size: 0.9rem;
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding: 0.3rem;
|
||||
background: #eee;
|
||||
border: 1px solid #ccc;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom: none;
|
||||
}
|
||||
caption a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
th:not(:first-of-type),
|
||||
td:not(:first-of-type) {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
tr.expandable td:first-of-type {
|
||||
padding-left: 4px;
|
||||
}
|
||||
tr.expandable, tr.expandable td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a.codeflow,
|
||||
td.expandable div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
column-gap: 4px;
|
||||
}
|
||||
td.expandable > svg {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
th[title] {
|
||||
text-decoration: underline dotted white;
|
||||
}
|
||||
|
||||
.source-text {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
padding: 10px 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.detail {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.detail header {
|
||||
padding: 0 0.3rem;
|
||||
display: flex;
|
||||
background: #eee;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail header h2 button {
|
||||
font-weight: bold;
|
||||
padding: 0.5rem;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.detail header .tabs button.current {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.detail header .tabs + .heading-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 0.4rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail ul {
|
||||
padding: 0.3rem 0.5rem;
|
||||
max-height: 250px;
|
||||
min-height: 250px;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.detail > button {
|
||||
display: flex;
|
||||
/*justify-content: space-between;*/
|
||||
align-items: center;
|
||||
column-gap: 0.3rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.detail header {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.detail header h2 button {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.detail header .tabs button.current {
|
||||
background-color: white;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
table caption {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Add table
Add a link
Reference in a new issue