Skip to content

Commit

Permalink
feat(i18n): dynamic locale and OALocaleSelect component (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
enzonotario authored Dec 15, 2024
1 parent 8e24335 commit 2eb3794
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 74 deletions.
5 changes: 5 additions & 0 deletions dev/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ export default defineConfigWithTheme({
],
},
],
nav: [
{
component: 'OALocaleSelect',
},
],
},
vite: {
resolve: {
Expand Down
10 changes: 9 additions & 1 deletion docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ export default defineConfigWithTheme({
title: 'VitePress OpenAPI',
description: 'Generate VitePress API Documentation from OpenAPI specs.',
themeConfig: {
nav: [],
nav: [
{
component: 'OALocaleSelect',
},
],

sidebar: [
{
Expand Down Expand Up @@ -91,6 +95,10 @@ export default defineConfigWithTheme({
text: 'Operation tags slot',
link: '/customizations/operation-tags-slot',
},
{
text: 'i18n',
link: '/customizations/i18n',
},
],
},
],
Expand Down
5 changes: 5 additions & 0 deletions docs/composables/useTheme.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ export default {
'operation.badgePrefix.operationId': 'ID de operación',
},
},
availableLocales: [
{ code: 'en', label: 'English' },
{ code: 'es', label: 'Español' },
{ code: 'pt-BR', label: 'Português (Brasil)' },
],
},
// Set spec configuration.
spec: {
Expand Down
78 changes: 78 additions & 0 deletions docs/customizations/i18n.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
aside: false
outline: false
---

# Internationalization (i18n)

You can use the `useTheme` composable to configure internationalization in your `.vitepress/theme/index.js` file, or in any `.md` page/file. All values are optional.

```vue
<script setup lang="ts">
import { useTheme, locales } from 'vitepress-openapi'
useTheme({
i18n: {
locale: 'en', // en | es | pt-BR | string
fallbackLocale: 'en', // en | es | pt-BR | string
messages: {
en: {
...locales.en,
'custom i18n key': 'Custom i18n value',
},
es: {
...locales.es,
'custom i18n key': 'Valor personalizado i18n',
},
'pt-BR': {
...locales['pt-BR'],
'custom i18n key': 'Valor personalizado i18n',
},
availableLocales: [
{ code: 'en', label: 'English' },
{ code: 'es', label: 'Español' },
{ code: 'pt-BR', label: 'Português (Brasil)' },
],
},
})
</script>
<!-- OASpec component or OAOperation component -->
```

If you're using the built-in VitePress i18n features, you can configure the locale as follows:

```vue
<script setup lang="ts">
import { useTheme } from 'vitepress-openapi'
import { useData } from 'vitepress'
const { lang } = useData()
useTheme({
i18n: {
locale: lang.value,
},
})
</script>
<!-- OASpec component or OAOperation component -->
```

You can see a [Live Example](https://vitepress-openapi-i18n.vercel.app/) and the [Source Code](https://github.com/enzonotario/vitepress-openapi-i18n) for more details.

## `OALocaleSelect` component

You can use the `OALocaleSelect` component to render a select with the available locales. Keep in mind that this will change only `vitepress-openapi` components, not the entire VitePress site. For example, you can add it to Navbar in your `.vitepress/config.js`:

```js
export default defineConfigWithTheme({
themeConfig: {
nav: [
{
component: 'OALocaleSelect',
},
],
},
})
```
63 changes: 14 additions & 49 deletions docs/public/openapi-response-statuses.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "200 OK"
}
}
"type": "string",
"example": "200 OK"
}
}
}
Expand All @@ -50,13 +45,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "201 Created"
}
}
"type": "string",
"example": "201 Created"
}
}
}
Expand All @@ -75,13 +65,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "400 Bad Request"
}
}
"type": "string",
"example": "400 Bad Request"
}
}
}
Expand All @@ -100,13 +85,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "401 Unauthorized"
}
}
"type": "string",
"example": "401 Unauthorized"
}
}
}
Expand All @@ -125,13 +105,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "403 Forbidden"
}
}
"type": "string",
"example": "403 Forbidden"
}
}
}
Expand All @@ -150,13 +125,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "404 Not Found"
}
}
"type": "string",
"example": "404 Not Found"
}
}
}
Expand All @@ -175,13 +145,8 @@
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"example": "500 Internal Server Error"
}
}
"type": "string",
"example": "500 Internal Server Error"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vitepress-openapi",
"type": "module",
"version": "0.0.3-alpha.52",
"version": "0.0.3-alpha.53",
"packageManager": "[email protected]",
"homepage": "https://vitepress-openapi.vercel.app/",
"repository": {
Expand Down
42 changes: 42 additions & 0 deletions src/components/Common/OALocaleSelect.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<script setup>
import { computed } from 'vue'
import { useTheme } from '../../composables/useTheme'
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger } from '../ui/select'
const themeConfig = useTheme()
const i18nConfig = themeConfig.getI18nConfig()
const defaultLocale = computed(() => i18nConfig.locale?.value ?? 'en')
function onLocaleChange(locale) {
themeConfig.setI18nConfig({
locale,
})
}
</script>
<template>
<div class="self-center">
<Select
:default-value="defaultLocale"
:model-value="i18nConfig.locale"
@update:model-value="onLocaleChange"
>
<SelectTrigger>
<span class="vpi-languages option-icon" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem
v-for="locale in i18nConfig.availableLocales"
:key="locale.code"
:value="locale.code"
>
{{ locale.label }}
</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</div>
</template>
2 changes: 2 additions & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import OASchemaTabs from './Schema/OASchemaTabs.vue'
import OASecurity from './Security/OASecurity.vue'
import OAInfo from './Common/OAInfo.vue'
import OAServers from './Common/OAServers.vue'
import OALocaleSelect from './Common/OALocaleSelect.vue'

export {
OASpec,
Expand All @@ -46,4 +47,5 @@ export {
OASecurity,
OAInfo,
OAServers,
OALocaleSelect,
}
1 change: 1 addition & 0 deletions src/components/ui/select/SelectContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const props = withDefaults(
defineProps<SelectContentProps & { class?: HTMLAttributes['class'] }>(),
{
position: 'popper',
bodyLock: false,
},
)
const emits = defineEmits<SelectContentEmits>()
Expand Down
2 changes: 1 addition & 1 deletion src/composables/useSidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export function useSidebar({
linkPrefix = DEFAULT_CONFIG.linkPrefix,
tagLinkPrefix = DEFAULT_CONFIG.tagLinkPrefix,
defaultTag = DEFAULT_CONFIG.defaultTag,
methodAliases,
methodAliases = {},
}: SidebarConfig = {}) {
useTheme({
spec: {
Expand Down
25 changes: 25 additions & 0 deletions src/composables/useTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ export type Languages = 'es' | 'en' | 'pt-BR' | string

export type Messages = Record<Languages, Record<string, string>>

export interface AvailableLocale {
code: string
label: string
}

export interface I18nConfig {
locale: Ref<Languages>
fallbackLocale: Ref<Languages>
messages: Messages
availableLocales?: AvailableLocale[]
}

export interface SpecConfig {
Expand Down Expand Up @@ -259,6 +265,20 @@ const themeConfig: UseThemeConfig = {
locale: ref<Languages>('en'),
fallbackLocale: ref<Languages>('en'),
messages: locales,
availableLocales: [
{
code: 'en',
label: 'English',
},
{
code: 'es',
label: 'Español',
},
{
code: 'pt-BR',
label: 'Português (Brasil)',
},
],
},
spec: {
groupByTags: ref(true),
Expand Down Expand Up @@ -598,6 +618,11 @@ export function useTheme(initialConfig: Partial<UseThemeConfigUnref> = {}) {
// @ts-expect-error: This is a valid expression.
themeConfig.i18n.messages = config.messages
}

if (config.availableLocales) {
// @ts-expect-error: This is a valid expression.
themeConfig.i18n.availableLocales = config.availableLocales
}
}

function getSpecConfig() {
Expand Down
Loading

0 comments on commit 2eb3794

Please sign in to comment.