feat(i18n): improved i18n contribution documentation (#292)

This commit is contained in:
Corentin THOMASSET 2024-10-17 15:50:04 +02:00 committed by GitHub
parent 192bab5ea8
commit 6fc79b6df5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 65 additions and 68 deletions

View File

@ -37,17 +37,7 @@ We use **[Conventional Commits](https://www.conventionalcommits.org/)** to keep
## i18n
### Adding a New Language
To contribute to the translation of the app, you can add a new language file in the [`packages/app-client/src/locales`](./packages/app-client/src/locales) directory. The file should be named according to the language code (e.g., `fr.json` for French). You can then add the new language to the `locales` array in the [`packages/app-client/src/modules/i18n/i18n.constants.ts`](./packages/app-client/src/modules/i18n/i18n.constants.ts) file.
The reference language file is [`en.json`](./packages/app-client/src/locales/en.json) it contains all the keys used in the app. You can use this file as a reference to create the new language file.
You can list the missing keys for each language by running the following command in the `app-client` package:
```bash
pnpm script:get-missing-i18n-keys
```
Information about the translation process can be found in the [locales README](./packages/app-client/src/locales/README.md).
### Updating an Existing Language

View File

@ -0,0 +1,31 @@
# Contributing to Translations
We welcome contributions to improve and expand the app's internationalization (i18n) support. Below are the guidelines for adding a new language or updating an existing translation.
## Adding a New Language
1. **Create a Language File**: To add a new language, create a JSON file named with the appropriate [ISO language code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g., `fr.json` for French) in the [`packages/app-client/src/locales`](./) directory.
2. **Use the Reference File**: Refer to the [`en.json`](./en.json) file, which contains all keys used in the app. Use it as a base to ensure consistency when creating your new language file. And act as a fallback if a key is missing in the new language file.
3. **Update the Locale List**: After adding the new language file, include the language code in the `locales` array found in the [`locales.ts`](./locales.ts) file.
4. **[Optional] Check for Missing Keys**: You can verify that all translation keys are included by running the following command in the `app-client` package:
```bash
pnpm script:get-missing-i18n-keys
```
5. **Submit a Pull Request**: Once you've added the file and updated `locales.ts`, create a pull request (PR) with your changes. Ensure that your PR is clearly titled with the language being added (e.g., "Add French translations").
## Updating an Existing Language
To improve or correct an existing language:
1. **Edit the Language File**: Make updates directly to the relevant JSON file in the [`packages/app-client/src/locales`](./) directory.
2. **Submit a Pull Request**: After making your changes, submit a pull request. Clearly describe the updates in the PR.
---
Thank you for helping us make the app accessible to a broader audience!

View File

@ -1,10 +1,18 @@
import { access, constants } from 'node:fs/promises';
import { flatten } from '@solid-primitives/i18n';
import { chain, difference, get, keys } from 'lodash-es';
import { describe, expect, test } from 'vitest';
import defaultLocale from './en.json';
import { locales } from './locales';
function fileExists(relativePath: string) {
const path = new URL(relativePath, import.meta.url).pathname;
return access(path, constants.F_OK).then(() => true).catch(() => false);
}
const localesFiles = import.meta.glob('./*.json', { eager: true });
const locales = chain(localesFiles)
const localesKeys = chain(localesFiles)
.map((value, key) => ({
key: key.replace('./', '').replace('.json', ''),
value: get(value, 'default') as any,
@ -16,11 +24,17 @@ const flattenedDefault = flatten(defaultLocale);
describe('locales', () => {
test('locales should not have extra keys compared to default locale', () => {
locales.forEach(({ key, value }) => {
localesKeys.forEach(({ key, value }) => {
const flattened = flatten(value);
const extraKeys = difference(keys(flattened), keys(flattenedDefault));
expect(extraKeys).to.eql([], `Extra keys found in ${key}.json`);
});
});
test('ensure all defined locales have a file in the locales folder', async () => {
for (const { key } of locales) {
expect(await fileExists(`./${key}.json`)).to.eql(true, `Missing file for locale ${key}`);
}
});
});

View File

@ -0,0 +1,13 @@
// Order of locales matters, keep it sorted by the native language name
export const locales = [
{ key: 'ar', name: 'العربية' },
{ key: 'zh-CN', name: '简体中文' },
{ key: 'de', name: 'Deutsch' },
{ key: 'en', name: 'English' },
{ key: 'es', name: 'Español' },
{ key: 'fr', name: 'Français' },
{ key: 'it', name: 'Italiano' },
{ key: 'pt', name: 'Português' },
{ key: 'ru', name: 'Русский' },
{ key: 'vi', name: 'Tiếng Việt' },
] as const;

View File

@ -1,52 +0,0 @@
export const locales = [
{
key: 'ar',
file: 'ar',
name: 'العربية',
},
{
key: 'en',
file: 'en',
name: 'English',
},
{
key: 'de',
file: 'de',
name: 'Deutsch',
},
{
key: 'fr',
file: 'fr',
name: 'Français',
},
{
key: 'it',
file: 'it',
name: 'Italiano',
},
{
key: 'es',
file: 'es',
name: 'Español',
},
{
key: 'pt',
file: 'pt',
name: 'Português',
},
{
key: 'ru',
file: 'ru',
name: 'Русский',
},
{
key: 'vi',
file: 'vi',
name: 'Tiếng Việt',
},
{
key: 'zh-CN',
file: 'zh-CN',
name: '简体中文',
},
] as const;

View File

@ -1,10 +1,10 @@
import type { ParentComponent } from 'solid-js';
import { locales } from '@/locales/locales';
import * as i18n from '@solid-primitives/i18n';
import { makePersisted } from '@solid-primitives/storage';
import { merge } from 'lodash-es';
import { createContext, createResource, createSignal, Show, useContext } from 'solid-js';
import defaultDict from '../../locales/en.json';
import { locales } from './i18n.constants';
export {
useI18n,

View File

@ -50,6 +50,7 @@ const LanguageSwitcher: Component = () => {
<DropdownMenuItem as="a" class="flex items-center gap-2 cursor-pointer" target="_blank" rel="noopener noreferrer" href="https://github.com/CorentinTh/enclosed/tree/main/packages/app-client/src/locales">
{t('navbar.settings.contribute-to-i18n')}
<div class="i-tabler-external-link text-lg text-muted-foreground"></div>
</DropdownMenuItem>
</>
);

View File

@ -2,10 +2,10 @@ import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { flatten } from '@solid-primitives/i18n';
import { difference, keys, omitBy } from 'lodash-es';
import { locales } from '../modules/i18n/i18n.constants';
import { locales } from '../locales/locales';
const localesContent = await Promise.all(locales.map(async (locale) => {
const filePath = join('src', 'locales', `${locale.file}.json`);
const filePath = join('src', 'locales', `${locale.key}.json`);
const fileContent = await readFile(filePath, 'utf-8');
return {