Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add i18n & Set default langage to french #6

Merged
merged 32 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f3bdc28
Configure Astro i18n
matiboux Dec 21, 2023
20063be
Add i18n function
matiboux Dec 21, 2023
b776205
Add en & fr locales
matiboux Dec 21, 2023
73788c9
Update locales types
matiboux Dec 22, 2023
8a768d8
Fix fr locale type check
matiboux Dec 22, 2023
8485339
Update i18n function
matiboux Dec 22, 2023
542ca32
Use i18n in page
matiboux Dec 22, 2023
33341f3
Add i18nFactory helper function
matiboux Dec 22, 2023
a1d453a
Add more strings in en locale
matiboux Dec 22, 2023
b762400
Add fr translations
matiboux Dec 22, 2023
358d3c1
Updat type for en locale
matiboux Dec 22, 2023
27b7b0e
Move locales
matiboux Dec 22, 2023
af039a3
Return value without expension if type is invalid
matiboux Dec 22, 2023
4c2262b
Fix i18n inferred return type
matiboux Dec 22, 2023
0d9e975
Export i18n config
matiboux Dec 23, 2023
0715b9e
Set trailingSlash to never
matiboux Dec 23, 2023
4aa06bc
Update i18n config type
matiboux Dec 23, 2023
5afcff3
Add defaultLocaleKey
matiboux Dec 23, 2023
8195db7
Add custom getLocaleByPath implementation
matiboux Dec 23, 2023
6fb3f5a
Add getLocaleByUrl function
matiboux Dec 23, 2023
6ef4be4
Update getLocaleByUrl to take strings
matiboux Dec 23, 2023
4a7b4c9
Add getUrlWithoutLocale function
matiboux Dec 23, 2023
d6c2add
Add button to switch locale
matiboux Dec 23, 2023
81878c6
Add flags in locale switch button
matiboux Dec 23, 2023
587ddc6
Refactor i18n config
matiboux Dec 23, 2023
c03416c
Switch default locale to FR
matiboux Dec 23, 2023
a90b10c
Export defaultLocale
matiboux Dec 23, 2023
a77a9c1
Add fallback to default locale in getLocaleByUrl
matiboux Dec 23, 2023
816bcbf
Refactor i18n type
matiboux Dec 23, 2023
02816b7
Add getLocaleUrlList function
matiboux Dec 23, 2023
07f1390
Use getLocaleUrlList in page
matiboux Dec 23, 2023
fe8f40c
Refactor i18n src files
matiboux Dec 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/app/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { defineConfig } from 'astro/config'

import i18n from './config/i18n'

// https://astro.build/config
export default defineConfig({
trailingSlash: 'never',
vite: {
server: {
watch: {
usePolling: true,
},
},
},
i18n: i18n,
})
25 changes: 25 additions & 0 deletions app/app/config/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const i18n =
{
defaultLocale: 'fr',
locales: [
'en',
'fr',
],
routing: {
prefixDefaultLocale: false,
},
} as {
readonly defaultLocale: string,
readonly locales: readonly (
| string
| {
readonly codes: readonly string[],
readonly path: string,
}
)[],
readonly routing: {
readonly prefixDefaultLocale: boolean,
}
}

export default i18n
1 change: 1 addition & 0 deletions app/app/src/assets/icons/flag-en.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/app/src/assets/icons/flag-fr.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions app/app/src/i18n/getLocaleByPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import i18nConfig from '/config/i18n'

function getLocaleByPath(path: string): string | undefined
{
for (const locale of i18nConfig.locales)
{
if (typeof locale !== 'string')
{
if (locale.path === path)
{
const code = locale.codes.at(0)
return code
}
}
else if (locale === path)
{
return locale
}
}

return undefined
}

export default getLocaleByPath
23 changes: 23 additions & 0 deletions app/app/src/i18n/getLocaleByUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import i18nConfig from '/config/i18n'
import getLocaleByPath from './getLocaleByPath'

const defaultLocale = i18nConfig.defaultLocale

function getLocaleByUrl(url: URL | string, fallback?: true): string
function getLocaleByUrl(url: URL | string, fallback: false): string | undefined
function getLocaleByUrl(url: URL | string, fallback: boolean = true): string | undefined
{
const urlParts = typeof url === 'string' ? url.split('/') : url.pathname.split('/')
for (const part of urlParts)
{
const locale = getLocaleByPath(part)
if (locale)
{
return locale
}
}

return fallback ? defaultLocale : undefined
}

export default getLocaleByUrl
26 changes: 26 additions & 0 deletions app/app/src/i18n/getLocaleUrlList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getAbsoluteLocaleUrlList } from 'astro:i18n'

import getLocaleByUrl from './getLocaleByUrl'
import getUrlWithoutLocale from './getUrlWithoutLocale'

function getLocaleUrlList(url: URL | string, excludeLocale?: string): { locale: string, url: string }[]
{
const pathname = typeof url === 'string' ? url : url.pathname
const list =
(getAbsoluteLocaleUrlList(getUrlWithoutLocale(pathname)) as string[])
.map(url =>
({
locale: getLocaleByUrl(url),
url,
})
)

if (excludeLocale !== undefined)
{
return list.filter(({ locale }) => locale !== excludeLocale)
}

return list
}

export default getLocaleUrlList
30 changes: 30 additions & 0 deletions app/app/src/i18n/getUrlWithoutLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import getLocaleByPath from './getLocaleByPath'

function getUrlWithoutLocale(url: URL | string): string
{
const urlPathnames = []
const urlParts = typeof url === 'string' ? url.split('/') : url.pathname.split('/')

let i = 0
while (i < urlParts.length)
{
const locale = getLocaleByPath(urlParts[i])
if (locale)
{
i++
continue
}

urlPathnames.push(urlParts[i])
i++
}
while (i < urlParts.length)
{
urlPathnames.push(urlParts[i])
i++
}

return urlPathnames.join('/')
}

export default getUrlWithoutLocale
38 changes: 38 additions & 0 deletions app/app/src/i18n/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import en from './locales/en'
import fr from './locales/fr'
import I18n from './type'

const locales = { en, fr } as const

function i18n(
locale: keyof typeof locales,
key: keyof I18n,
...args: string[]
)
{
const value = (locales[locale][key] ?? key) as I18n[keyof I18n]

if (typeof value !== 'string')
{
return value
}

return value.replace(/{(\d+)}/g, (match, number) =>
typeof args[number] != 'undefined'
? args[number]
: match
)
}

type Tail<T extends any[]> = ((...args: T) => any) extends (arg: any, ...tail: infer U) => any ? U : never

function i18nFactory(locale: Parameters<typeof i18n>[0])
{
return (...args: Tail<Parameters<typeof i18n>>) => i18n(locale, ...args)
}

export default i18n

export {
i18nFactory,
}
18 changes: 18 additions & 0 deletions app/app/src/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import getLocaleByPath from './getLocaleByPath'
import getLocaleByUrl from './getLocaleByUrl'
import getLocaleUrlList from './getLocaleUrlList'
import getUrlWithoutLocale from './getUrlWithoutLocale'
import i18n, { i18nFactory } from './i18n'
import { defaultLocale, defaultLocaleKey } from './type'

export default i18n

export {
defaultLocale,
defaultLocaleKey,
i18nFactory,
getLocaleByPath,
getLocaleByUrl,
getUrlWithoutLocale,
getLocaleUrlList,
}
23 changes: 23 additions & 0 deletions app/app/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const defaultLocale =
[
'What is {0}?',
'Visit our website',
'Join our Discord',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus cursus magna at libero tristique, faucibus ullamcorper orci volutpat. Integer aliquet nulla ut ante porttitor faucibus. Sed faucibus consectetur pellentesque. Proin malesuada purus vel posuere sollicitudin. Donec vel efficitur magna. Nullam vel convallis tortor. Sed eu scelerisque purus, vitae iaculis felis.',
'Donec suscipit nisi et tincidunt eleifend. Mauris sed massa et magna interdum semper a quis neque. Etiam posuere volutpat mauris, consectetur cursus justo viverra vel. Nunc hendrerit sapien nec augue porta, sed congue est ornare.',
] as const

type Keys = typeof defaultLocale[number]
type Type = { [key in Keys]: key }

// Default locale uses the key as the value
const locale = defaultLocale
.reduce((acc, key) =>
{
acc[key] = key
return acc
},
{},
) as Type

export default locale as Readonly<Type>
11 changes: 11 additions & 0 deletions app/app/src/i18n/locales/fr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import I18n from '../type'

const locale =
{
'What is {0}?': 'Qu\'est-ce que {0} ?',
'Visit our website': 'Visite notre site web',
'Join our Discord': 'Rejoins notre Discord',
} as const

// Static type check
export default locale satisfies Partial<I18n>
16 changes: 16 additions & 0 deletions app/app/src/i18n/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import i18nConfig from '/config/i18n'

import defaultLocaleData from './locales/en'

const defaultLocale = i18nConfig.defaultLocale

const defaultLocaleKey = 'en' as const

type I18n = Readonly<Record<keyof typeof defaultLocaleData, string>>

export default I18n

export {
defaultLocale,
defaultLocaleKey,
}
36 changes: 30 additions & 6 deletions app/app/src/pages/404.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import { Image } from 'astro:assets'

import Layout from '~/layouts/Layout.astro'

import { i18nFactory, getLocaleUrlList } from '~/i18n'
const _ = i18nFactory(Astro.currentLocale)

import background from '~/assets/background.png'
import closeSvg from '~/assets/icons/close.svg?raw'
import infoSvg from '~/assets/icons/info.svg?raw'
import bookmarkSvg from '~/assets/icons/bookmark.svg?raw'
import discordSvg from '~/assets/icons/discord.svg?raw'
import flagFRSvg from '~/assets/icons/flag-fr.svg?raw'
import flagENSvg from '~/assets/icons/flag-en.svg?raw'

const title = '2GETHER App'
---
Expand All @@ -25,12 +30,12 @@ const title = '2GETHER App'
<!-- Close -->
</div>
<div class="modal-content">
<h2>What is { title }?</h2>
<h2>{_('What is {0}?', title)}</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus cursus magna at libero tristique, faucibus ullamcorper orci volutpat. Integer aliquet nulla ut ante porttitor faucibus. Sed faucibus consectetur pellentesque. Proin malesuada purus vel posuere sollicitudin. Donec vel efficitur magna. Nullam vel convallis tortor. Sed eu scelerisque purus, vitae iaculis felis.
{_('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus cursus magna at libero tristique, faucibus ullamcorper orci volutpat. Integer aliquet nulla ut ante porttitor faucibus. Sed faucibus consectetur pellentesque. Proin malesuada purus vel posuere sollicitudin. Donec vel efficitur magna. Nullam vel convallis tortor. Sed eu scelerisque purus, vitae iaculis felis.')}
</p>
<p>
Donec suscipit nisi et tincidunt eleifend. Mauris sed massa et magna interdum semper a quis neque. Etiam posuere volutpat mauris, consectetur cursus justo viverra vel. Nunc hendrerit sapien nec augue porta, sed congue est ornare.
{_('Donec suscipit nisi et tincidunt eleifend. Mauris sed massa et magna interdum semper a quis neque. Etiam posuere volutpat mauris, consectetur cursus justo viverra vel. Nunc hendrerit sapien nec augue porta, sed congue est ornare.')}
</p>
</div>
</div>
Expand All @@ -50,16 +55,35 @@ const title = '2GETHER App'
<div class="buttons">
<a href="#info" class="button" id="info-button">
<Fragment set:html={ infoSvg } />
What is { title }?
{_('What is {0}?', title)}
</a>
<a href="https://2gether-asso.fr" class="button">
<Fragment set:html={ bookmarkSvg } />
Visit our website
{_('Visit our website')}
</a>
<a href="https://discord.2gether-asso.fr" class="button">
<Fragment set:html={ discordSvg } />
Join our Discord
{_('Join our Discord')}
</a>
{getLocaleUrlList(Astro.url, Astro.currentLocale).map(({ locale, url }) =>
{
const flagSvg =
locale === 'fr' ? flagFRSvg :
locale === 'en' ? flagENSvg :
null

return (
<a href={url} class="button">
{flagSvg
&& (
<Fragment set:html={ flagSvg } />
)
|| locale
}
</a>
)
})
}
</div>

</div>
Expand Down
2 changes: 2 additions & 0 deletions app/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
{
"extends": "astro/tsconfigs/strictest",
"compilerOptions": {
"target": "es2022",
"baseUrl": ".",
"paths": {
"/*": [ "*" ],
"~/*": [ "src/*" ],
}
}
Expand Down