diff --git a/.vscode/i18n-ally-custom-framework.yml b/.vscode/i18n-ally-custom-framework.yml
new file mode 100644
index 000000000..f69203c30
--- /dev/null
+++ b/.vscode/i18n-ally-custom-framework.yml
@@ -0,0 +1,12 @@
+languageIds:
+ - typescript
+usageMatchRegex:
+ - "(?:\\$\\{)?translate\\(['\"`]({key})['\"`]"
+scopeRangeRegex: "useTranslation\\(\\s*\\[?\\s*['\"`](.*?)['\"`]"
+derivedKeyRules:
+ - '{key}_zero'
+ - '{key}_one'
+ - '{key}_two'
+ - '{key}_few'
+ - '{key}_many'
+ - '{key}_other'
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 511541411..8d0b5dbc4 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,5 +4,12 @@
"editor.codeActionsOnSave": {
"source.organizeImports": "never"
},
- "eslint.useFlatConfig": true
+ "eslint.useFlatConfig": true,
+ "i18n-ally.sourceLanguage": "en-US",
+ "i18n-ally.displayLanguage": "en-US",
+ "i18n-ally.localesPaths": "packages/core/src/locales",
+ "i18n-ally.sortKeys": true,
+ "i18n-ally.namespace": true,
+ "i18n-ally.enabledFrameworks": ["custom", "i18next"],
+ "i18n-ally.keystyle": "nested"
}
diff --git a/README.md b/README.md
index 14de9d285..dc6b8eaa7 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,10 @@ _React Bindings_
- [Features](#features)
- [Usage](#usage)
- [Using the Discord font](#using-the-discord-font)
+ - [Internationalization](#internationalization)
+ - [Initialization](#initialization)
+ - [Setting the language manually](#setting-the-language-manually)
+ - [Supported languages](#supported-languages)
- [Integrations](#integrations)
- [Angular](#angular)
- [Important Notes](#important-notes)
@@ -197,6 +201,82 @@ do so by including the CSS below:
}
```
+### Internationalization
+
+This package uses [i18next](https://www.i18next.com/) for internationalization.
+We load
+[i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector)
+plugin to attempt to detect the user's browser language, or you can set the
+language yourself as seen at
+[Setting the language manually](#setting-the-language-manually).
+
+#### Initialization
+
+i18next will be initialized by importing any component internally.
+Alternatively, if you want to initialize it yourself (i.e. in your application
+entrypoint) you can do so with the following code:
+
+```ts
+import '@skyra/discord-components';
+```
+
+or if you only want to load i18n and not any of the component side effects:
+
+```ts
+import '@skyra/discord-components/i18n';
+```
+
+#### Setting the language manually
+
+We expose the function `setI18nLanguage` which can be used to manually set the
+language of i18next. You can use one of the following:
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components';
+```
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components/i18n/utils';
+```
+
+#### Supported languages
+
+The list of languages supported are matched to the list of languages the Discord
+client supports. The list is as follows:
+
+- `bg`: Bulgarian
+- `cs`: Czech
+- `da`: Danish
+- `de`: German
+- `el`: Greek
+- `en-GB`: English (British)
+- `en-US`: English (American)
+- `es-419`: Spanish (Latin America)
+- `es-ES`: Spanish (Spain)
+- `fi`: Finnish
+- `fr`: French
+- `hi`: Hindi
+- `hr`: Croatian
+- `hu`: Hungarian
+- `id`: Indonesian
+- `it`: Italian
+- `ja`: Japanese
+- `ko`: Korean
+- `lt`: Lithuanian
+- `nl`: Dutch
+- `no`: Norwegian
+- `pl`: Polish
+- `pt-BR`: Portuguese (Brazil)
+- `ro`: Romanian
+- `ru`: Russian
+- `sv-SE`: Swedish
+- `th`: Thai
+- `tr`: Turkish
+- `uk`: Ukrainian
+- `vi`: Vietnamese
+- `zh-CN`: Chinese (Simplified)
+- `zh-TW`: Chinese (Traditional)
+
### Integrations
#### Angular
diff --git a/assets/readme-templates/CORE_USAGE.md b/assets/readme-templates/CORE_USAGE.md
index b89b6b4df..1f269043d 100644
--- a/assets/readme-templates/CORE_USAGE.md
+++ b/assets/readme-templates/CORE_USAGE.md
@@ -61,6 +61,82 @@ do so by including the CSS below:
}
```
+### Internationalization
+
+This package uses [i18next](https://www.i18next.com/) for internationalization.
+We load
+[i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector)
+plugin to attempt to detect the user's browser language, or you can set the
+language yourself as seen at
+[Setting the language manually](#setting-the-language-manually).
+
+#### Initialization
+
+i18next will be initialized by importing any component internally.
+Alternatively, if you want to initialize it yourself (i.e. in your application
+entrypoint) you can do so with the following code:
+
+```ts
+import '@skyra/discord-components';
+```
+
+or if you only want to load i18n and not any of the component side effects:
+
+```ts
+import '@skyra/discord-components/i18n';
+```
+
+#### Setting the language manually
+
+We expose the function `setI18nLanguage` which can be used to manually set the
+language of i18next. You can use one of the following:
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components';
+```
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components/i18n/utils';
+```
+
+#### Supported languages
+
+The list of languages supported are matched to the list of languages the Discord
+client supports. The list is as follows:
+
+- `bg`: Bulgarian
+- `cs`: Czech
+- `da`: Danish
+- `de`: German
+- `el`: Greek
+- `en-GB`: English (British)
+- `en-US`: English (American)
+- `es-419`: Spanish (Latin America)
+- `es-ES`: Spanish (Spain)
+- `fi`: Finnish
+- `fr`: French
+- `hi`: Hindi
+- `hr`: Croatian
+- `hu`: Hungarian
+- `id`: Indonesian
+- `it`: Italian
+- `ja`: Japanese
+- `ko`: Korean
+- `lt`: Lithuanian
+- `nl`: Dutch
+- `no`: Norwegian
+- `pl`: Polish
+- `pt-BR`: Portuguese (Brazil)
+- `ro`: Romanian
+- `ru`: Russian
+- `sv-SE`: Swedish
+- `th`: Thai
+- `tr`: Turkish
+- `uk`: Ukrainian
+- `vi`: Vietnamese
+- `zh-CN`: Chinese (Simplified)
+- `zh-TW`: Chinese (Traditional)
+
### Integrations
#### Angular
diff --git a/assets/readme-templates/REACT_USAGE.md b/assets/readme-templates/REACT_USAGE.md
index 16ef5ecdd..143ee1169 100644
--- a/assets/readme-templates/REACT_USAGE.md
+++ b/assets/readme-templates/REACT_USAGE.md
@@ -49,6 +49,78 @@ do so by including the CSS below:
}
```
+### Internationalization
+
+This package uses [i18next](https://www.i18next.com/) for internationalization.
+We load i18next into Lit using [lit-i18n](https://github.com/colscott/lit-i18n)
+and we also add the
+[i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector)
+plugin to attempt to detect the user's browser language.
+
+#### Initialization
+
+i18next will be initialized by importing any component internally.
+Alternatively, if you want to initialize it yourself (i.e. in your application
+entrypoint) you can do so with the following code:
+
+```ts
+import '@skyra/discord-components-react';
+```
+
+#### Setting the language manually
+
+We expose the function `setI18nLanguage` which can be used to manually set the
+language of i18next. You can use one of the following:
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components-react';
+```
+
+#### Integrating with `react-i18next`
+
+If you want to integrate this with
+[`react-i18next`](https://github.com/i18next/react-i18next), you can simply
+initialize i18next as shown above, then import either `useTranslation` or
+`Trans` from `react-i18next` and use them as you would normally.
+
+#### Supported languages
+
+The list of languages supported are matched to the list of languages the Discord
+client supports. The list is as follows:
+
+- `bg`: Bulgarian
+- `cs`: Czech
+- `da`: Danish
+- `de`: German
+- `el`: Greek
+- `en-GB`: English (British)
+- `en-US`: English (American)
+- `es-419`: Spanish (Latin America)
+- `es-ES`: Spanish (Spain)
+- `fi`: Finnish
+- `fr`: French
+- `hi`: Hindi
+- `hr`: Croatian
+- `hu`: Hungarian
+- `id`: Indonesian
+- `it`: Italian
+- `ja`: Japanese
+- `ko`: Korean
+- `lt`: Lithuanian
+- `nl`: Dutch
+- `no`: Norwegian
+- `pl`: Polish
+- `pt-BR`: Portuguese (Brazil)
+- `ro`: Romanian
+- `ru`: Russian
+- `sv-SE`: Swedish
+- `th`: Thai
+- `tr`: Turkish
+- `uk`: Ukrainian
+- `vi`: Vietnamese
+- `zh-CN`: Chinese (Simplified)
+- `zh-TW`: Chinese (Traditional)
+
### Vite
#### Live Demo
diff --git a/packages/core/README.md b/packages/core/README.md
index a6f4377e2..fbc62f0b1 100644
--- a/packages/core/README.md
+++ b/packages/core/README.md
@@ -41,6 +41,10 @@ _React Bindings_
- [Installation](#installation)
- [Usage](#usage)
- [Using the Discord font](#using-the-discord-font)
+ - [Internationalization](#internationalization)
+ - [Initialization](#initialization)
+ - [Setting the language manually](#setting-the-language-manually)
+ - [Supported languages](#supported-languages)
- [Integrations](#integrations)
- [Angular](#angular)
- [Important Notes](#important-notes)
@@ -205,6 +209,82 @@ do so by including the CSS below:
}
```
+### Internationalization
+
+This package uses [i18next](https://www.i18next.com/) for internationalization.
+We load
+[i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector)
+plugin to attempt to detect the user's browser language, or you can set the
+language yourself as seen at
+[Setting the language manually](#setting-the-language-manually).
+
+#### Initialization
+
+i18next will be initialized by importing any component internally.
+Alternatively, if you want to initialize it yourself (i.e. in your application
+entrypoint) you can do so with the following code:
+
+```ts
+import '@skyra/discord-components';
+```
+
+or if you only want to load i18n and not any of the component side effects:
+
+```ts
+import '@skyra/discord-components/i18n';
+```
+
+#### Setting the language manually
+
+We expose the function `setI18nLanguage` which can be used to manually set the
+language of i18next. You can use one of the following:
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components';
+```
+
+```ts
+import { setI18nLanguage } from '@skyra/discord-components/i18n/utils';
+```
+
+#### Supported languages
+
+The list of languages supported are matched to the list of languages the Discord
+client supports. The list is as follows:
+
+- `bg`: Bulgarian
+- `cs`: Czech
+- `da`: Danish
+- `de`: German
+- `el`: Greek
+- `en-GB`: English (British)
+- `en-US`: English (American)
+- `es-419`: Spanish (Latin America)
+- `es-ES`: Spanish (Spain)
+- `fi`: Finnish
+- `fr`: French
+- `hi`: Hindi
+- `hr`: Croatian
+- `hu`: Hungarian
+- `id`: Indonesian
+- `it`: Italian
+- `ja`: Japanese
+- `ko`: Korean
+- `lt`: Lithuanian
+- `nl`: Dutch
+- `no`: Norwegian
+- `pl`: Polish
+- `pt-BR`: Portuguese (Brazil)
+- `ro`: Romanian
+- `ru`: Russian
+- `sv-SE`: Swedish
+- `th`: Thai
+- `tr`: Turkish
+- `uk`: Ukrainian
+- `vi`: Vietnamese
+- `zh-CN`: Chinese (Simplified)
+- `zh-TW`: Chinese (Traditional)
+
### Integrations
#### Angular
diff --git a/packages/core/package.json b/packages/core/package.json
index a2cae7a74..51972c614 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -11,6 +11,8 @@
"customElements": "custom-elements.json",
"exports": {
".": "./dist/index.js",
+ "./i18n": "./dist/i18n/init.js",
+ "./i18n/utils": "./dist/i18n/utils.js",
"./discord-action-row.js": "./dist/components/discord-action-row/DiscordActionRow.js",
"./discord-attachments.js": "./dist/components/discord-attachments/DiscordAttachments.js",
"./discord-audio-attachment.js": "./dist/components/discord-audio-attachment/DiscordAudioAttachment.js",
@@ -59,6 +61,8 @@
},
"sideEffects": [
"./dist/index.js",
+ "./dist/i18n/init.js",
+ "./dist/i18n/utils.js",
"./dist/components/discord-action-row/DiscordActionRow.js",
"./dist/components/discord-attachments/DiscordAttachments.js",
"./dist/components/discord-audio-attachment/DiscordAudioAttachment.js",
@@ -120,6 +124,8 @@
},
"dependencies": {
"@lit/context": "^1.1.2",
+ "i18next": "^23.14.0",
+ "i18next-browser-languagedetector": "^8.0.0",
"lit": "^3.2.0"
},
"devDependencies": {
diff --git a/packages/core/src/components/discord-author-info/DiscordAuthorInfo.ts b/packages/core/src/components/discord-author-info/DiscordAuthorInfo.ts
index 45968539d..7fa388594 100644
--- a/packages/core/src/components/discord-author-info/DiscordAuthorInfo.ts
+++ b/packages/core/src/components/discord-author-info/DiscordAuthorInfo.ts
@@ -6,6 +6,7 @@ import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
+import { translate } from '../../i18n/lit-integration.js';
import { getClanIcon } from '../../util.js';
import { messagesCompactMode, messagesLightTheme } from '../discord-messages/DiscordMessages.js';
@@ -222,8 +223,8 @@ export class DiscordAuthorInfo extends LitElement {
/>`
)}
${when(this.bot && !this.server, () => html``)}
- ${when(this.server && !this.bot, () => html`Server`)}
- ${when(this.op, () => html`OP`)}
+ ${when(this.server && !this.bot, () => html`${translate('discord-author-info.server')}`)}
+ ${when(this.op, () => html`${translate('discord-author-info.op')}`)}
${when(
this.compactMode,
() => html`${this.author}`
diff --git a/packages/core/src/components/discord-button/DiscordButton.ts b/packages/core/src/components/discord-button/DiscordButton.ts
index 4375128dd..52fc6f099 100644
--- a/packages/core/src/components/discord-button/DiscordButton.ts
+++ b/packages/core/src/components/discord-button/DiscordButton.ts
@@ -1,3 +1,4 @@
+import i18next from 'i18next';
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
@@ -143,16 +144,16 @@ export class DiscordButton extends LitElement {
public checkType() {
if (this.type) {
if (typeof this.type !== 'string') {
- throw new TypeError('DiscordButton `type` prop must be a string.');
+ throw new TypeError(i18next.t('discord-button.errors.type-error'));
} else if (!this.validButtonTypes.has(this.type)) {
- throw new RangeError("DiscordButton `type` prop must be one of: 'primary', 'secondary', 'success', 'destructive'");
+ throw new RangeError(i18next.t('discord-button.errors.range-error'));
}
}
}
public checkParentElement() {
if (this.parentElement?.tagName.toLowerCase() !== 'discord-action-row') {
- throw new DiscordComponentsError('All components must be direct children of .');
+ throw new DiscordComponentsError(i18next.t('discord-button.errors.wrong-parent'));
}
}
diff --git a/packages/core/src/components/discord-command/DiscordCommand.ts b/packages/core/src/components/discord-command/DiscordCommand.ts
index a9d4f1719..726706405 100644
--- a/packages/core/src/components/discord-command/DiscordCommand.ts
+++ b/packages/core/src/components/discord-command/DiscordCommand.ts
@@ -5,6 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { avatars, profiles } from '../../config.js';
+import { translate } from '../../i18n/lit-integration.js';
import type { LightTheme, Profile } from '../../types.js';
import { messagesCompactMode, messagesLightTheme } from '../discord-messages/DiscordMessages.js';
import { DiscordReply } from '../discord-reply/DiscordReply.js';
@@ -94,7 +95,7 @@ export class DiscordCommand extends LitElement implements LightTheme {
() => html``
)}
${profile.author}
- used
+ ${translate('discord-command.used')}
${this.command}
`;
}
diff --git a/packages/core/src/components/discord-custom-emoji/DiscordCustomEmoji.ts b/packages/core/src/components/discord-custom-emoji/DiscordCustomEmoji.ts
index 2c8e4cd41..468b15de9 100644
--- a/packages/core/src/components/discord-custom-emoji/DiscordCustomEmoji.ts
+++ b/packages/core/src/components/discord-custom-emoji/DiscordCustomEmoji.ts
@@ -109,7 +109,7 @@ export class DiscordCustomEmoji extends LitElement {
'discord-custom-emoji-image': !this.embedEmoji,
'discord-custom-jumbo-emoji-image': this.jumbo
})}
- /> `;
+ />`;
}
}
diff --git a/packages/core/src/components/discord-embed-field/DiscordEmbedField.ts b/packages/core/src/components/discord-embed-field/DiscordEmbedField.ts
index 6387cafb2..097393959 100644
--- a/packages/core/src/components/discord-embed-field/DiscordEmbedField.ts
+++ b/packages/core/src/components/discord-embed-field/DiscordEmbedField.ts
@@ -1,4 +1,5 @@
import { consume } from '@lit/context';
+import i18next from 'i18next';
import { css, html, LitElement, type TemplateResult } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { ifDefined } from 'lit/directives/if-defined.js';
@@ -86,7 +87,7 @@ export class DiscordEmbedField extends LitElement implements LightTheme {
if (this.inlineIndex) {
const inlineIndexAsNumber = Number(this.inlineIndex);
if (!Number.isNaN(inlineIndexAsNumber) && !this.validInlineIndices.has(inlineIndexAsNumber)) {
- throw new RangeError('DiscordEmbedField `inlineIndex` prop must be one of: 1, 2, or 3');
+ throw new RangeError(i18next.t('discord-embed-field.errors.range-error'));
}
}
}
diff --git a/packages/core/src/components/discord-header/DiscordHeader.ts b/packages/core/src/components/discord-header/DiscordHeader.ts
index 4c2810657..94266cb46 100644
--- a/packages/core/src/components/discord-header/DiscordHeader.ts
+++ b/packages/core/src/components/discord-header/DiscordHeader.ts
@@ -1,3 +1,4 @@
+import i18next from 'i18next';
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { choose } from 'lit/directives/choose.js';
@@ -51,7 +52,7 @@ export class DiscordHeader extends LitElement {
public checkLevel() {
if (this.level < 1 || this.level > 3) {
- throw new RangeError('The level property must be a number between 1 and 3 (inclusive)');
+ throw new RangeError(i18next.t('discord-header.errors.range-error'));
}
}
diff --git a/packages/core/src/components/discord-input-text/DiscordInputText.ts b/packages/core/src/components/discord-input-text/DiscordInputText.ts
index f1006de1a..895135ead 100644
--- a/packages/core/src/components/discord-input-text/DiscordInputText.ts
+++ b/packages/core/src/components/discord-input-text/DiscordInputText.ts
@@ -1,9 +1,11 @@
import { consume } from '@lit/context';
+import i18next from 'i18next';
import { css, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { when } from 'lit/directives/when.js';
+import { translate } from '../../i18n/lit-integration.js';
import { DiscordComponentsError } from '../../util.js';
import { messagesLightTheme } from '../discord-messages/DiscordMessages.js';
@@ -321,8 +323,11 @@ export class DiscordInputText extends LitElement {
${when(
this.hasWarning,
() =>
- html`
- - Must be between ${this.minLength} and ${this.maxLength} in length.${translate('discord-input-text.must-be-between-length', {
+ minLength: this.minLength,
+ maxLength: this.maxLength
+ })}`
)}
@@ -382,7 +387,7 @@ export class DiscordInputText extends LitElement {
!
- Please fill out this field.
+ ${translate('discord-input-text.required-field')}
`
)}
@@ -394,8 +399,11 @@ export class DiscordInputText extends LitElement {
!
Increase this text to ${this.minLength} characters or more. You are currently using ${this.calculatedCharactersCount}
- characters${translate('discord-input-text.increase-length', {
+ minLength: this.minLength,
+ calculatedCharactersCount: this.calculatedCharactersCount,
+ count: this.calculatedCharactersCount
+ })}
`
@@ -406,7 +414,7 @@ export class DiscordInputText extends LitElement {
this.hasWarning && this.valueIsNotNullOrUndefined(this.minLength),
() =>
html`Must be ${this.minLength} characters or more in length.${translate('discord-input-text.required-minimum-length', { minLength: this.minLength })}`
)}
@@ -419,17 +427,17 @@ export class DiscordInputText extends LitElement {
private checkNeededArgument() {
if (!this.label) {
- throw new DiscordComponentsError('Label is required to input text');
+ throw new DiscordComponentsError(i18next.t('discord-input-text.errors.label-required'));
} else if (!this.type) {
- throw new DiscordComponentsError('Type is required to input text');
+ throw new DiscordComponentsError(i18next.t('discord-input-text.errors.type-required'));
}
}
private checkType() {
if (typeof this.type !== 'string') {
- throw new TypeError('DiscordInputText `type` prop must be a string.');
+ throw new TypeError(i18next.t('discord-input-text.errors.type-must-be-string'));
} else if (!this.validInputTextTypes.has(this.type)) {
- throw new RangeError("DiscordInputText `type` prop must be one of: 'short', 'paragraph'");
+ throw new RangeError(i18next.t('discord-input-text.errors.type-required-variant'));
}
}
diff --git a/packages/core/src/components/discord-list-item/DiscordListItem.ts b/packages/core/src/components/discord-list-item/DiscordListItem.ts
index 4d21468bd..1e03630b0 100644
--- a/packages/core/src/components/discord-list-item/DiscordListItem.ts
+++ b/packages/core/src/components/discord-list-item/DiscordListItem.ts
@@ -1,3 +1,4 @@
+import i18next from 'i18next';
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { DiscordComponentsError } from '../../util.js';
@@ -18,9 +19,7 @@ export class DiscordListItem extends LitElement {
this.parentElement?.tagName.toLowerCase() !== 'discord-unordered-list' &&
this.parentElement?.tagName.toLowerCase() !== 'discord-ordered-list'
) {
- throw new DiscordComponentsError(
- 'All components must be direct children of or .'
- );
+ throw new DiscordComponentsError(i18next.t('discord-list-item.errors.wrong-parent'));
}
}
diff --git a/packages/core/src/components/discord-modal/DiscordModal.ts b/packages/core/src/components/discord-modal/DiscordModal.ts
index fdc0874a4..268b64d95 100644
--- a/packages/core/src/components/discord-modal/DiscordModal.ts
+++ b/packages/core/src/components/discord-modal/DiscordModal.ts
@@ -5,6 +5,7 @@ import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { createRef, ref, type Ref } from 'lit/directives/ref.js';
import { avatars, profiles } from '../../config.js';
+import { translate } from '../../i18n/lit-integration.js';
import type { LightTheme, Profile } from '../../types.js';
import { DiscordInputText } from '../discord-input-text/DiscordInputText.js';
import { messagesLightTheme } from '../discord-messages/DiscordMessages.js';
@@ -626,7 +627,7 @@ export class DiscordModal extends LitElement implements LightTheme {
})}
>
diff --git a/packages/core/src/components/discord-ordered-list/DiscordOrderedList.ts b/packages/core/src/components/discord-ordered-list/DiscordOrderedList.ts
index 28b70c64b..3dca4ab47 100644
--- a/packages/core/src/components/discord-ordered-list/DiscordOrderedList.ts
+++ b/packages/core/src/components/discord-ordered-list/DiscordOrderedList.ts
@@ -1,3 +1,4 @@
+import i18next from 'i18next';
import { css, html, LitElement } from 'lit';
import { customElement, property, state } from 'lit/decorators.js';
import { DiscordComponentsError } from '../../util.js';
@@ -44,9 +45,7 @@ export class DiscordOrderedList extends LitElement {
});
if (!allChildrenAreListItems) {
- throw new DiscordComponentsError(
- 'All direct children inside of a components must be one of , , or .'
- );
+ throw new DiscordComponentsError(i18next.t('discord-ordered-list.errors.invalid-children'));
}
}
diff --git a/packages/core/src/components/discord-reply/DiscordReply.ts b/packages/core/src/components/discord-reply/DiscordReply.ts
index 6334f864e..5829bcf1d 100644
--- a/packages/core/src/components/discord-reply/DiscordReply.ts
+++ b/packages/core/src/components/discord-reply/DiscordReply.ts
@@ -5,6 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { when } from 'lit/directives/when.js';
import { avatars, profiles } from '../../config.js';
+import { translate } from '../../i18n/lit-integration.js';
import type { LightTheme, Profile } from '../../types.js';
import { getClanIcon } from '../../util.js';
import { messagesCompactMode, messagesLightTheme } from '../discord-messages/DiscordMessages.js';
@@ -390,10 +391,10 @@ export class DiscordReply extends LitElement implements LightTheme {
const profileTag = html`
${when(
profile.bot && !profile.server,
- () => html`${profile.verified ? VerifiedTick() : ''}App`
+ () => html`${profile.verified ? VerifiedTick() : ''}${translate('discord-reply.app')}`
)}
- ${when(profile.server && !profile.bot, () => html`Server`)}
- ${when(profile.op, () => html`OP`)}
+ ${when(profile.server && !profile.bot, () => html`${translate('discord-reply.server')}`)}
+ ${when(profile.op, () => html`${translate('discord-reply.op')}`)}
`;
return html`${when(
@@ -403,7 +404,7 @@ export class DiscordReply extends LitElement implements LightTheme {
)}
${when(
this.deleted,
- () => html`