-
Notifications
You must be signed in to change notification settings - Fork 205
Adding Internationalization (i18n) support to Appwrite console. #451
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import i18next from 'i18next'; | ||
| import type { i18n } from 'i18next'; | ||
| import { writable, type Readable, type Writable } from 'svelte/store'; | ||
| import resourcesToBackend from 'i18next-resources-to-backend'; | ||
|
|
||
| export const supportedLanguages = ['de', 'en']; | ||
|
|
||
| interface TranslationService { | ||
| i18n: Readable<i18n>; | ||
| } | ||
|
|
||
| export const isLoading = writable(true); | ||
|
|
||
| export class I18NextTranslationStore implements TranslationService { | ||
| public i18n: Readable<i18n>; | ||
| public isLoading: Writable<boolean>; | ||
|
|
||
| constructor(i18n: i18n) { | ||
| this.i18n = this.createInstance(i18n); | ||
| this.isLoading = this.createLoadingInstance(i18n); | ||
| } | ||
|
|
||
| private createInstance(i18n: i18n): Writable<i18n> { | ||
| const i18nWritable = writable(i18n); | ||
|
|
||
| i18n.on('initialized', () => { | ||
| i18nWritable.set(i18n); | ||
| }); | ||
| i18n.on('loaded', () => { | ||
| i18nWritable.set(i18n); | ||
| }); | ||
| i18n.on('added', () => i18nWritable.set(i18n)); | ||
| i18n.on('languageChanged', () => { | ||
| i18nWritable.set(i18n); | ||
| }); | ||
| return i18nWritable; | ||
| } | ||
|
|
||
| private createLoadingInstance(i18n: i18n): Writable<boolean> { | ||
| // if loaded resources are empty || {}, set loading to true | ||
| i18n.on('loaded', (resources) => { | ||
| Object.keys(resources).length !== 0 && isLoading.set(false); | ||
| }); | ||
|
|
||
| // if resources failed loading, set loading to true | ||
| i18n.on('failedLoading', () => { | ||
| isLoading.set(true); | ||
| }); | ||
|
|
||
| return isLoading; | ||
| } | ||
| } | ||
|
|
||
| export const createI18nStore = () => { | ||
| i18next | ||
| .use( | ||
| resourcesToBackend((language, _namespace, callback) => { | ||
| import(`$lib/translations/${language}.json`) | ||
| .then((resources) => { | ||
| callback(null, resources); | ||
| }) | ||
| .catch((error) => { | ||
| callback(error, null); | ||
| }); | ||
| }) | ||
| ) | ||
| .init({ | ||
| // lng: 'de', | ||
| lng: window.navigator.language, | ||
| fallbackLng: 'en' | ||
| }); | ||
|
|
||
| const i18nStore = new I18NextTranslationStore(i18next); | ||
|
|
||
| return i18nStore.i18n; | ||
| }; | ||
|
|
||
| export const _ = createI18nStore(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "globals": { | ||
| "placeholders": { | ||
| "email": "E-Mail", | ||
| "password": "Passwort", | ||
| "enter_password": "Passwort eingeben", | ||
| "confirm_password": "Passwort bestätigen", | ||
| "your_name": "Ihr Name", | ||
| "your_email": "Ihre E-Mail-Adresse", | ||
| "your_password": "Ihr Passwort", | ||
| "your_code": "dein Code" | ||
| }, | ||
| "update": "aktualisieren", | ||
| "recover": "genesen", | ||
| "accept": "akzeptieren", | ||
| "cancel": "stornieren" | ||
| }, | ||
| "login": { | ||
| "title": "anmelden", | ||
| "field_inputs": { | ||
| "email": "E-Mail", | ||
| "password": "Passwort" | ||
| } | ||
| }, | ||
| "sign_up": { | ||
| "title": "Anmeldung", | ||
| "field_inputs": { | ||
| "name": "Name", | ||
| "email": "E-Mail", | ||
| "password": "Passwort", | ||
| "code": "Code" | ||
| }, | ||
| "already_registered": "Sie haben bereits ein Konto?" | ||
| }, | ||
| "password_recovery": { | ||
| "title": "Passwort-Wiederherstellung", | ||
| "forgot_password": "Passwort vergessen", | ||
| "field_inputs": { | ||
| "email": "E-Mail", | ||
| "new_password": "Neues Kennwort", | ||
| "cnf_password": "Bestätige das Passwort" | ||
| } | ||
| }, | ||
| "accept_invite": { | ||
| "title": "Einladung annehmen", | ||
| "invalid_invite": "Ungültige Einladung", | ||
| "invite": "einladen", | ||
| "sign_up": "Melden Sie sich bei Appwrite an", | ||
| "warning_alert": { | ||
| "title": "Der Einladungslink ist ungültig", | ||
| "sub_title": "Bitten Sie den Projektinhaber, Ihnen eine neue Einladung zu senden." | ||
| }, | ||
| "success_alert": { | ||
| "title": "Sie wurden eingeladen, an einem Teamprojekt auf Appwrite teilzunehmen" | ||
| } | ||
| }, | ||
| "privacy_and_policy": { | ||
| "main_text": "Indem Sie die Einladung annehmen, erklären Sie sich damit einverstanden", | ||
| "terms_and_condition": "Geschäftsbedingungen", | ||
| "and": "Und", | ||
| "privacy_policy": "Datenschutz und Richtlinien" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "globals": { | ||
| "placeholders": { | ||
| "email": "Email", | ||
| "password": "Password", | ||
| "enter_password": "Enter Password", | ||
| "confirm_password": "Confirm Password", | ||
| "your_name": "Your name", | ||
| "your_email": "Your email", | ||
| "your_password": "Your password", | ||
| "your_code": "Your code" | ||
| }, | ||
| "update": "Update", | ||
| "recover": "Recover", | ||
| "accept": "Accept", | ||
| "cancel": "Cancel" | ||
| }, | ||
| "login": { | ||
| "title": "Sign in", | ||
| "field_inputs": { | ||
| "email": "Email", | ||
| "password": "Password" | ||
| } | ||
| }, | ||
| "sign_up": { | ||
| "title": "Sign up", | ||
| "field_inputs": { | ||
| "name": "Name", | ||
| "email": "Email", | ||
| "password": "Password", | ||
| "code": "code" | ||
| }, | ||
| "already_registered": "Already got an account?" | ||
| }, | ||
| "password_recovery": { | ||
| "title": "Password Recovery", | ||
| "forgot_password": "Forgot Password", | ||
| "field_inputs": { | ||
| "email": "Email", | ||
| "new_password": "New password", | ||
| "cnf_password": "Confirm password" | ||
| } | ||
| }, | ||
| "accept_invite": { | ||
| "title": "Accept invite", | ||
| "invalid_invite": "Invalid invite", | ||
| "invite": "Invite", | ||
| "sign_up": "Sign up to Appwrite", | ||
| "warning_alert": { | ||
| "title": "The invite link is not valid", | ||
| "sub_title": "Please ask the project owner to send you a new invite." | ||
| }, | ||
| "success_alert": { | ||
| "title": "You have been invited to join a team project on Appwrite" | ||
| } | ||
| }, | ||
| "privacy_and_policy": { | ||
| "main_text": "By accepting the invitation, you agree to the", | ||
| "terms_and_condition": "Terms and Conditions", | ||
| "and": "and", | ||
| "privacy_policy": "Privacy and Policy" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -107,6 +107,11 @@ | |
| } | ||
| } | ||
| } | ||
| // $: { | ||
| // const getUserLanguage = window.navigator.language; | ||
| // console.log(getUserLanguage); | ||
| // } | ||
|
Comment on lines
+111
to
+114
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be removed
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, how is language initialized and changed in this implementation? |
||
| </script> | ||
|
|
||
| <Notifications /> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ | |
| import { onMount } from 'svelte'; | ||
| import { Submit, trackEvent, trackError } from '$lib/actions/analytics'; | ||
| import { Alert } from '$lib/components'; | ||
| import { _ } from '$lib/i18n'; | ||
| let teamId: string, membershipId: string, userId: string, secret: string; | ||
| let terms = false; | ||
|
|
@@ -40,28 +41,29 @@ | |
| </script> | ||
|
|
||
| <svelte:head> | ||
| <title>Accept invite - Appwrite</title> | ||
| <title>{$_.t('accept_invite.title')} - Appwrite</title> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot, is this typesafe? E.g. if I pass in a string that doesn't exist, will the IDE (not the server) return an error?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TGlide I think now we are working with pull request #459, so we have stopped working on this PR we can close this PR, but firstly we need to discuss it with @TorstenDittmann. |
||
| </svelte:head> | ||
|
|
||
| <Unauthenticated> | ||
| <svelte:fragment slot="title"> | ||
| {#if !userId || !secret || !membershipId || !teamId} | ||
| Invalid invite | ||
| {$_.t('accept_invite.invalid_invite')} | ||
| {:else} | ||
| Invite | ||
| {$_.t('accept_invite.invite')} | ||
| {/if} | ||
| </svelte:fragment> | ||
| <svelte:fragment> | ||
| {#if !userId || !secret || !membershipId || !teamId} | ||
| <Alert type="warning"> | ||
| <svelte:fragment slot="title">The invite link is not valid</svelte:fragment> | ||
| Please ask the project owner to send you a new invite. | ||
| <svelte:fragment slot="title" | ||
| >{$_.t('accept_invite.warning_alert.title')}</svelte:fragment> | ||
| {$_.t('accept_invite.warning_alert.title')} | ||
| </Alert> | ||
| <div class="u-flex u-main-end u-margin-block-start-40"> | ||
| <Button href={`${base}/register`}>Sign up to Appwrite</Button> | ||
| <Button href={`${base}/register`}>{$_.t('accept_invite.sign_up')}</Button> | ||
| </div> | ||
| {:else} | ||
| <p class="text">You have been invited to join a team project on Appwrite</p> | ||
| <p class="text">{$_.t('accept_invite.success_alert.title')}</p> | ||
| <Form onSubmit={acceptInvite}> | ||
| <FormList> | ||
| <InputChoice | ||
|
|
@@ -70,23 +72,25 @@ | |
| id="terms" | ||
| label="terms" | ||
| showLabel={false}> | ||
| By accepting the invitation, you agree to the <a | ||
| {$_.t('privacy_and_policy.main_text')} | ||
| <a | ||
| class="link" | ||
| href="https://appwrite.io/policy/terms" | ||
| target="_blank" | ||
| rel="noopener noreferrer">Terms and Conditions</a> | ||
| and | ||
| rel="noopener noreferrer" | ||
| >{$_.t('privacy_and_policy.terms_and_condition')}</a> | ||
| {$_.t('privacy_and_policy.and')} | ||
| <a | ||
| class="link" | ||
| href="https://appwrite.io/policy/privacy" | ||
| target="_blank" | ||
| rel="noopener noreferrer"> | ||
| Privacy Policy</a | ||
| {$_.t('privacy_and_policy.privacy_policy')}</a | ||
| >.</InputChoice> | ||
|
|
||
| <div class="u-flex u-main-end u-gap-12"> | ||
| <Button secondary href={`${base}/login`}>Cancel</Button> | ||
| <Button submit>Accept</Button> | ||
| <Button secondary href={`${base}/login`}>{$_.t('globals.cancel')}</Button> | ||
| <Button submit>{$_.t('globals.accept')}</Button> | ||
| </div> | ||
| </FormList> | ||
| </Form> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure, I was testing different libraries so I did the following changes. I'll make it back to the original.