diff --git a/docs/usage/i18n.md b/docs/usage/i18n.md new file mode 100644 index 00000000000..4c9f282a6e6 --- /dev/null +++ b/docs/usage/i18n.md @@ -0,0 +1,263 @@ +# Internationalization (i18n) + +Swagger UI ships with a built-in internationalization system based on a lightweight key/message lookup. No additional third-party i18n libraries are required. + +--- + +## Quick start + +### Display the UI in a specific language + +Pass a `locale` option when initialising Swagger UI. Swagger UI ships built-in translations for several languages — when a matching locale is detected or configured, those translations are loaded automatically with no extra setup: + +```js +const ui = SwaggerUIBundle({ + url: "https://petstore.swagger.io/v2/swagger.json", + dom_id: "#swagger-ui", + locale: "fr", // French translations are loaded automatically +}) +``` + +### Auto-detect the browser language + +Set `locale` to `null` (the default) and Swagger UI will read `navigator.languages[0]` / `navigator.language` and normalise it to a BCP 47 base language code (e.g. `"fr-CA"` → `"fr"`). If a built-in or user-registered locale matches, it is used; otherwise the UI falls back to English. + +```js +SwaggerUIBundle({ + url: "...", + dom_id: "#swagger-ui", + locale: null, // default — auto-detect from browser +}) +``` + +--- + +## Built-in locales + +The following locales are bundled with Swagger UI and loaded automatically when the detected or configured locale matches: + +| Code | Language | +|------|----------| +| `ca` | Catalan (Català) | +| `de` | German (Deutsch) | +| `en` | English *(default / fallback)* | +| `es` | Spanish (Español) | +| `fr` | French (Français) | +| `is` | Icelandic (Íslenska) | +| `it` | Italian (Italiano) | +| `ja` | Japanese (日本語) | +| `ka` | Georgian (ქართული) | +| `ko` | Korean (한국어) | +| `pl` | Polish (Polski) | +| `pt` | Portuguese / Brazilian Portuguese (Português do Brasil) | +| `ru` | Russian (Русский) | +| `tr` | Turkish (Türkçe) | +| `zh` | Chinese Simplified (简体中文) | + +All locale files are located in `src/core/plugins/i18n/locales/`. + +### Adding or extending translations + +Call `loadMessages` at any time to register additional keys or override existing ones. Partial overrides are supported — any key absent from the registered messages falls through to the English fallback: + +```js +const ui = SwaggerUIBundle({ url: "...", dom_id: "#swagger-ui", locale: "fr" }) + +// Override a subset of French translations +ui.i18nActions.loadMessages("fr", { + "button.execute": "Lancer", + "button.try_it_out": "Tester", +}) + +// Switch locale at runtime +ui.i18nActions.setLocale("de") +``` + +--- + +## Configuration options + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `locale` | `string \| null` | `null` | BCP 47 language tag (e.g. `"en"`, `"fr"`, `"de"`). `null` means auto-detect from the browser. | + +--- + +## Key catalog + +All translatable strings are identified by dot-namespaced keys. The full catalog is defined in [`src/core/plugins/i18n/locales/en.js`](../../src/core/plugins/i18n/locales/en.js). + +### Buttons / actions + +| Key | Default (English) | +|-----|-------------------| +| `button.authorize` | Authorize | +| `button.cancel` | Cancel | +| `button.clear` | Clear | +| `button.close` | Close | +| `button.copy_to_clipboard` | Copy to clipboard | +| `button.download_file` | Download file | +| `button.edit` | Edit | +| `button.execute` | Execute | +| `button.explore` | Explore | +| `button.hide` | Hide | +| `button.logout` | Logout | +| `button.reset` | Reset | +| `button.show` | Show | +| `button.try_it_out` | Try it out | + +### Section / column labels + +| Key | Default (English) | +|-----|-------------------| +| `label.callbacks` | Callbacks | +| `label.code` | Code | +| `label.description` | Description | +| `label.details` | Details | +| `label.examples` | Examples | +| `label.headers` | Headers: | +| `label.links` | Links | +| `label.media_type` | Media type | +| `label.models` | Models | +| `label.name` | Name | +| `label.no_links` | No links | +| `label.no_parameters` | No parameters | +| `label.parameter_content_type` | Parameter content type | +| `label.parameters` | Parameters | +| `label.request_body` | Request body | +| `label.request_duration` | Request duration | +| `label.request_url` | Request URL | +| `label.response_body` | Response body | +| `label.response_content_type` | Response content type | +| `label.response_headers` | Response headers | +| `label.responses` | Responses | +| `label.schemas` | Schemas | +| `label.server_response` | Server response | +| `label.snippets` | Snippets | +| `label.type` | Type | +| `label.undocumented` | Undocumented | + +### Authentication + +| Key | Default (English) | +|-----|-------------------| +| `auth.api_key_in` | In: | +| `auth.api_key_name` | Name: | +| `auth.api_key_value` | Value: | +| `auth.application` | Application: | +| `auth.authorization_header` | Authorization header | +| `auth.authorization_url` | Authorization URL: | +| `auth.authorized` | Authorized | +| `auth.basic_authorization_title` | Basic authorization | +| `auth.client_credentials_location` | Client credentials location: | +| `auth.flow` | Flow: | +| `auth.openid_connect_url` | OpenID Connect URL: | +| `auth.password` | password: | +| `auth.password_cap` | Password: | +| `auth.request_body_option` | Request body | +| `auth.scopes_description` | Scopes are used to grant … | +| `auth.scopes_required` | API requires the following scopes … | +| `auth.scopes_title` | Scopes: | +| `auth.select_all` | select all | +| `auth.select_none` | select none | +| `auth.token_url` | Token URL: | +| `auth.username` | username: | +| `auth.username_cap` | Username: | + +### Accessibility (aria-labels / titles) + +| Key | Default (English) | +|-----|-------------------| +| `aria.apply_credentials` | Apply credentials | +| `aria.apply_oauth2_credentials` | Apply given OAuth2 credentials | +| `aria.authorization_button_locked` | authorization button locked | +| `aria.authorization_button_unlocked` | authorization button unlocked | +| `aria.collapse_operation` | Collapse operation | +| `aria.expand_operation` | Expand operation | +| `aria.remove_authorization` | Remove authorization | +| `aria.request_content_type` | Request content type | +| `aria.response_content_type` | Response content type | + +### Errors + +| Key | Default (English) | +|-----|-------------------| +| `errors.jump_to_line` | Jump to line {{line}} | +| `errors.title` | Errors | + +### Placeholders + +| Key | Default (English) | +|-----|-------------------| +| `placeholder.filter_by_tag` | Filter by tag | + +### Response + +| Key | Default (English) | +|-----|-------------------| +| `response.controls_accept_header_prefix` | Controls | +| `response.controls_accept_header_suffix` | header. | +| `response.json_parse_error` | Can't parse JSON. Raw result:\n\n | +| `response.no_blob_support` | Download headers detected but your browser does not support downloading binary via XHR (Blob). | +| `response.unrecognized_type_display_as_text` | Unrecognized response type; displaying content as text. | +| `response.unrecognized_type_unable_to_display` | Unrecognized response type; unable to display. | + +### Topbar + +| Key | Default (English) | +|-----|-------------------| +| `topbar.select_definition` | Select a definition | + +--- + +## Variable interpolation + +Some messages contain `{{varName}}` placeholders that are replaced at runtime: + +| Key | Variable | +|-----|----------| +| `errors.jump_to_line` | `line` — the line number | + +Example override with interpolation preserved: + +```js +ui.i18nActions.loadMessages("de", { + "errors.jump_to_line": "Gehe zu Zeile {{line}}" +}) +``` + +--- + +## Programmatic API + +The i18n system is exposed through the Redux system just like any other plugin. + +### Dispatch actions + +```js +// Change the active locale at runtime +getSystem().i18nActions.setLocale("de") + +// Register or extend a locale's messages +getSystem().i18nActions.loadMessages("de", { + "button.execute": "Ausführen" +}) +``` + +### Call the translation function directly + +```js +const t = getSystem().t +console.log(t("button.execute")) // "Execute" (or translated equivalent) +console.log(t("errors.jump_to_line", { line: 42 })) // "Jump to line 42" +``` + +--- + +## Architecture notes + +- The i18n system is implemented as a Redux **plugin** (`I18nPlugin`) registered early in the base preset, making it available to all other plugins and components. +- The `t(key, vars?)` function is injected into `rootInjects`, so every connected component receives it as a direct prop alongside `getComponent`, `getConfigs`, etc. +- The **English locale** (`locales/en.js`) is the canonical catalog and ultimate fallback. If a key is absent from the active locale messages, the English string is used. If the key is absent from English too, the raw key string is returned. +- Locale is normalised to the BCP 47 base language code (`"fr-CA"` → `"fr"`). +- All existing config options and behaviour remain unchanged when no `locale` is supplied. diff --git a/src/core/components/auth/api-key-auth.jsx b/src/core/components/auth/api-key-auth.jsx index 03c1e73cce9..d4952e44de7 100644 --- a/src/core/components/auth/api-key-auth.jsx +++ b/src/core/components/auth/api-key-auth.jsx @@ -1,5 +1,6 @@ import React from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class ApiKeyAuth extends React.Component { static propTypes = { @@ -9,7 +10,12 @@ export default class ApiKeyAuth extends React.Component { schema: PropTypes.object.isRequired, name: PropTypes.string.isRequired, onChange: PropTypes.func, - authSelectors: PropTypes.object.isRequired + authSelectors: PropTypes.object.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } constructor(props, context) { @@ -40,7 +46,7 @@ export default class ApiKeyAuth extends React.Component { } render() { - let { schema, getComponent, errSelectors, name, authSelectors } = this.props + let { schema, getComponent, errSelectors, name, authSelectors, t } = this.props const Input = getComponent("Input") const Row = getComponent("Row") const Col = getComponent("Col") @@ -57,18 +63,18 @@ export default class ApiKeyAuth extends React.Component { { name || schema.get("name") } (apiKey) - { value &&
Authorized
} + { value &&
{t("auth.authorized")}
} -

Name: { schema.get("name") }

+

{t("auth.api_key_name")} { schema.get("name") }

-

In: { schema.get("in") }

+

{t("auth.api_key_in")} { schema.get("in") }

- + { value ? ****** : diff --git a/src/core/components/auth/authorize-btn.jsx b/src/core/components/auth/authorize-btn.jsx index 21b9ba2c0b5..9ce235ca333 100644 --- a/src/core/components/auth/authorize-btn.jsx +++ b/src/core/components/auth/authorize-btn.jsx @@ -1,16 +1,22 @@ import React from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class AuthorizeBtn extends React.Component { static propTypes = { onClick: PropTypes.func, isAuthorized: PropTypes.bool, showPopup: PropTypes.bool, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } render() { - let { isAuthorized, showPopup, onClick, getComponent } = this.props + let { isAuthorized, showPopup, onClick, getComponent, t } = this.props //must be moved out of button component const AuthorizationPopup = getComponent("authorizationPopup", true) @@ -20,7 +26,7 @@ export default class AuthorizeBtn extends React.Component { return (
{ showPopup && } diff --git a/src/core/components/auth/authorize-operation-btn.jsx b/src/core/components/auth/authorize-operation-btn.jsx index fcb1fdfd4b7..270c4c04c3a 100644 --- a/src/core/components/auth/authorize-operation-btn.jsx +++ b/src/core/components/auth/authorize-operation-btn.jsx @@ -1,11 +1,17 @@ import React from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class AuthorizeOperationBtn extends React.Component { static propTypes = { isAuthorized: PropTypes.bool.isRequired, onClick: PropTypes.func, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } onClick =(e) => { @@ -18,14 +24,14 @@ export default class AuthorizeOperationBtn extends React.Component { } render() { - let { isAuthorized, getComponent } = this.props + let { isAuthorized, getComponent, t } = this.props const LockAuthOperationIcon = getComponent("LockAuthOperationIcon", true) const UnlockAuthOperationIcon = getComponent("UnlockAuthOperationIcon", true) return ( diff --git a/src/core/components/auth/auths.jsx b/src/core/components/auth/auths.jsx index a29b57359f3..f2d826004cd 100644 --- a/src/core/components/auth/auths.jsx +++ b/src/core/components/auth/auths.jsx @@ -1,6 +1,7 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" +import { fallbackT } from "core/plugins/i18n/fn" export default class Auths extends React.Component { static propTypes = { @@ -9,7 +10,12 @@ export default class Auths extends React.Component { authSelectors: PropTypes.object.isRequired, authActions: PropTypes.object.isRequired, errSelectors: PropTypes.object.isRequired, - specSelectors: PropTypes.object.isRequired + specSelectors: PropTypes.object.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } constructor(props, context) { @@ -55,7 +61,7 @@ export default class Auths extends React.Component { } render() { - let { definitions, getComponent, authSelectors, errSelectors } = this.props + let { definitions, getComponent, authSelectors, errSelectors, t } = this.props const AuthItem = getComponent("AuthItem") const Oauth2 = getComponent("oauth2", true) const Button = getComponent("Button") @@ -89,10 +95,10 @@ export default class Auths extends React.Component { }
{ - nonOauthDefinitions.size === authorizedAuth.size ? - : + nonOauthDefinitions.size === authorizedAuth.size ? + : } - +
} @@ -100,8 +106,8 @@ export default class Auths extends React.Component { { oauthDefinitions && oauthDefinitions.size ?
-

Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.

-

API requires the following scopes. Select which ones you want to grant to Swagger UI.

+

{t("auth.scopes_description")}

+

{t("auth.scopes_required")}

{ definitions.filter( schema => schema.get("type") === "oauth2") diff --git a/src/core/components/auth/basic-auth.jsx b/src/core/components/auth/basic-auth.jsx index b8c69bd5a2a..4b0122ebd0b 100644 --- a/src/core/components/auth/basic-auth.jsx +++ b/src/core/components/auth/basic-auth.jsx @@ -1,6 +1,7 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" +import { fallbackT } from "core/plugins/i18n/fn" export default class BasicAuth extends React.Component { static propTypes = { @@ -10,7 +11,12 @@ export default class BasicAuth extends React.Component { onChange: PropTypes.func.isRequired, name: PropTypes.string.isRequired, errSelectors: PropTypes.object.isRequired, - authSelectors: PropTypes.object.isRequired + authSelectors: PropTypes.object.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } constructor(props, context) { @@ -48,7 +54,7 @@ export default class BasicAuth extends React.Component { } render() { - let { schema, getComponent, name, errSelectors, authSelectors } = this.props + let { schema, getComponent, name, errSelectors, authSelectors, t } = this.props const Input = getComponent("Input") const Row = getComponent("Row") const Col = getComponent("Col") @@ -61,13 +67,13 @@ export default class BasicAuth extends React.Component { return (
-

Basic authorization

- { username &&
Authorized
} +

{t("auth.basic_authorization_title")}

+ { username &&
{t("auth.authorized")}
} - + { username ? { username } : @@ -83,7 +89,7 @@ export default class BasicAuth extends React.Component { } - + { username ? ****** : diff --git a/src/core/components/auth/oauth2.jsx b/src/core/components/auth/oauth2.jsx index dfb6845ba27..6263f6dedf0 100644 --- a/src/core/components/auth/oauth2.jsx +++ b/src/core/components/auth/oauth2.jsx @@ -1,6 +1,7 @@ import React from "react" import PropTypes from "prop-types" import oauth2Authorize from "core/oauth2-authorize" +import { fallbackT } from "core/plugins/i18n/fn" export default class Oauth2 extends React.Component { static propTypes = { @@ -14,7 +15,12 @@ export default class Oauth2 extends React.Component { oas3Selectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired, errActions: PropTypes.object.isRequired, - getConfigs: PropTypes.any + getConfigs: PropTypes.any, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } constructor(props, context) { @@ -109,7 +115,7 @@ export default class Oauth2 extends React.Component { render() { let { - schema, getComponent, authSelectors, errSelectors, name, specSelectors + schema, getComponent, authSelectors, errSelectors, name, specSelectors, t } = this.props const Input = getComponent("Input") const Row = getComponent("Row") @@ -147,21 +153,21 @@ export default class Oauth2 extends React.Component { return (

{name} (OAuth2, { flowToDisplay })

- { !this.state.appName ? null :
Application: { this.state.appName }
} + { !this.state.appName ? null :
{t("auth.application")} { this.state.appName }
} { description && } - { isAuthorized &&
Authorized
} + { isAuthorized &&
{t("auth.authorized")}
} - { oidcUrl &&

OpenID Connect URL: { oidcUrl }

} - { ( flow === AUTH_FLOW_IMPLICIT || flow === AUTH_FLOW_ACCESS_CODE ) &&

Authorization URL: { schema.get("authorizationUrl") }

} - { ( flow === AUTH_FLOW_PASSWORD || flow === AUTH_FLOW_ACCESS_CODE || flow === AUTH_FLOW_APPLICATION ) &&

Token URL: { schema.get("tokenUrl") }

} -

Flow: { flowToDisplay }

+ { oidcUrl &&

{t("auth.openid_connect_url")} { oidcUrl }

} + { ( flow === AUTH_FLOW_IMPLICIT || flow === AUTH_FLOW_ACCESS_CODE ) &&

{t("auth.authorization_url")} { schema.get("authorizationUrl") }

} + { ( flow === AUTH_FLOW_PASSWORD || flow === AUTH_FLOW_ACCESS_CODE || flow === AUTH_FLOW_APPLICATION ) &&

{t("auth.token_url")} { schema.get("tokenUrl") }

} +

{t("auth.flow")} { flowToDisplay }

{ flow !== AUTH_FLOW_PASSWORD ? null : - + { isAuthorized ? { this.state.username } : @@ -173,7 +179,7 @@ export default class Oauth2 extends React.Component { } - + { isAuthorized ? ****** : @@ -182,13 +188,13 @@ export default class Oauth2 extends React.Component { } - + { isAuthorized ? { this.state.passwordType } : } @@ -233,9 +239,9 @@ export default class Oauth2 extends React.Component { { !isAuthorized && scopes && scopes.size ?

- Scopes: - select all - select none + {t("auth.scopes_title")} + {t("auth.select_all")} + {t("auth.select_none")}

{ scopes.map((description, name) => { return ( @@ -270,11 +276,11 @@ export default class Oauth2 extends React.Component { }
{ isValid && - ( isAuthorized ? - : + ( isAuthorized ? + : ) } - +
diff --git a/src/core/components/clear.jsx b/src/core/components/clear.jsx index d4e0ec859ff..2c299608ce1 100644 --- a/src/core/components/clear.jsx +++ b/src/core/components/clear.jsx @@ -1,5 +1,6 @@ import React, { Component } from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class Clear extends Component { @@ -10,9 +11,10 @@ export default class Clear extends Component { } render(){ + const { t } = this.props return ( ) } @@ -21,5 +23,10 @@ export default class Clear extends Component { specActions: PropTypes.object.isRequired, path: PropTypes.string.isRequired, method: PropTypes.string.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } } diff --git a/src/core/components/copy-to-clipboard-btn.jsx b/src/core/components/copy-to-clipboard-btn.jsx index 9c44487e2c5..e31fbf6cffe 100644 --- a/src/core/components/copy-to-clipboard-btn.jsx +++ b/src/core/components/copy-to-clipboard-btn.jsx @@ -1,6 +1,7 @@ import React from "react" import { CopyToClipboard } from "react-copy-to-clipboard" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" /** * @param {{ getComponent: func, textToCopy: string }} props @@ -9,12 +10,12 @@ import PropTypes from "prop-types" */ export default class CopyToClipboardBtn extends React.Component { render() { - let { getComponent } = this.props + let { getComponent, t } = this.props const CopyIcon = getComponent("CopyIcon") return ( -
+
@@ -25,5 +26,10 @@ export default class CopyToClipboardBtn extends React.Component { static propTypes = { getComponent: PropTypes.func.isRequired, textToCopy: PropTypes.string.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } } diff --git a/src/core/components/errors.jsx b/src/core/components/errors.jsx index afe2baaf794..b5576103555 100644 --- a/src/core/components/errors.jsx +++ b/src/core/components/errors.jsx @@ -1,6 +1,7 @@ import React from "react" import PropTypes from "prop-types" import { List } from "immutable" +import { fallbackT } from "core/plugins/i18n/fn" export default class Errors extends React.Component { @@ -10,11 +11,15 @@ export default class Errors extends React.Component { layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, + t: PropTypes.func, } - render() { - let { editorActions, errSelectors, layoutSelectors, layoutActions, getComponent } = this.props + static defaultProps = { + t: fallbackT, + } + render() { + let { editorActions, errSelectors, layoutSelectors, layoutActions, getComponent, t } = this.props const Collapse = getComponent("Collapse") if(editorActions && editorActions.jumpToLine) { @@ -38,18 +43,18 @@ export default class Errors extends React.Component { return (
         
-

Errors

- +

{t("errors.title")}

+
{ sortedJSErrors.map((err, i) => { let type = err.get("type") if(type === "thrown" || type === "auth") { - return + return } if(type === "spec") { - return + return } }) }
@@ -59,7 +64,7 @@ export default class Errors extends React.Component { } } -const ThrownErrorItem = ( { error, jumpToLine } ) => { +const ThrownErrorItem = ( { error, jumpToLine, t } ) => { if(!error) { return null } @@ -76,7 +81,7 @@ const ThrownErrorItem = ( { error, jumpToLine } ) => { { error.get("message") }
- { errorLine && jumpToLine ? Jump to line { errorLine } : null } + { errorLine && jumpToLine ? {t("errors.jump_to_line", { line: errorLine })} : null }
} @@ -84,7 +89,7 @@ const ThrownErrorItem = ( { error, jumpToLine } ) => { ) } -const SpecErrorItem = ( { error, jumpToLine = null } ) => { +const SpecErrorItem = ( { error, jumpToLine, t } ) => { let locationMessage = null if(error.get("path")) { @@ -105,7 +110,7 @@ const SpecErrorItem = ( { error, jumpToLine = null } ) => { { error.get("message") }
@@ -123,10 +128,21 @@ function toTitleCase(str) { ThrownErrorItem.propTypes = { error: PropTypes.object.isRequired, - jumpToLine: PropTypes.func + jumpToLine: PropTypes.func, + t: PropTypes.func, +} + +ThrownErrorItem.defaultProps = { + t: fallbackT, } SpecErrorItem.propTypes = { error: PropTypes.object.isRequired, - jumpToLine: PropTypes.func + jumpToLine: PropTypes.func, + t: PropTypes.func, +} + +SpecErrorItem.defaultProps = { + jumpToLine: null, + t: fallbackT, } diff --git a/src/core/components/execute.jsx b/src/core/components/execute.jsx index e6b3e1b0bed..bdbac7914e9 100644 --- a/src/core/components/execute.jsx +++ b/src/core/components/execute.jsx @@ -1,5 +1,6 @@ import React, { Component } from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class Execute extends Component { @@ -12,7 +13,12 @@ export default class Execute extends Component { oas3Selectors: PropTypes.object.isRequired, oas3Actions: PropTypes.object.isRequired, onExecute: PropTypes.func, - disabled: PropTypes.bool + disabled: PropTypes.bool, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } handleValidateParameters = () => { @@ -93,10 +99,10 @@ export default class Execute extends Component { onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val) render(){ - const { disabled } = this.props + const { disabled, t } = this.props return ( ) } diff --git a/src/core/components/headers.jsx b/src/core/components/headers.jsx index c717b61e571..87b1473ba8a 100644 --- a/src/core/components/headers.jsx +++ b/src/core/components/headers.jsx @@ -1,18 +1,23 @@ import React from "react" import PropTypes from "prop-types" import Im from "immutable" +import { fallbackT } from "core/plugins/i18n/fn" const propClass = "header-example" export default class Headers extends React.Component { static propTypes = { headers: PropTypes.object.isRequired, - getComponent: PropTypes.func.isRequired + getComponent: PropTypes.func.isRequired, + t: PropTypes.func, } - render() { - let { headers, getComponent } = this.props + static defaultProps = { + t: fallbackT, + } + render() { + let { headers, getComponent, t } = this.props const Property = getComponent("Property") const Markdown = getComponent("Markdown", true) @@ -21,13 +26,13 @@ export default class Headers extends React.Component { return (
-

Headers:

+

{t("label.headers")}

- - - + + + diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx index 38ddcafa6df..c2f2543fd26 100644 --- a/src/core/components/live-response.jsx +++ b/src/core/components/live-response.jsx @@ -1,28 +1,39 @@ import React from "react" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" +import { fallbackT } from "core/plugins/i18n/fn" -const Headers = ( { headers } )=>{ +const Headers = ( { headers, t } )=>{ return (
-
Response headers
+
{t("label.response_headers")}
{headers}
) } Headers.propTypes = { - headers: PropTypes.array.isRequired + headers: PropTypes.array.isRequired, + t: PropTypes.func, } -const Duration = ( { duration } ) => { +Headers.defaultProps = { + t: fallbackT, +} + +const Duration = ( { duration, t } ) => { return (
-
Request duration
+
{t("label.request_duration")}
{duration} ms
) } Duration.propTypes = { - duration: PropTypes.number.isRequired + duration: PropTypes.number.isRequired, + t: PropTypes.func, +} + +Duration.defaultProps = { + t: fallbackT, } @@ -34,7 +45,12 @@ export default class LiveResponse extends React.Component { displayRequestDuration: PropTypes.bool.isRequired, specSelectors: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, - getConfigs: PropTypes.func.isRequired + getConfigs: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } shouldComponentUpdate(nextProps) { @@ -47,7 +63,7 @@ export default class LiveResponse extends React.Component { } render() { - const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props + const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method, t } = this.props const { showMutatedRequest, requestSnippetsEnabled } = getConfigs() const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method) @@ -79,17 +95,17 @@ export default class LiveResponse extends React.Component { } { url &&
-

Request URL

+

{t("label.request_url")}

{url}
} -

Server response

+

{t("label.server_response")}

NameDescriptionType{t("label.name")}{t("label.description")}{t("label.type")}
- - + + @@ -98,7 +114,7 @@ export default class LiveResponse extends React.Component { { status } { notDocumented ?
- Undocumented + {t("label.undocumented")}
: null } @@ -118,10 +134,10 @@ export default class LiveResponse extends React.Component { : null } { - hasHeaders ? : null + hasHeaders ? : null } { - displayRequestDuration && duration ? : null + displayRequestDuration && duration ? : null } diff --git a/src/core/components/operation-tag.jsx b/src/core/components/operation-tag.jsx index fe9124bb4f3..fd9576b58d3 100644 --- a/src/core/components/operation-tag.jsx +++ b/src/core/components/operation-tag.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import Im from "immutable" import { createDeepLinkPath, escapeDeepLinkPath, isFunc } from "core/utils" +import { fallbackT } from "core/plugins/i18n/fn" import { safeBuildUrl, sanitizeUrl } from "core/utils/url" /* eslint-disable react/jsx-no-bind */ @@ -12,6 +13,7 @@ export default class OperationTag extends React.Component { static defaultProps = { tagObj: Im.fromJS({}), tag: "", + t: fallbackT, } static propTypes = { @@ -28,6 +30,7 @@ export default class OperationTag extends React.Component { specUrl: PropTypes.string.isRequired, children: PropTypes.element, + t: PropTypes.func, } render() { @@ -41,6 +44,7 @@ export default class OperationTag extends React.Component { getConfigs, getComponent, specUrl, + t, } = this.props let { @@ -105,7 +109,7 @@ export default class OperationTag extends React.Component { } diff --git a/src/core/components/parameters/parameters.jsx b/src/core/components/parameters/parameters.jsx index 0a9bd517b24..a918a2b2bf5 100644 --- a/src/core/components/parameters/parameters.jsx +++ b/src/core/components/parameters/parameters.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types" import { Map, List } from "immutable" import ImPropTypes from "react-immutable-proptypes" import createHtmlReadyId from "core/utils/create-html-ready-id" +import { fallbackT } from "core/plugins/i18n/fn" export default class Parameters extends Component { @@ -32,6 +33,7 @@ export default class Parameters extends Component { pathMethod: PropTypes.array.isRequired, getConfigs: PropTypes.func.isRequired, specPath: ImPropTypes.list.isRequired, + t: PropTypes.func, } @@ -42,6 +44,7 @@ export default class Parameters extends Component { allowTryItOut: true, onChangeKey: [], specPath: [], + t: fallbackT, } onChange = (param, value, isXml) => { @@ -110,6 +113,7 @@ export default class Parameters extends Component { oas3Actions, oas3Selectors, operation, + t, } = this.props const ParameterRow = getComponent("parameterRow") @@ -145,20 +149,20 @@ export default class Parameters extends Component {
this.toggleTab("parameters")} className={`tab-item ${this.state.parametersVisible && "active"}`}> -

Parameters

+

{t("label.parameters")}

{operation.get("callbacks") ? (
this.toggleTab("callbacks")} className={`tab-item ${this.state.callbackVisible && "active"}`}> -

Callbacks

+

{t("label.callbacks")}

) : null }
) : (
-

Parameters

+

{t("label.parameters")}

)} {allowTryItOut ? ( @@ -172,13 +176,13 @@ export default class Parameters extends Component { ) : null} {this.state.parametersVisible ?
- {!groupedParametersArr.length ?

No parameters

: + {!groupedParametersArr.length ?

{t("label.no_parameters")}

:
CodeDetails{t("label.code")}{t("label.details")}
- - + + @@ -218,8 +222,7 @@ export default class Parameters extends Component { isOAS3 && requestBody && this.state.parametersVisible &&
-

Request - body

+

{t("label.request_body")}

diff --git a/src/core/components/response-body.jsx b/src/core/components/response-body.jsx index 4b99f41f8bb..5078c679095 100644 --- a/src/core/components/response-body.jsx +++ b/src/core/components/response-body.jsx @@ -5,6 +5,7 @@ import toLower from "lodash/toLower" import { extractFileNameFromContentDispositionHeader } from "core/utils" import { getKnownSyntaxHighlighterLanguage } from "core/utils/jsonParse" import win from "core/window" +import { fallbackT } from "core/plugins/i18n/fn" export default class ResponseBody extends React.PureComponent { state = { @@ -16,7 +17,12 @@ export default class ResponseBody extends React.PureComponent { contentType: PropTypes.string, getComponent: PropTypes.func.isRequired, headers: PropTypes.object, - url: PropTypes.string + url: PropTypes.string, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } updateParsedContent = (prevContent) => { @@ -50,7 +56,7 @@ export default class ResponseBody extends React.PureComponent { } render() { - let { content, contentType, url, headers={}, getComponent } = this.props + let { content, contentType, url, headers={}, getComponent, t } = this.props const { parsedContent } = this.state const HighlightCode = getComponent("HighlightCode", true) const downloadName = "response_" + new Date().getTime() @@ -85,12 +91,12 @@ export default class ResponseBody extends React.PureComponent { } if(win.navigator && win.navigator.msSaveOrOpenBlob) { - bodyEl = + bodyEl = } else { - bodyEl = + bodyEl = } } else { - bodyEl =
Download headers detected but your browser does not support downloading binary via XHR (Blob).
+ bodyEl =
{ t("response.no_blob_support") }
} // Anything else (CORS) @@ -104,7 +110,7 @@ export default class ResponseBody extends React.PureComponent { try { body = JSON.stringify(JSON.parse(content), null, " ") } catch (error) { - body = "can't parse JSON. Raw result:\n\n" + content + body = t("response.json_parse_error") + content } bodyEl = {body} @@ -145,7 +151,7 @@ export default class ResponseBody extends React.PureComponent { // in `updateParsedContent`, so let's display it bodyEl =

- Unrecognized response type; displaying content as text. + { t("response.unrecognized_type_display_as_text") }

{parsedContent}
@@ -153,7 +159,7 @@ export default class ResponseBody extends React.PureComponent { } else { // Give up bodyEl =

- Unrecognized response type; unable to display. + { t("response.unrecognized_type_unable_to_display") }

} } else { @@ -162,7 +168,7 @@ export default class ResponseBody extends React.PureComponent { } return ( !bodyEl ? null :
-
Response body
+
{t("label.response_body")}
{ bodyEl }
) diff --git a/src/core/components/response.jsx b/src/core/components/response.jsx index b1d0b971cf6..0705a61d9bf 100644 --- a/src/core/components/response.jsx +++ b/src/core/components/response.jsx @@ -3,6 +3,7 @@ import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import cx from "classnames" import { fromJS, Seq, Iterable, Map } from "immutable" +import { fallbackT } from "core/plugins/i18n/fn" import { getExtensions, fromJSOrdered, stringify } from "core/utils" import { getKnownSyntaxHighlighterLanguage } from "core/utils/jsonParse" @@ -44,12 +45,14 @@ export default class Response extends React.Component { contentType: PropTypes.string, activeExamplesKey: PropTypes.string, controlsAcceptHeader: PropTypes.bool, - onContentTypeChange: PropTypes.func + onContentTypeChange: PropTypes.func, + t: PropTypes.func, } static defaultProps = { response: fromJS({}), - onContentTypeChange: () => {} + onContentTypeChange: () => {}, + t: fallbackT, } _onContentTypeChange = (value) => { @@ -87,6 +90,7 @@ export default class Response extends React.Component { contentType, controlsAcceptHeader, oas3Actions, + t, } = this.props let { inferSchema, getSampleSchema } = fn @@ -194,7 +198,7 @@ export default class Response extends React.Component { })} > - Media type + {t("label.media_type")} {controlsAcceptHeader ? ( - Controls Accept header. + {t("response.controls_accept_header_prefix")} + Accept + {t("response.controls_accept_header_suffix")} ) : null}
{Map.isMap(examplesForMediaType) && !examplesForMediaType.isEmpty() ? (
- Examples + {t("label.examples")} { return }) - : No links} + : {t("label.no_links")}} : null} ) diff --git a/src/core/components/responses.jsx b/src/core/components/responses.jsx index fe1f7ddcb8b..56794bf97cc 100644 --- a/src/core/components/responses.jsx +++ b/src/core/components/responses.jsx @@ -3,6 +3,7 @@ import { fromJS, Iterable } from "immutable" import PropTypes from "prop-types" import ImPropTypes from "react-immutable-proptypes" import { defaultStatusCode, getAcceptControllingResponse, isExtension } from "core/utils" +import { fallbackT } from "core/plugins/i18n/fn" import createHtmlReadyId from "core/utils/create-html-ready-id" export default class Responses extends React.Component { @@ -21,13 +22,15 @@ export default class Responses extends React.Component { oas3Actions: PropTypes.object.isRequired, oas3Selectors: PropTypes.object.isRequired, specPath: ImPropTypes.list.isRequired, - fn: PropTypes.object.isRequired + fn: PropTypes.object.isRequired, + t: PropTypes.func, } static defaultProps = { tryItOutResponse: null, produces: fromJS(["application/json"]), - displayRequestDuration: false + displayRequestDuration: false, + t: fallbackT, } // These performance-enhancing checks were disabled as part of Multiple Examples @@ -73,6 +76,7 @@ export default class Responses extends React.Component { method, oas3Selectors, oas3Actions, + t, } = this.props let defaultCode = defaultStatusCode( responses ) @@ -94,12 +98,12 @@ export default class Responses extends React.Component { return (!nonExtensionResponses || !nonExtensionResponses.size) ? null : (
-

Responses

+

{t("label.responses")}

{ specSelectors.isOAS3() ? null :
} @@ -125,9 +129,9 @@ export default class Responses extends React.Component {
NameDescription{t("label.name")}{t("label.description")}
- - - { specSelectors.isOAS3() ? : null } + + + { specSelectors.isOAS3() ? : null } diff --git a/src/core/components/try-it-out-button.jsx b/src/core/components/try-it-out-button.jsx index 176ce314c81..08f25464066 100644 --- a/src/core/components/try-it-out-button.jsx +++ b/src/core/components/try-it-out-button.jsx @@ -1,5 +1,6 @@ import React from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class TryItOutButton extends React.Component { @@ -10,6 +11,7 @@ export default class TryItOutButton extends React.Component { enabled: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form hasUserEditedBody: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form isOAS3: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form + t: PropTypes.func, } static defaultProps = { @@ -19,21 +21,22 @@ export default class TryItOutButton extends React.Component { enabled: false, hasUserEditedBody: false, isOAS3: false, + t: fallbackT, } render() { - const { onTryoutClick, onCancelClick, onResetClick, enabled, hasUserEditedBody, isOAS3 } = this.props + const { onTryoutClick, onCancelClick, onResetClick, enabled, hasUserEditedBody, isOAS3, t } = this.props const showReset = isOAS3 && hasUserEditedBody return (
{ - enabled ? - : + enabled ? + : } { - showReset && + showReset && }
) diff --git a/src/core/config/defaults.js b/src/core/config/defaults.js index 96102dda55e..ee0d8760f68 100644 --- a/src/core/config/defaults.js +++ b/src/core/config/defaults.js @@ -96,6 +96,11 @@ const defaultOptions = Object.freeze({ ], uncaughtExceptionHandler: null, + + // ── Internationalization ───────────────────────────────────────────────── + // BCP 47 locale tag (e.g. "en", "fr", "de"). null = auto-detect from + // navigator.languages / navigator.language, falling back to "en". + locale: null, }) export default defaultOptions diff --git a/src/core/containers/filter.jsx b/src/core/containers/filter.jsx index 440b1ac4ad9..5dfa2df4d12 100644 --- a/src/core/containers/filter.jsx +++ b/src/core/containers/filter.jsx @@ -1,5 +1,6 @@ import React from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" export default class FilterContainer extends React.Component { @@ -8,6 +9,11 @@ export default class FilterContainer extends React.Component { layoutSelectors: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } onFilterChange = (e) => { @@ -16,7 +22,7 @@ export default class FilterContainer extends React.Component { } render () { - const {specSelectors, layoutSelectors, getComponent} = this.props + const {specSelectors, layoutSelectors, getComponent, t} = this.props const Col = getComponent("Col") const isLoading = specSelectors.loadingStatus() === "loading" @@ -32,7 +38,7 @@ export default class FilterContainer extends React.Component { {filter === false ? null :
- diff --git a/src/core/plugins/i18n/actions.js b/src/core/plugins/i18n/actions.js new file mode 100644 index 00000000000..a1562d952d7 --- /dev/null +++ b/src/core/plugins/i18n/actions.js @@ -0,0 +1,20 @@ +/** + * @prettier + */ + +export const SET_LOCALE = "i18n_set_locale" +export const LOAD_MESSAGES = "i18n_load_messages" + +export function setLocale(locale) { + return { + type: SET_LOCALE, + payload: locale, + } +} + +export function loadMessages(locale, messages) { + return { + type: LOAD_MESSAGES, + payload: { locale, messages }, + } +} diff --git a/src/core/plugins/i18n/fn.js b/src/core/plugins/i18n/fn.js new file mode 100644 index 00000000000..046c6468a87 --- /dev/null +++ b/src/core/plugins/i18n/fn.js @@ -0,0 +1,40 @@ +/** + * @prettier + */ +import en from "./locales/en" + +/** + * Pure translation helper. + * Looks up `key` in `localeMsgs` (a plain JS object), then falls back to + * `fallbackMsgs`. Interpolates `{{varName}}` placeholders with values from + * `vars`. Returns the key itself when no match is found. + * + * @param {object} localeMsgs - Key→value map for the active locale (may be null/undefined) + * @param {object} fallbackMsgs - Key→value map for the fallback locale (usually "en") + * @param {string} key - Message key, e.g. "button.cancel" + * @param {object} [vars] - Interpolation variables, e.g. { line: 42 } + * @returns {string} + */ +export function translate(localeMsgs, fallbackMsgs, key, vars) { + const own = Object.prototype.hasOwnProperty + const raw = + localeMsgs && own.call(localeMsgs, key) + ? localeMsgs[key] + : fallbackMsgs && own.call(fallbackMsgs, key) + ? fallbackMsgs[key] + : key + + if (!vars) return String(raw) + return String(raw).replace(/\{\{(\w+)\}\}/g, (_, k) => + own.call(vars, k) ? String(vars[k]) : `{{${k}}}` + ) +} + +/** + * Default translation function that uses the built-in English locale. + * Used as a fallback in components when no `t` prop is injected (e.g. in + * unit tests that do not go through the Redux system). + */ +export function fallbackT(key, vars) { + return translate(null, en, key, vars) +} diff --git a/src/core/plugins/i18n/index.js b/src/core/plugins/i18n/index.js new file mode 100644 index 00000000000..21e696a9186 --- /dev/null +++ b/src/core/plugins/i18n/index.js @@ -0,0 +1,75 @@ +/** + * @prettier + */ +import reducers from "./reducers" +import * as actions from "./actions" +import * as selectors from "./selectors" +import en from "./locales/en" +import builtinLocales from "./locales" +import win from "core/window" + +export default function I18nPlugin() { + return { + afterLoad(system) { + // ── 1. Load built-in English messages ──────────────────────────────── + system.i18nActions.loadMessages("en", en) + + // ── 2. Determine locale ────────────────────────────────────────────── + const { locale: configLocale } = system.getConfigs() + let locale + if (configLocale) { + // Normalize configured locale to base language code, same as auto-detection + locale = configLocale.split("-")[0].toLowerCase() + } else { + const browserLang = + (win.navigator && + win.navigator.languages && + win.navigator.languages[0]) || + (win.navigator && win.navigator.language) || + "en" + locale = browserLang.split("-")[0].toLowerCase() + } + system.i18nActions.setLocale(locale) + + // ── 3. Auto-load matching built-in locale (if any) ─────────────────── + if (locale !== "en" && builtinLocales[locale]) { + system.i18nActions.loadMessages(locale, builtinLocales[locale]) + } + + // ── 4. Register the t() translation function ───────────────────────── + this.rootInjects = this.rootInjects || {} + const own = Object.prototype.hasOwnProperty + this.rootInjects.t = (key, vars) => { + const allMessages = system.i18nSelectors.getMessages() + const currentLocale = system.i18nSelectors.getLocale() + + // Use Immutable-native lookups — avoids expensive .toJS() on every call + const localeMap = allMessages.get(currentLocale) + const enMap = allMessages.get("en") + + let raw + if (localeMap && localeMap.has(key)) { + raw = localeMap.get(key) + } else if (enMap && enMap.has(key)) { + raw = enMap.get(key) + } else { + // Ultimate fallback: static en object (always available without Redux) + raw = own.call(en, key) ? en[key] : key + } + + if (!vars) return String(raw) + return String(raw).replace(/\{\{(\w+)\}\}/g, (_, k) => + own.call(vars, k) ? String(vars[k]) : `{{${k}}}` + ) + } + }, + + statePlugins: { + i18n: { + reducers, + actions, + selectors, + }, + }, + } +} diff --git a/src/core/plugins/i18n/locales/ca.js b/src/core/plugins/i18n/locales/ca.js new file mode 100644 index 00000000000..9d6b0fa4d75 --- /dev/null +++ b/src/core/plugins/i18n/locales/ca.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * Catalan (Català) message catalog. + */ +const ca = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autoritza", + "button.cancel": "Cancel·la", + "button.clear": "Neteja", + "button.close": "Tanca", + "button.copy_to_clipboard": "Copia al porta-retalls", + "button.download_file": "Descarrega el fitxer", + "button.edit": "Edita", + "button.execute": "Executa", + "button.explore": "Explora", + "button.hide": "Amaga", + "button.logout": "Tanca sessió", + "button.reset": "Restableix", + "button.show": "Mostra", + "button.try_it_out": "Prova-ho", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Codi", + "label.description": "Descripció", + "label.details": "Detalls", + "label.examples": "Exemples", + "label.headers": "Capçaleres:", + "label.links": "Enllaços", + "label.media_type": "Tipus de mitjà", + "label.models": "Models", + "label.name": "Nom", + "label.no_links": "Sense enllaços", + "label.no_parameters": "Sense paràmetres", + "label.parameter_content_type": "Tipus de contingut del paràmetre", + "label.parameters": "Paràmetres", + "label.request_body": "Cos de la sol·licitud", + "label.request_duration": "Durada de la sol·licitud", + "label.request_url": "URL de la sol·licitud", + "label.response_body": "Cos de la resposta", + "label.response_content_type": "Tipus de contingut de la resposta", + "label.response_headers": "Capçaleres de la resposta", + "label.responses": "Respostes", + "label.schemas": "Esquemes", + "label.server_response": "Resposta del servidor", + "label.snippets": "Fragments", + "label.type": "Tipus", + "label.undocumented": "No documentat", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "A:", + "auth.api_key_name": "Nom:", + "auth.api_key_value": "Valor:", + "auth.application": "Aplicació:", + "auth.authorization_header": "Capçalera d'autorització", + "auth.authorization_url": "URL d'autorització:", + "auth.authorized": "Autoritzat", + "auth.basic_authorization_title": "Autorització bàsica", + "auth.client_credentials_location": "Ubicació de les credencials del client:", + "auth.flow": "Flux:", + "auth.openid_connect_url": "URL de connexió OpenID:", + "auth.password": "contrasenya:", + "auth.password_cap": "Contrasenya:", + "auth.request_body_option": "Cos de la sol·licitud", + "auth.scopes_description": + "Els àmbits s'utilitzen per atorgar a una aplicació diferents nivells d'accés a les dades en nom de l'usuari final. Cada API pot declarar un o més àmbits.", + "auth.scopes_required": + "L'API requereix els àmbits següents. Seleccioneu quins voleu atorgar a Swagger UI.", + "auth.scopes_title": "Àmbits:", + "auth.select_all": "selecciona-ho tot", + "auth.select_none": "no en seleccionis cap", + "auth.token_url": "URL del testimoni:", + "auth.username": "nom d'usuari:", + "auth.username_cap": "Nom d'usuari:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Aplica les credencials", + "aria.apply_oauth2_credentials": "Aplica les credencials OAuth2 indicades", + "aria.authorization_button_locked": "botó d'autorització bloquejat", + "aria.authorization_button_unlocked": "botó d'autorització desbloquejat", + "aria.collapse_operation": "Replega l'operació", + "aria.expand_operation": "Expandeix l'operació", + "aria.remove_authorization": "Elimina l'autorització", + "aria.request_content_type": "Tipus de contingut de la sol·licitud", + "aria.response_content_type": "Tipus de contingut de la resposta", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Vés a la línia {{line}}", + "errors.title": "Errors", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtra per etiqueta", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Controla la capçalera ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "No es pot analitzar el JSON. Resultat sense format:\n\n", + "response.no_blob_support": + "S'han detectat capçaleres de descàrrega, però el vostre navegador no suporta la descàrrega de dades binàries via XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Tipus de resposta no reconegut; es mostra el contingut com a text.", + "response.unrecognized_type_unable_to_display": + "Tipus de resposta no reconegut; no es pot mostrar.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Selecciona una definició", +} + +export default ca diff --git a/src/core/plugins/i18n/locales/de.js b/src/core/plugins/i18n/locales/de.js new file mode 100644 index 00000000000..89d664c06cf --- /dev/null +++ b/src/core/plugins/i18n/locales/de.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * German (Deutsch) message catalog. + */ +const de = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autorisieren", + "button.cancel": "Abbrechen", + "button.clear": "Löschen", + "button.close": "Schließen", + "button.copy_to_clipboard": "In die Zwischenablage kopieren", + "button.download_file": "Datei herunterladen", + "button.edit": "Bearbeiten", + "button.execute": "Ausführen", + "button.explore": "Erkunden", + "button.hide": "Verbergen", + "button.logout": "Abmelden", + "button.reset": "Zurücksetzen", + "button.show": "Anzeigen", + "button.try_it_out": "Ausprobieren", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Code", + "label.description": "Beschreibung", + "label.details": "Details", + "label.examples": "Beispiele", + "label.headers": "Header:", + "label.links": "Links", + "label.media_type": "Medientyp", + "label.models": "Modelle", + "label.name": "Name", + "label.no_links": "Keine Links", + "label.no_parameters": "Keine Parameter", + "label.parameter_content_type": "Parameter-Inhaltstyp", + "label.parameters": "Parameter", + "label.request_body": "Anfrage-Body", + "label.request_duration": "Anfragedauer", + "label.request_url": "Anfrage-URL", + "label.response_body": "Antwort-Body", + "label.response_content_type": "Antwort-Inhaltstyp", + "label.response_headers": "Antwortheader", + "label.responses": "Antworten", + "label.schemas": "Schemas", + "label.server_response": "Serverantwort", + "label.snippets": "Snippets", + "label.type": "Typ", + "label.undocumented": "Nicht dokumentiert", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "In:", + "auth.api_key_name": "Name:", + "auth.api_key_value": "Wert:", + "auth.application": "Anwendung:", + "auth.authorization_header": "Autorisierungsheader", + "auth.authorization_url": "Autorisierungs-URL:", + "auth.authorized": "Autorisiert", + "auth.basic_authorization_title": "Basisautorisierung", + "auth.client_credentials_location": "Speicherort der Client-Anmeldedaten:", + "auth.flow": "Flow:", + "auth.openid_connect_url": "OpenID Connect-URL:", + "auth.password": "Passwort:", + "auth.password_cap": "Passwort:", + "auth.request_body_option": "Anfrage-Body", + "auth.scopes_description": + "Scopes werden verwendet, um einer Anwendung verschiedene Zugriffsebenen auf Daten im Namen des Endbenutzers zu gewähren. Jede API kann einen oder mehrere Scopes deklarieren.", + "auth.scopes_required": + "Die API erfordert die folgenden Scopes. Wählen Sie aus, welche Sie Swagger UI gewähren möchten.", + "auth.scopes_title": "Scopes:", + "auth.select_all": "Alle auswählen", + "auth.select_none": "Keine auswählen", + "auth.token_url": "Token-URL:", + "auth.username": "Benutzername:", + "auth.username_cap": "Benutzername:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Anmeldedaten anwenden", + "aria.apply_oauth2_credentials": "Angegebene OAuth2-Anmeldedaten anwenden", + "aria.authorization_button_locked": "Autorisierungsschaltfläche gesperrt", + "aria.authorization_button_unlocked": "Autorisierungsschaltfläche entsperrt", + "aria.collapse_operation": "Operation einklappen", + "aria.expand_operation": "Operation ausklappen", + "aria.remove_authorization": "Autorisierung entfernen", + "aria.request_content_type": "Anfrage-Inhaltstyp", + "aria.response_content_type": "Antwort-Inhaltstyp", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Zur Zeile {{line}} springen", + "errors.title": "Fehler", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Nach Tag filtern", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Steuert den ", + "response.controls_accept_header_suffix": "-Header.", + "response.json_parse_error": + "JSON konnte nicht verarbeitet werden. Rohergebnis:\n\n", + "response.no_blob_support": + "Download-Header erkannt, aber Ihr Browser unterstützt kein Herunterladen von Binärdaten via XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Unbekannter Antworttyp; Inhalt wird als Text angezeigt.", + "response.unrecognized_type_unable_to_display": + "Unbekannter Antworttyp; kann nicht angezeigt werden.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Definition auswählen", +} + +export default de diff --git a/src/core/plugins/i18n/locales/en.js b/src/core/plugins/i18n/locales/en.js new file mode 100644 index 00000000000..60b5989e251 --- /dev/null +++ b/src/core/plugins/i18n/locales/en.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * English message catalog — the canonical reference and ultimate fallback. + * Keys use a namespaced dot-notation: .. + */ +const en = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Authorize", + "button.cancel": "Cancel", + "button.clear": "Clear", + "button.close": "Close", + "button.copy_to_clipboard": "Copy to clipboard", + "button.download_file": "Download file", + "button.edit": "Edit", + "button.execute": "Execute", + "button.explore": "Explore", + "button.hide": "Hide", + "button.logout": "Logout", + "button.reset": "Reset", + "button.show": "Show", + "button.try_it_out": "Try it out", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Code", + "label.description": "Description", + "label.details": "Details", + "label.examples": "Examples", + "label.headers": "Headers:", + "label.links": "Links", + "label.media_type": "Media type", + "label.models": "Models", + "label.name": "Name", + "label.no_links": "No links", + "label.no_parameters": "No parameters", + "label.parameter_content_type": "Parameter content type", + "label.parameters": "Parameters", + "label.request_body": "Request body", + "label.request_duration": "Request duration", + "label.request_url": "Request URL", + "label.response_body": "Response body", + "label.response_content_type": "Response content type", + "label.response_headers": "Response headers", + "label.responses": "Responses", + "label.schemas": "Schemas", + "label.server_response": "Server response", + "label.snippets": "Snippets", + "label.type": "Type", + "label.undocumented": "Undocumented", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "In:", + "auth.api_key_name": "Name:", + "auth.api_key_value": "Value:", + "auth.application": "Application:", + "auth.authorization_header": "Authorization header", + "auth.authorization_url": "Authorization URL:", + "auth.authorized": "Authorized", + "auth.basic_authorization_title": "Basic authorization", + "auth.client_credentials_location": "Client credentials location:", + "auth.flow": "Flow:", + "auth.openid_connect_url": "OpenID Connect URL:", + "auth.password": "password:", + "auth.password_cap": "Password:", + "auth.request_body_option": "Request body", + "auth.scopes_description": + "Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.", + "auth.scopes_required": + "API requires the following scopes. Select which ones you want to grant to Swagger UI.", + "auth.scopes_title": "Scopes:", + "auth.select_all": "select all", + "auth.select_none": "select none", + "auth.token_url": "Token URL:", + "auth.username": "username:", + "auth.username_cap": "Username:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Apply credentials", + "aria.apply_oauth2_credentials": "Apply given OAuth2 credentials", + "aria.authorization_button_locked": "authorization button locked", + "aria.authorization_button_unlocked": "authorization button unlocked", + "aria.collapse_operation": "Collapse operation", + "aria.expand_operation": "Expand operation", + "aria.remove_authorization": "Remove authorization", + "aria.request_content_type": "Request content type", + "aria.response_content_type": "Response content type", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Jump to line {{line}}", + "errors.title": "Errors", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filter by tag", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Controls ", + "response.controls_accept_header_suffix": " header.", + "response.json_parse_error": "Can't parse JSON. Raw result:\n\n", + "response.no_blob_support": + "Download headers detected but your browser does not support downloading binary via XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Unrecognized response type; displaying content as text.", + "response.unrecognized_type_unable_to_display": + "Unrecognized response type; unable to display.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Select a definition", +} + +export default en diff --git a/src/core/plugins/i18n/locales/es.js b/src/core/plugins/i18n/locales/es.js new file mode 100644 index 00000000000..f52b7b66759 --- /dev/null +++ b/src/core/plugins/i18n/locales/es.js @@ -0,0 +1,114 @@ +/** + * @prettier + */ + +/** + * Spanish (Español) message catalog. + */ +const es = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autorizar", + "button.cancel": "Cancelar", + "button.clear": "Limpiar", + "button.close": "Cerrar", + "button.copy_to_clipboard": "Copiar al portapapeles", + "button.download_file": "Descargar archivo", + "button.edit": "Editar", + "button.execute": "Ejecutar", + "button.explore": "Explorar", + "button.hide": "Ocultar", + "button.logout": "Cerrar sesión", + "button.reset": "Restablecer", + "button.show": "Mostrar", + "button.try_it_out": "Pruébalo", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Código", + "label.description": "Descripción", + "label.details": "Detalles", + "label.examples": "Ejemplos", + "label.headers": "Cabeceras:", + "label.links": "Vínculos", + "label.media_type": "Tipo de medio", + "label.models": "Modelos", + "label.name": "Nombre", + "label.no_links": "Sin vínculos", + "label.no_parameters": "Sin parámetros", + "label.parameter_content_type": "Tipo de contenido del parámetro", + "label.parameters": "Parámetros", + "label.request_body": "Cuerpo de la solicitud", + "label.request_duration": "Duración de la solicitud", + "label.request_url": "URL de la solicitud", + "label.response_body": "Cuerpo de la respuesta", + "label.response_content_type": "Tipo de contenido de la respuesta", + "label.response_headers": "Cabeceras de respuesta", + "label.responses": "Respuestas", + "label.schemas": "Esquemas", + "label.server_response": "Respuesta del servidor", + "label.snippets": "Fragmentos", + "label.type": "Tipo", + "label.undocumented": "No documentado", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "En:", + "auth.api_key_name": "Nombre:", + "auth.api_key_value": "Valor:", + "auth.application": "Aplicación:", + "auth.authorization_header": "Cabecera de autorización", + "auth.authorization_url": "URL de autorización:", + "auth.authorized": "Autorizado", + "auth.basic_authorization_title": "Autorización básica", + "auth.client_credentials_location": "Ubicación de credenciales del cliente:", + "auth.flow": "Flujo:", + "auth.openid_connect_url": "URL de OpenID Connect:", + "auth.password": "contraseña:", + "auth.password_cap": "Contraseña:", + "auth.request_body_option": "Cuerpo de la solicitud", + "auth.scopes_description": + "Los scopes se utilizan para otorgar a una aplicación diferentes niveles de acceso a los datos en nombre del usuario final. Cada API puede declarar uno o más scopes.", + "auth.scopes_required": + "La API requiere los siguientes scopes. Seleccione cuáles desea otorgar a Swagger UI.", + "auth.scopes_title": "Scopes:", + "auth.select_all": "seleccionar todos", + "auth.select_none": "seleccionar ninguno", + "auth.token_url": "URL del token:", + "auth.username": "nombre de usuario:", + "auth.username_cap": "Nombre de usuario:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Aplicar credenciales", + "aria.apply_oauth2_credentials": + "Aplicar las credenciales OAuth2 proporcionadas", + "aria.authorization_button_locked": "botón de autorización bloqueado", + "aria.authorization_button_unlocked": "botón de autorización desbloqueado", + "aria.collapse_operation": "Contraer operación", + "aria.expand_operation": "Expandir operación", + "aria.remove_authorization": "Eliminar autorización", + "aria.request_content_type": "Tipo de contenido de la solicitud", + "aria.response_content_type": "Tipo de contenido de la respuesta", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Saltar a la línea {{line}}", + "errors.title": "Errores", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtrar por etiqueta", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Controla la cabecera ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "No se puede analizar el JSON. Resultado sin formato:\n\n", + "response.no_blob_support": + "Encabezados de descarga detectados, pero su navegador no es compatible con la descarga de datos binarios mediante XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Tipo de respuesta no reconocido; mostrando contenido como texto.", + "response.unrecognized_type_unable_to_display": + "Tipo de respuesta no reconocido; no se puede mostrar.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Seleccionar una definición", +} + +export default es diff --git a/src/core/plugins/i18n/locales/fr.js b/src/core/plugins/i18n/locales/fr.js new file mode 100644 index 00000000000..1688322cb21 --- /dev/null +++ b/src/core/plugins/i18n/locales/fr.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * French (Français) message catalog. + */ +const fr = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autoriser", + "button.cancel": "Annuler", + "button.clear": "Effacer", + "button.close": "Fermer", + "button.copy_to_clipboard": "Copier dans le presse-papiers", + "button.download_file": "Télécharger le fichier", + "button.edit": "Modifier", + "button.execute": "Exécuter", + "button.explore": "Explorer", + "button.hide": "Masquer", + "button.logout": "Se déconnecter", + "button.reset": "Réinitialiser", + "button.show": "Afficher", + "button.try_it_out": "Essayer", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Code", + "label.description": "Description", + "label.details": "Détails", + "label.examples": "Exemples", + "label.headers": "En-têtes :", + "label.links": "Liens", + "label.media_type": "Type de média", + "label.models": "Modèles", + "label.name": "Nom", + "label.no_links": "Aucun lien", + "label.no_parameters": "Aucun paramètre", + "label.parameter_content_type": "Type de contenu du paramètre", + "label.parameters": "Paramètres", + "label.request_body": "Corps de la requête", + "label.request_duration": "Durée de la requête", + "label.request_url": "URL de la requête", + "label.response_body": "Corps de la réponse", + "label.response_content_type": "Type de contenu de la réponse", + "label.response_headers": "En-têtes de réponse", + "label.responses": "Réponses", + "label.schemas": "Schémas", + "label.server_response": "Réponse du serveur", + "label.snippets": "Extraits", + "label.type": "Type", + "label.undocumented": "Non documenté", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "Dans :", + "auth.api_key_name": "Nom :", + "auth.api_key_value": "Valeur :", + "auth.application": "Application :", + "auth.authorization_header": "En-tête d'autorisation", + "auth.authorization_url": "URL d'autorisation :", + "auth.authorized": "Autorisé", + "auth.basic_authorization_title": "Autorisation de base", + "auth.client_credentials_location": "Emplacement des identifiants client :", + "auth.flow": "Flux :", + "auth.openid_connect_url": "URL OpenID Connect :", + "auth.password": "mot de passe :", + "auth.password_cap": "Mot de passe :", + "auth.request_body_option": "Corps de la requête", + "auth.scopes_description": + "Les scopes sont utilisés pour accorder à une application différents niveaux d'accès aux données au nom de l'utilisateur final. Chaque API peut déclarer un ou plusieurs scopes.", + "auth.scopes_required": + "L'API nécessite les scopes suivants. Sélectionnez ceux que vous souhaitez accorder à Swagger UI.", + "auth.scopes_title": "Scopes :", + "auth.select_all": "tout sélectionner", + "auth.select_none": "ne rien sélectionner", + "auth.token_url": "URL du token :", + "auth.username": "nom d'utilisateur :", + "auth.username_cap": "Nom d'utilisateur :", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Appliquer les identifiants", + "aria.apply_oauth2_credentials": "Appliquer les identifiants OAuth2 fournis", + "aria.authorization_button_locked": "bouton d'autorisation verrouillé", + "aria.authorization_button_unlocked": "bouton d'autorisation déverrouillé", + "aria.collapse_operation": "Réduire l'opération", + "aria.expand_operation": "Développer l'opération", + "aria.remove_authorization": "Supprimer l'autorisation", + "aria.request_content_type": "Type de contenu de la requête", + "aria.response_content_type": "Type de contenu de la réponse", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Aller à la ligne {{line}}", + "errors.title": "Erreurs", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtrer par étiquette", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Contrôle l'en-tête ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "Impossible d'analyser le JSON. Résultat brut\u00a0:\n\n", + "response.no_blob_support": + "En-têtes de téléchargement détectés, mais votre navigateur ne prend pas en charge le téléchargement de données binaires via XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Type de réponse non reconnu\u00a0; affichage du contenu en texte.", + "response.unrecognized_type_unable_to_display": + "Type de réponse non reconnu\u00a0; impossible d'afficher.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Sélectionner une définition", +} + +export default fr diff --git a/src/core/plugins/i18n/locales/index.js b/src/core/plugins/i18n/locales/index.js new file mode 100644 index 00000000000..9c76d38119c --- /dev/null +++ b/src/core/plugins/i18n/locales/index.js @@ -0,0 +1,27 @@ +/** + * @prettier + */ + +/** + * Built-in locale catalogs bundled with Swagger UI. + * Exported as a plain object keyed by BCP 47 base language code. + */ +import en from "./en" +import ca from "./ca" +import de from "./de" +import es from "./es" +import fr from "./fr" +import is from "./is" +import it from "./it" +import ja from "./ja" +import ka from "./ka" +import ko from "./ko" +import pl from "./pl" +import pt from "./pt" +import ru from "./ru" +import tr from "./tr" +import zh from "./zh" + +const builtinLocales = { en, ca, de, es, fr, is, it, ja, ka, ko, pl, pt, ru, tr, zh } + +export default builtinLocales diff --git a/src/core/plugins/i18n/locales/is.js b/src/core/plugins/i18n/locales/is.js new file mode 100644 index 00000000000..106570cfe99 --- /dev/null +++ b/src/core/plugins/i18n/locales/is.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * Icelandic (Íslenska) message catalog. + */ +const is = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Heimila", + "button.cancel": "Hætta við", + "button.clear": "Hreinsa", + "button.close": "Loka", + "button.copy_to_clipboard": "Afrita á klippiborð", + "button.download_file": "Sækja skrá", + "button.edit": "Breyta", + "button.execute": "Keyra", + "button.explore": "Kanna", + "button.hide": "Fela", + "button.logout": "Útskrá", + "button.reset": "Endurstilla", + "button.show": "Sýna", + "button.try_it_out": "Prófaðu", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Kóði", + "label.description": "Lýsing", + "label.details": "Upplýsingar", + "label.examples": "Dæmi", + "label.headers": "Hausar:", + "label.links": "Tenglar", + "label.media_type": "Miðilsgerð", + "label.models": "Líkön", + "label.name": "Nafn", + "label.no_links": "Engir tenglar", + "label.no_parameters": "Engar breytur", + "label.parameter_content_type": "Innihaldsgerð breytu", + "label.parameters": "Breytur", + "label.request_body": "Meginmál beiðni", + "label.request_duration": "Lengd beiðni", + "label.request_url": "Slóð beiðni", + "label.response_body": "Meginmál svars", + "label.response_content_type": "Innihaldsgerð svars", + "label.response_headers": "Hausar svars", + "label.responses": "Svör", + "label.schemas": "Skema", + "label.server_response": "Svar þjóns", + "label.snippets": "Brot", + "label.type": "Tegund", + "label.undocumented": "Óskráð", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "Í:", + "auth.api_key_name": "Nafn:", + "auth.api_key_value": "Gildi:", + "auth.application": "Forrit:", + "auth.authorization_header": "Heimildarhausur", + "auth.authorization_url": "Heimildarvefslóð:", + "auth.authorized": "Heimilt", + "auth.basic_authorization_title": "Grunnheimild", + "auth.client_credentials_location": "Staðsetning skilríkja biðlara:", + "auth.flow": "Flæði:", + "auth.openid_connect_url": "OpenID Connect vefslóð:", + "auth.password": "lykilorð:", + "auth.password_cap": "Lykilorð:", + "auth.request_body_option": "Meginmál beiðni", + "auth.scopes_description": + "Scopes eru notaðar til að veita forriti mismunandi aðgangsstig að gögnum fyrir hönd notandans. Sérhver API getur lýst yfir einum eða fleiri scopes.", + "auth.scopes_required": + "API krefst eftirfarandi scopes. Veldu þær sem þú vilt veita Swagger UI.", + "auth.scopes_title": "Scopes:", + "auth.select_all": "velja allt", + "auth.select_none": "velja ekkert", + "auth.token_url": "Token vefslóð:", + "auth.username": "notandanafn:", + "auth.username_cap": "Notandanafn:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Nota skilríki", + "aria.apply_oauth2_credentials": "Nota tilgreind OAuth2-skilríki", + "aria.authorization_button_locked": "heimildarhnappur læstur", + "aria.authorization_button_unlocked": "heimildarhnappur opinn", + "aria.collapse_operation": "Fella saman aðgerð", + "aria.expand_operation": "Víkka út aðgerð", + "aria.remove_authorization": "Fjarlægja heimild", + "aria.request_content_type": "Innihaldsgerð beiðni", + "aria.response_content_type": "Innihaldsgerð svars", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Fara í línu {{line}}", + "errors.title": "Villur", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Sía eftir merki", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Stýrir ", + "response.controls_accept_header_suffix": "-hausi.", + "response.json_parse_error": + "Ekki tókst að þátta JSON. Hrá niðurstaða:\n\n", + "response.no_blob_support": + "Niðurhalshaus greindir, en vafrinn þinn styður ekki niðurhal á tvíundagögnum í gegnum XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Óþekkt svargerð; sýni efni sem texta.", + "response.unrecognized_type_unable_to_display": + "Óþekkt svargerð; get ekki sýnt.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Velja skilgreiningu", +} + +export default is diff --git a/src/core/plugins/i18n/locales/it.js b/src/core/plugins/i18n/locales/it.js new file mode 100644 index 00000000000..501edc6ba2c --- /dev/null +++ b/src/core/plugins/i18n/locales/it.js @@ -0,0 +1,115 @@ +/** + * @prettier + */ + +/** + * Italian (Italiano) message catalog. + */ +const it = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autorizza", + "button.cancel": "Annulla", + "button.clear": "Cancella", + "button.close": "Chiudi", + "button.copy_to_clipboard": "Copia negli appunti", + "button.download_file": "Scarica file", + "button.edit": "Modifica", + "button.execute": "Esegui", + "button.explore": "Esplora", + "button.hide": "Nascondi", + "button.logout": "Esci", + "button.reset": "Reimposta", + "button.show": "Mostra", + "button.try_it_out": "Prova", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callback", + "label.code": "Codice", + "label.description": "Descrizione", + "label.details": "Dettagli", + "label.examples": "Esempi", + "label.headers": "Intestazioni:", + "label.links": "Link", + "label.media_type": "Tipo di media", + "label.models": "Modelli", + "label.name": "Nome", + "label.no_links": "Nessun link", + "label.no_parameters": "Nessun parametro", + "label.parameter_content_type": "Tipo di contenuto del parametro", + "label.parameters": "Parametri", + "label.request_body": "Corpo della richiesta", + "label.request_duration": "Durata della richiesta", + "label.request_url": "URL della richiesta", + "label.response_body": "Corpo della risposta", + "label.response_content_type": "Tipo di contenuto della risposta", + "label.response_headers": "Intestazioni della risposta", + "label.responses": "Risposte", + "label.schemas": "Schemi", + "label.server_response": "Risposta del server", + "label.snippets": "Frammenti", + "label.type": "Tipo", + "label.undocumented": "Non documentato", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "In:", + "auth.api_key_name": "Nome:", + "auth.api_key_value": "Valore:", + "auth.application": "Applicazione:", + "auth.authorization_header": "Intestazione di autorizzazione", + "auth.authorization_url": "URL di autorizzazione:", + "auth.authorized": "Autorizzato", + "auth.basic_authorization_title": "Autorizzazione di base", + "auth.client_credentials_location": + "Posizione delle credenziali del client:", + "auth.flow": "Flusso:", + "auth.openid_connect_url": "URL OpenID Connect:", + "auth.password": "password:", + "auth.password_cap": "Password:", + "auth.request_body_option": "Corpo della richiesta", + "auth.scopes_description": + "Gli scope vengono utilizzati per concedere a un'applicazione diversi livelli di accesso ai dati per conto dell'utente finale. Ogni API può dichiarare uno o più scope.", + "auth.scopes_required": + "L'API richiede i seguenti scope. Seleziona quelli che desideri concedere a Swagger UI.", + "auth.scopes_title": "Scope:", + "auth.select_all": "seleziona tutto", + "auth.select_none": "deseleziona tutto", + "auth.token_url": "URL del token:", + "auth.username": "nome utente:", + "auth.username_cap": "Nome utente:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Applica credenziali", + "aria.apply_oauth2_credentials": "Applica le credenziali OAuth2 fornite", + "aria.authorization_button_locked": "pulsante di autorizzazione bloccato", + "aria.authorization_button_unlocked": + "pulsante di autorizzazione sbloccato", + "aria.collapse_operation": "Comprimi operazione", + "aria.expand_operation": "Espandi operazione", + "aria.remove_authorization": "Rimuovi autorizzazione", + "aria.request_content_type": "Tipo di contenuto della richiesta", + "aria.response_content_type": "Tipo di contenuto della risposta", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Vai alla riga {{line}}", + "errors.title": "Errori", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtra per tag", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Controlla l'intestazione ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "Impossibile analizzare il JSON. Risultato grezzo:\n\n", + "response.no_blob_support": + "Intestazioni di download rilevate ma il browser non supporta il download di dati binari tramite XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Tipo di risposta non riconosciuto; visualizzazione del contenuto come testo.", + "response.unrecognized_type_unable_to_display": + "Tipo di risposta non riconosciuto; impossibile visualizzare.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Seleziona una definizione", +} + +export default it diff --git a/src/core/plugins/i18n/locales/ja.js b/src/core/plugins/i18n/locales/ja.js new file mode 100644 index 00000000000..162d6922a6a --- /dev/null +++ b/src/core/plugins/i18n/locales/ja.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * Japanese (日本語) message catalog. + */ +const ja = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "認証", + "button.cancel": "キャンセル", + "button.clear": "クリア", + "button.close": "閉じる", + "button.copy_to_clipboard": "クリップボードにコピー", + "button.download_file": "ファイルをダウンロード", + "button.edit": "編集", + "button.execute": "実行", + "button.explore": "探索", + "button.hide": "非表示", + "button.logout": "ログアウト", + "button.reset": "リセット", + "button.show": "表示", + "button.try_it_out": "試してみる", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "コールバック", + "label.code": "コード", + "label.description": "説明", + "label.details": "詳細", + "label.examples": "例", + "label.headers": "ヘッダ:", + "label.links": "リンク", + "label.media_type": "メディアタイプ", + "label.models": "モデル", + "label.name": "名前", + "label.no_links": "リンクなし", + "label.no_parameters": "パラメータなし", + "label.parameter_content_type": "パラメータのコンテンツタイプ", + "label.parameters": "パラメータ", + "label.request_body": "リクエストボディ", + "label.request_duration": "リクエスト時間", + "label.request_url": "リクエストURL", + "label.response_body": "レスポンスボディ", + "label.response_content_type": "レスポンスのコンテンツタイプ", + "label.response_headers": "レスポンスヘッダ", + "label.responses": "レスポンス", + "label.schemas": "スキーマ", + "label.server_response": "サーバレスポンス", + "label.snippets": "スニペット", + "label.type": "タイプ", + "label.undocumented": "未ドキュメント", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "場所:", + "auth.api_key_name": "名前:", + "auth.api_key_value": "値:", + "auth.application": "アプリケーション:", + "auth.authorization_header": "認証ヘッダ", + "auth.authorization_url": "認証URL:", + "auth.authorized": "認証済み", + "auth.basic_authorization_title": "基本認証", + "auth.client_credentials_location": "クライアント認証情報の場所:", + "auth.flow": "フロー:", + "auth.openid_connect_url": "OpenID Connect URL:", + "auth.password": "パスワード:", + "auth.password_cap": "パスワード:", + "auth.request_body_option": "リクエストボディ", + "auth.scopes_description": + "スコープは、エンドユーザーに代わってアプリケーションにデータへの異なるアクセスレベルを付与するために使用されます。各APIは1つ以上のスコープを宣言できます。", + "auth.scopes_required": + "APIには以下のスコープが必要です。Swagger UIに付与するスコープを選択してください。", + "auth.scopes_title": "スコープ:", + "auth.select_all": "すべて選択", + "auth.select_none": "選択なし", + "auth.token_url": "トークンURL:", + "auth.username": "ユーザ名:", + "auth.username_cap": "ユーザ名:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "認証情報を適用", + "aria.apply_oauth2_credentials": "指定されたOAuth2認証情報を適用", + "aria.authorization_button_locked": "認証ボタンがロックされています", + "aria.authorization_button_unlocked": "認証ボタンがロック解除されています", + "aria.collapse_operation": "操作を折りたたむ", + "aria.expand_operation": "操作を展開する", + "aria.remove_authorization": "認証を削除", + "aria.request_content_type": "リクエストのコンテンツタイプ", + "aria.response_content_type": "レスポンスのコンテンツタイプ", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "{{line}}行目に移動", + "errors.title": "エラー", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "タグで絞り込む", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "", + "response.controls_accept_header_suffix": "ヘッダを制御します。", + "response.json_parse_error": + "JSONを解析できません。 未加工の結果:\n\n", + "response.no_blob_support": + "ダウンロードヘッダが検出されましたが、お使いのブラウザはXHR (Blob) を通じたバイナリデータのダウンロードをサポートしていません。", + "response.unrecognized_type_display_as_text": + "認識できないレスポンスタイプです。コンテンツをテキストとして表示します。", + "response.unrecognized_type_unable_to_display": + "認識できないレスポンスタイプです。表示できません。", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "定義を選択", +} + +export default ja diff --git a/src/core/plugins/i18n/locales/ka.js b/src/core/plugins/i18n/locales/ka.js new file mode 100644 index 00000000000..3bdc53eea9d --- /dev/null +++ b/src/core/plugins/i18n/locales/ka.js @@ -0,0 +1,114 @@ +/** + * @prettier + */ + +/** + * Georgian (ქართული) message catalog. + * ISO 639-1 code: ka (bibliographic "geo" used in the original dist/lang/geo.js) + */ +const ka = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "ავტორიზაცია", + "button.cancel": "გაუქმება", + "button.clear": "გასუფთავება", + "button.close": "დახურვა", + "button.copy_to_clipboard": "ბუფერში კოპირება", + "button.download_file": "ფაილის ჩამოტვირთვა", + "button.edit": "რედაქტირება", + "button.execute": "გაშვება", + "button.explore": "ნახვა", + "button.hide": "დამალვა", + "button.logout": "გასვლა", + "button.reset": "გადატვირთვა", + "button.show": "გამოჩენა", + "button.try_it_out": "ცადე", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "კოდი", + "label.description": "აღწერა", + "label.details": "დეტალები", + "label.examples": "მაგალითები", + "label.headers": "ჰედერები:", + "label.links": "ლინკები", + "label.media_type": "მედიის ტიპი", + "label.models": "მოდელები", + "label.name": "სახელი", + "label.no_links": "ლინკი არ არის", + "label.no_parameters": "პარამეტრი არ არის", + "label.parameter_content_type": "პარამეტრის კონტენტის ტიპი", + "label.parameters": "პარამეტრები", + "label.request_body": "მოთხოვნის სხეული", + "label.request_duration": "მოთხოვნის ხანგრძლივობა", + "label.request_url": "მოთხოვნის URL", + "label.response_body": "პასუხის სხეული", + "label.response_content_type": "პასუხის კონტენტის ტიპი", + "label.response_headers": "პასუხის ჰედერები", + "label.responses": "პასუხები", + "label.schemas": "სქემები", + "label.server_response": "სერვერის პასუხი", + "label.snippets": "ფრაგმენტები", + "label.type": "ტიპი", + "label.undocumented": "დოკუმენტირებული არ არის", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "სად:", + "auth.api_key_name": "სახელი:", + "auth.api_key_value": "მნიშვნელობა:", + "auth.application": "აპლიკაცია:", + "auth.authorization_header": "ავტორიზაციის ჰედერი", + "auth.authorization_url": "ავტორიზაციის URL:", + "auth.authorized": "ავტორიზებულია", + "auth.basic_authorization_title": "საბაზო ავტორიზაცია", + "auth.client_credentials_location": "კლიენტის სერთიფიკატების მდებარეობა:", + "auth.flow": "ნაკადი:", + "auth.openid_connect_url": "OpenID Connect URL:", + "auth.password": "პაროლი:", + "auth.password_cap": "პაროლი:", + "auth.request_body_option": "მოთხოვნის სხეული", + "auth.scopes_description": + "Scope-ები გამოიყენება აპლიკაციისთვის მომხმარებლის სახელით მონაცემებზე განსხვავებული წვდომის დონის მინიჭებისთვის. თითოეული API-ი შეიძლება ერთ ან მეტ scope-ს ეყრდნობოდეს.", + "auth.scopes_required": + "API-ს სჭირდება შემდეგი scope-ები. აირჩიეთ, რომელი გსურთ Swagger UI-სთვის.", + "auth.scopes_title": "Scope-ები:", + "auth.select_all": "ყველას მონიშვნა", + "auth.select_none": "მოხსენება", + "auth.token_url": "ტოკენის URL:", + "auth.username": "მოხმარებელი:", + "auth.username_cap": "მოხმარებელი:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "სერთიფიკატების გამოყენება", + "aria.apply_oauth2_credentials": "მოცემული OAuth2 სერთიფიკატების გამოყენება", + "aria.authorization_button_locked": "ავტორიზაციის ღილაკი დაბლოკილია", + "aria.authorization_button_unlocked": "ავტორიზაციის ღილაკი განბლოკილია", + "aria.collapse_operation": "ოპერაციის ჩაკეცვა", + "aria.expand_operation": "ოპერაციის გაშლა", + "aria.remove_authorization": "ავტორიზაციის წაშლა", + "aria.request_content_type": "მოთხოვნის კონტენტის ტიპი", + "aria.response_content_type": "პასუხის კონტენტის ტიპი", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "{{line}} სტრიქონზე გადასვლა", + "errors.title": "შეცდომები", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "ტეგით გაფილტვრა", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "", + "response.controls_accept_header_suffix": " ჰედერს მართავს.", + "response.json_parse_error": + "JSON-ის დამუშავება ვერ მოხერხდა. ნედლი შედეგი:\n\n", + "response.no_blob_support": + "ჩამოტვირთვის ჰედერები გამოვლინდა, მაგრამ თქვენი ბრაუზერი XHR (Blob) საშუალებით ბინარული მონაცემების ჩამოტვირთვას არ უჭერს მხარს.", + "response.unrecognized_type_display_as_text": + "პასუხის ტიპი ვერ ამოვიცანით; კონტენტს ტექსტად ვაჩვენებთ.", + "response.unrecognized_type_unable_to_display": + "პასუხის ტიპი ვერ ამოვიცანით; ვერ ვაჩვენებთ.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "განსაზღვრების არჩევა", +} + +export default ka diff --git a/src/core/plugins/i18n/locales/ko.js b/src/core/plugins/i18n/locales/ko.js new file mode 100644 index 00000000000..e0909042a2b --- /dev/null +++ b/src/core/plugins/i18n/locales/ko.js @@ -0,0 +1,112 @@ +/** + * @prettier + */ + +/** + * Korean (한국어) message catalog. + */ +const ko = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "인증", + "button.cancel": "취소", + "button.clear": "지우기", + "button.close": "닫기", + "button.copy_to_clipboard": "클립보드에 복사", + "button.download_file": "파일 다운로드", + "button.edit": "수정", + "button.execute": "실행", + "button.explore": "탐색", + "button.hide": "숨기기", + "button.logout": "로그아웃", + "button.reset": "초기화", + "button.show": "표시", + "button.try_it_out": "직접 해보기", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "콜백", + "label.code": "코드", + "label.description": "설명", + "label.details": "세부 정보", + "label.examples": "예시", + "label.headers": "헤더:", + "label.links": "링크", + "label.media_type": "미디어 유형", + "label.models": "모델", + "label.name": "이름", + "label.no_links": "링크 없음", + "label.no_parameters": "파라미터 없음", + "label.parameter_content_type": "파라미터 콘텐츠 유형", + "label.parameters": "파라미터", + "label.request_body": "요청 본문", + "label.request_duration": "요청 시간", + "label.request_url": "요청 URL", + "label.response_body": "응답 본문", + "label.response_content_type": "응답 콘텐츠 유형", + "label.response_headers": "응답 헤더", + "label.responses": "응답", + "label.schemas": "스키마", + "label.server_response": "서버 응답", + "label.snippets": "스니펫", + "label.type": "유형", + "label.undocumented": "미문서화", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "위치:", + "auth.api_key_name": "이름:", + "auth.api_key_value": "값:", + "auth.application": "애플리케이션:", + "auth.authorization_header": "인증 헤더", + "auth.authorization_url": "인증 URL:", + "auth.authorized": "인증됨", + "auth.basic_authorization_title": "기본 인증", + "auth.client_credentials_location": "클라이언트 자격증명 위치:", + "auth.flow": "흐름:", + "auth.openid_connect_url": "OpenID Connect URL:", + "auth.password": "비밀번호:", + "auth.password_cap": "비밀번호:", + "auth.request_body_option": "요청 본문", + "auth.scopes_description": + "스코프는 최종 사용자를 대신하여 애플리케이션에 데이터에 대한 다양한 수준의 액세스를 부여하는 데 사용됩니다. 각 API는 하나 이상의 스코프를 선언할 수 있습니다.", + "auth.scopes_required": + "API에는 다음 스코프가 필요합니다. Swagger UI에 부여할 스코프를 선택하세요.", + "auth.scopes_title": "스코프:", + "auth.select_all": "전체 선택", + "auth.select_none": "전체 해제", + "auth.token_url": "토큰 URL:", + "auth.username": "사용자 이름:", + "auth.username_cap": "사용자 이름:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "자격증명 적용", + "aria.apply_oauth2_credentials": "주어진 OAuth2 자격증명 적용", + "aria.authorization_button_locked": "인증 버튼 잠김", + "aria.authorization_button_unlocked": "인증 버튼 잠금 해제", + "aria.collapse_operation": "작업 축소", + "aria.expand_operation": "작업 확장", + "aria.remove_authorization": "인증 제거", + "aria.request_content_type": "요청 콘텐츠 유형", + "aria.response_content_type": "응답 콘텐츠 유형", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "{{line}}번 줄로 이동", + "errors.title": "오류", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "태그로 필터링", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "", + "response.controls_accept_header_suffix": " 헤더를 제어합니다.", + "response.json_parse_error": "JSON을 파싱할 수 없습니다. 원시 결과:\n\n", + "response.no_blob_support": + "다운로드 헤더가 감지되었지만 브라우저가 XHR (Blob)을 통한 바이너리 다운로드를 지원하지 않습니다.", + "response.unrecognized_type_display_as_text": + "인식되지 않는 응답 유형입니다. 내용을 텍스트로 표시합니다.", + "response.unrecognized_type_unable_to_display": + "인식되지 않는 응답 유형입니다. 표시할 수 없습니다.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "정의 선택", +} + +export default ko diff --git a/src/core/plugins/i18n/locales/pl.js b/src/core/plugins/i18n/locales/pl.js new file mode 100644 index 00000000000..41343dcbfa0 --- /dev/null +++ b/src/core/plugins/i18n/locales/pl.js @@ -0,0 +1,115 @@ +/** + * @prettier + */ + +/** + * Polish (Polski) message catalog. + */ +const pl = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autoryzuj", + "button.cancel": "Anuluj", + "button.clear": "Wyczyść", + "button.close": "Zamknij", + "button.copy_to_clipboard": "Kopiuj do schowka", + "button.download_file": "Pobierz plik", + "button.edit": "Edytuj", + "button.execute": "Wykonaj", + "button.explore": "Eksploruj", + "button.hide": "Ukryj", + "button.logout": "Wyloguj", + "button.reset": "Resetuj", + "button.show": "Pokaż", + "button.try_it_out": "Wypróbuj", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacki", + "label.code": "Kod", + "label.description": "Opis", + "label.details": "Szczegóły", + "label.examples": "Przykłady", + "label.headers": "Nagłówki:", + "label.links": "Łącza", + "label.media_type": "Typ mediów", + "label.models": "Modele", + "label.name": "Nazwa", + "label.no_links": "Brak łączy", + "label.no_parameters": "Brak parametrów", + "label.parameter_content_type": "Typ zawartości parametru", + "label.parameters": "Parametry", + "label.request_body": "Treść żądania", + "label.request_duration": "Czas żądania", + "label.request_url": "URL żądania", + "label.response_body": "Treść odpowiedzi", + "label.response_content_type": "Typ zawartości odpowiedzi", + "label.response_headers": "Nagłówki odpowiedzi", + "label.responses": "Odpowiedzi", + "label.schemas": "Schematy", + "label.server_response": "Odpowiedź serwera", + "label.snippets": "Fragmenty", + "label.type": "Typ", + "label.undocumented": "Nieudokumentowane", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "W:", + "auth.api_key_name": "Nazwa:", + "auth.api_key_value": "Wartość:", + "auth.application": "Aplikacja:", + "auth.authorization_header": "Nagłówek autoryzacji", + "auth.authorization_url": "URL autoryzacji:", + "auth.authorized": "Autoryzowano", + "auth.basic_authorization_title": "Autoryzacja podstawowa", + "auth.client_credentials_location": + "Lokalizacja danych uwierzytelniających klienta:", + "auth.flow": "Przepływ:", + "auth.openid_connect_url": "URL OpenID Connect:", + "auth.password": "hasło:", + "auth.password_cap": "Hasło:", + "auth.request_body_option": "Treść żądania", + "auth.scopes_description": + "Zakresy są używane do przyznawania aplikacji różnych poziomów dostępu do danych w imieniu użytkownika końcowego. Każde API może zadeklarować jeden lub więcej zakresów.", + "auth.scopes_required": + "API wymaga następujących zakresów. Wybierz, które chcesz przyznać Swagger UI.", + "auth.scopes_title": "Zakresy:", + "auth.select_all": "zaznacz wszystkie", + "auth.select_none": "odznacz wszystkie", + "auth.token_url": "URL tokenu:", + "auth.username": "nazwa użytkownika:", + "auth.username_cap": "Nazwa użytkownika:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Zastosuj dane uwierzytelniające", + "aria.apply_oauth2_credentials": + "Zastosuj podane dane uwierzytelniające OAuth2", + "aria.authorization_button_locked": "przycisk autoryzacji zablokowany", + "aria.authorization_button_unlocked": "przycisk autoryzacji odblokowany", + "aria.collapse_operation": "Zwiń operację", + "aria.expand_operation": "Rozwiń operację", + "aria.remove_authorization": "Usuń autoryzację", + "aria.request_content_type": "Typ zawartości żądania", + "aria.response_content_type": "Typ zawartości odpowiedzi", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Przejdź do linii {{line}}", + "errors.title": "Błędy", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtruj według tagu", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Kontroluje nagłówek ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "Nie można przetworzyć JSON. Nieprzetworzone dane:\n\n", + "response.no_blob_support": + "Wykryto nagłówki pobierania, ale przeglądarka nie obsługuje pobierania danych binarnych przez XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Nierozpoznany typ odpowiedzi; wyświetlanie zawartości jako tekstu.", + "response.unrecognized_type_unable_to_display": + "Nierozpoznany typ odpowiedzi; nie można wyświetlić.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Wybierz definicję", +} + +export default pl diff --git a/src/core/plugins/i18n/locales/pt.js b/src/core/plugins/i18n/locales/pt.js new file mode 100644 index 00000000000..b367afbd876 --- /dev/null +++ b/src/core/plugins/i18n/locales/pt.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * Portuguese / Brazilian Portuguese (Português do Brasil) message catalog. + */ +const pt = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Autorizar", + "button.cancel": "Cancelar", + "button.clear": "Limpar", + "button.close": "Fechar", + "button.copy_to_clipboard": "Copiar para a área de transferência", + "button.download_file": "Baixar arquivo", + "button.edit": "Editar", + "button.execute": "Executar", + "button.explore": "Explorar", + "button.hide": "Ocultar", + "button.logout": "Sair", + "button.reset": "Redefinir", + "button.show": "Mostrar", + "button.try_it_out": "Experimente", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Código", + "label.description": "Descrição", + "label.details": "Detalhes", + "label.examples": "Exemplos", + "label.headers": "Cabeçalhos:", + "label.links": "Links", + "label.media_type": "Tipo de mídia", + "label.models": "Modelos", + "label.name": "Nome", + "label.no_links": "Sem links", + "label.no_parameters": "Sem parâmetros", + "label.parameter_content_type": "Tipo de conteúdo do parâmetro", + "label.parameters": "Parâmetros", + "label.request_body": "Corpo da requisição", + "label.request_duration": "Duração da requisição", + "label.request_url": "URL da requisição", + "label.response_body": "Corpo da resposta", + "label.response_content_type": "Tipo de conteúdo da resposta", + "label.response_headers": "Cabeçalhos da resposta", + "label.responses": "Respostas", + "label.schemas": "Esquemas", + "label.server_response": "Resposta do servidor", + "label.snippets": "Trechos", + "label.type": "Tipo", + "label.undocumented": "Não documentado", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "Em:", + "auth.api_key_name": "Nome:", + "auth.api_key_value": "Valor:", + "auth.application": "Aplicação:", + "auth.authorization_header": "Cabeçalho de autorização", + "auth.authorization_url": "URL de autorização:", + "auth.authorized": "Autorizado", + "auth.basic_authorization_title": "Autorização básica", + "auth.client_credentials_location": "Localização das credenciais do cliente:", + "auth.flow": "Fluxo:", + "auth.openid_connect_url": "URL do OpenID Connect:", + "auth.password": "senha:", + "auth.password_cap": "Senha:", + "auth.request_body_option": "Corpo da requisição", + "auth.scopes_description": + "Os escopos são usados para conceder a um aplicativo diferentes níveis de acesso aos dados em nome do usuário final. Cada API pode declarar um ou mais escopos.", + "auth.scopes_required": + "A API requer os seguintes escopos. Selecione quais você deseja conceder ao Swagger UI.", + "auth.scopes_title": "Escopos:", + "auth.select_all": "selecionar todos", + "auth.select_none": "selecionar nenhum", + "auth.token_url": "URL do token:", + "auth.username": "nome de usuário:", + "auth.username_cap": "Nome de usuário:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Aplicar credenciais", + "aria.apply_oauth2_credentials": "Aplicar as credenciais OAuth2 fornecidas", + "aria.authorization_button_locked": "botão de autorização bloqueado", + "aria.authorization_button_unlocked": "botão de autorização desbloqueado", + "aria.collapse_operation": "Recolher operação", + "aria.expand_operation": "Expandir operação", + "aria.remove_authorization": "Remover autorização", + "aria.request_content_type": "Tipo de conteúdo da requisição", + "aria.response_content_type": "Tipo de conteúdo da resposta", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Ir para a linha {{line}}", + "errors.title": "Erros", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Filtrar por tag", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Controla o cabeçalho ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "Não foi possível analisar o JSON. Resultado bruto:\n\n", + "response.no_blob_support": + "Cabeçalhos de download detectados, mas seu navegador não suporta download de dados binários via XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Tipo de resposta não reconhecido; exibindo conteúdo como texto.", + "response.unrecognized_type_unable_to_display": + "Tipo de resposta não reconhecido; não é possível exibir.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Selecionar uma definição", +} + +export default pt diff --git a/src/core/plugins/i18n/locales/ru.js b/src/core/plugins/i18n/locales/ru.js new file mode 100644 index 00000000000..5e2c6bffe6d --- /dev/null +++ b/src/core/plugins/i18n/locales/ru.js @@ -0,0 +1,113 @@ +/** + * @prettier + */ + +/** + * Russian (Русский) message catalog. + */ +const ru = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Авторизовать", + "button.cancel": "Отмена", + "button.clear": "Очистить", + "button.close": "Закрыть", + "button.copy_to_clipboard": "Копировать в буфер обмена", + "button.download_file": "Скачать файл", + "button.edit": "Редактировать", + "button.execute": "Выполнить", + "button.explore": "Исследовать", + "button.hide": "Скрыть", + "button.logout": "Выйти", + "button.reset": "Сбросить", + "button.show": "Показать", + "button.try_it_out": "Попробовать", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Коллбэки", + "label.code": "Код", + "label.description": "Описание", + "label.details": "Детали", + "label.examples": "Примеры", + "label.headers": "Заголовки:", + "label.links": "Ссылки", + "label.media_type": "Тип медиа", + "label.models": "Модели", + "label.name": "Имя", + "label.no_links": "Нет ссылок", + "label.no_parameters": "Нет параметров", + "label.parameter_content_type": "Тип содержимого параметра", + "label.parameters": "Параметры", + "label.request_body": "Тело запроса", + "label.request_duration": "Продолжительность запроса", + "label.request_url": "URL запроса", + "label.response_body": "Тело ответа", + "label.response_content_type": "Тип содержимого ответа", + "label.response_headers": "Заголовки ответа", + "label.responses": "Ответы", + "label.schemas": "Схемы", + "label.server_response": "Ответ сервера", + "label.snippets": "Фрагменты", + "label.type": "Тип", + "label.undocumented": "Не задокументировано", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "В:", + "auth.api_key_name": "Имя:", + "auth.api_key_value": "Значение:", + "auth.application": "Приложение:", + "auth.authorization_header": "Заголовок авторизации", + "auth.authorization_url": "URL авторизации:", + "auth.authorized": "Авторизован", + "auth.basic_authorization_title": "Базовая авторизация", + "auth.client_credentials_location": "Местонахождение учётных данных клиента:", + "auth.flow": "Поток:", + "auth.openid_connect_url": "URL OpenID Connect:", + "auth.password": "пароль:", + "auth.password_cap": "Пароль:", + "auth.request_body_option": "Тело запроса", + "auth.scopes_description": + "Области используются для предоставления приложению разных уровней доступа к данным от имени конечного пользователя. Каждый API может объявлять одну или несколько областей.", + "auth.scopes_required": + "API требует следующих областей. Выберите те, которые вы хотите предоставить Swagger UI.", + "auth.scopes_title": "Области:", + "auth.select_all": "выбрать все", + "auth.select_none": "снять выбор", + "auth.token_url": "URL токена:", + "auth.username": "имя пользователя:", + "auth.username_cap": "Имя пользователя:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Применить учётные данные", + "aria.apply_oauth2_credentials": "Применить указанные учётные данные OAuth2", + "aria.authorization_button_locked": "кнопка авторизации заблокирована", + "aria.authorization_button_unlocked": "кнопка авторизации разблокирована", + "aria.collapse_operation": "Свернуть операцию", + "aria.expand_operation": "Развернуть операцию", + "aria.remove_authorization": "Удалить авторизацию", + "aria.request_content_type": "Тип содержимого запроса", + "aria.response_content_type": "Тип содержимого ответа", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "Перейти к строке {{line}}", + "errors.title": "Ошибки", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Фильтровать по тегу", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "Управляет заголовком ", + "response.controls_accept_header_suffix": ".", + "response.json_parse_error": + "Не удаётся разобрать JSON. Необработанный результат:\n\n", + "response.no_blob_support": + "Обнаружены заголовки загрузки, но ваш браузер не поддерживает загрузку двоичных данных через XHR (Blob).", + "response.unrecognized_type_display_as_text": + "Нераспознанный тип ответа; содержимое отображается как текст.", + "response.unrecognized_type_unable_to_display": + "Нераспознанный тип ответа; отображение невозможно.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Выбрать определение", +} + +export default ru diff --git a/src/core/plugins/i18n/locales/tr.js b/src/core/plugins/i18n/locales/tr.js new file mode 100644 index 00000000000..b6d9538cc1b --- /dev/null +++ b/src/core/plugins/i18n/locales/tr.js @@ -0,0 +1,112 @@ +/** + * @prettier + */ + +/** + * Turkish (Türkçe) message catalog. + */ +const tr = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "Yetkilendir", + "button.cancel": "İptal", + "button.clear": "Temizle", + "button.close": "Kapat", + "button.copy_to_clipboard": "Panoya kopyala", + "button.download_file": "Dosyayı indir", + "button.edit": "Düzenle", + "button.execute": "Çalıştır", + "button.explore": "Keşfet", + "button.hide": "Gizle", + "button.logout": "Çıkış yap", + "button.reset": "Sıfırla", + "button.show": "Göster", + "button.try_it_out": "Dene", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "Callbacks", + "label.code": "Kod", + "label.description": "Açıklama", + "label.details": "Detaylar", + "label.examples": "Örnekler", + "label.headers": "Başlıklar:", + "label.links": "Bağlantılar", + "label.media_type": "Medya türü", + "label.models": "Modeller", + "label.name": "Ad", + "label.no_links": "Bağlantı yok", + "label.no_parameters": "Parametre yok", + "label.parameter_content_type": "Parametre içerik türü", + "label.parameters": "Parametreler", + "label.request_body": "İstek gövdesi", + "label.request_duration": "İstek süresi", + "label.request_url": "İstek URL'i", + "label.response_body": "Yanıt gövdesi", + "label.response_content_type": "Yanıt içerik türü", + "label.response_headers": "Yanıt başlıkları", + "label.responses": "Yanıtlar", + "label.schemas": "Şemalar", + "label.server_response": "Sunucu yanıtı", + "label.snippets": "Parçacıklar", + "label.type": "Tür", + "label.undocumented": "Belgelenmemiş", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "İçinde:", + "auth.api_key_name": "Ad:", + "auth.api_key_value": "Değer:", + "auth.application": "Uygulama:", + "auth.authorization_header": "Yetkilendirme başlığı", + "auth.authorization_url": "Yetkilendirme URL'i:", + "auth.authorized": "Yetkilendirildi", + "auth.basic_authorization_title": "Temel yetkilendirme", + "auth.client_credentials_location": "İstemci kimlik bilgilerinin konumu:", + "auth.flow": "Akış:", + "auth.openid_connect_url": "OpenID Connect URL'i:", + "auth.password": "şifre:", + "auth.password_cap": "Şifre:", + "auth.request_body_option": "İstek gövdesi", + "auth.scopes_description": + "Kapsamlar, son kullanıcı adına bir uygulamaya verilere farklı erişim düzeyleri tanımlamak için kullanılır. Her API bir veya daha fazla kapsam tanımlayabilir.", + "auth.scopes_required": + "API aşağıdaki kapsamları gerektirmektedir. Swagger UI'ye hangi kapsamları vermek istediğinizi seçin.", + "auth.scopes_title": "Kapsamlar:", + "auth.select_all": "tümünü seç", + "auth.select_none": "hiçbirini seçme", + "auth.token_url": "Token URL'i:", + "auth.username": "kullanıcı adı:", + "auth.username_cap": "Kullanıcı Adı:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "Kimlik bilgilerini uygula", + "aria.apply_oauth2_credentials": "Verilen OAuth2 kimlik bilgilerini uygula", + "aria.authorization_button_locked": "yetkilendirme düğmesi kilitli", + "aria.authorization_button_unlocked": "yetkilendirme düğmesi kilidi açık", + "aria.collapse_operation": "İşlemi daralt", + "aria.expand_operation": "İşlemi genişlet", + "aria.remove_authorization": "Yetkilendirmeyi kaldır", + "aria.request_content_type": "İstek içerik türü", + "aria.response_content_type": "Yanıt içerik türü", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "{{line}} satırına git", + "errors.title": "Hatalar", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "Etikete göre filtrele", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "", + "response.controls_accept_header_suffix": " başlığını kontrol eder.", + "response.json_parse_error": "JSON ayrıştırılamıyor. Ham sonuç:\n\n", + "response.no_blob_support": + "İndirme başlıkları algılandı, ancak tarayıcınız XHR (Blob) aracılığıyla ikili veri indirmeyi desteklemiyor.", + "response.unrecognized_type_display_as_text": + "Tanınmayan yanıt türü; içerik metin olarak görüntüleniyor.", + "response.unrecognized_type_unable_to_display": + "Tanınmayan yanıt türü; görüntülenemiyor.", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "Bir tanım seçin", +} + +export default tr diff --git a/src/core/plugins/i18n/locales/zh.js b/src/core/plugins/i18n/locales/zh.js new file mode 100644 index 00000000000..11f59c86f64 --- /dev/null +++ b/src/core/plugins/i18n/locales/zh.js @@ -0,0 +1,112 @@ +/** + * @prettier + */ + +/** + * Chinese Simplified (简体中文) message catalog. + */ +const zh = { + // ── Buttons / actions ───────────────────────────────────────────────────── + "button.authorize": "授权", + "button.cancel": "取消", + "button.clear": "清除", + "button.close": "关闭", + "button.copy_to_clipboard": "复制到剪贴板", + "button.download_file": "下载文件", + "button.edit": "编辑", + "button.execute": "执行", + "button.explore": "探索", + "button.hide": "隐藏", + "button.logout": "退出登录", + "button.reset": "重置", + "button.show": "显示", + "button.try_it_out": "试一下", + + // ── Section / column labels ──────────────────────────────────────────────── + "label.callbacks": "回调", + "label.code": "代码", + "label.description": "描述", + "label.details": "详情", + "label.examples": "示例", + "label.headers": "标头:", + "label.links": "链接", + "label.media_type": "媒体类型", + "label.models": "模型", + "label.name": "名称", + "label.no_links": "无链接", + "label.no_parameters": "无参数", + "label.parameter_content_type": "参数内容类型", + "label.parameters": "参数", + "label.request_body": "请求体", + "label.request_duration": "请求时长", + "label.request_url": "请求 URL", + "label.response_body": "响应体", + "label.response_content_type": "响应内容类型", + "label.response_headers": "响应头", + "label.responses": "响应", + "label.schemas": "模式", + "label.server_response": "服务器响应", + "label.snippets": "代码片段", + "label.type": "类型", + "label.undocumented": "未文档化", + + // ── Authentication ───────────────────────────────────────────────────────── + "auth.api_key_in": "位置:", + "auth.api_key_name": "名称:", + "auth.api_key_value": "值:", + "auth.application": "应用程序:", + "auth.authorization_header": "授权请求头", + "auth.authorization_url": "授权 URL:", + "auth.authorized": "已授权", + "auth.basic_authorization_title": "基本授权", + "auth.client_credentials_location": "客户端凭据位置:", + "auth.flow": "流程:", + "auth.openid_connect_url": "OpenID Connect URL:", + "auth.password": "密码:", + "auth.password_cap": "密码:", + "auth.request_body_option": "请求体", + "auth.scopes_description": + "范围用于代表最终用户向应用程序授予对数据的不同访问级别。每个 API 可以声明一个或多个范围。", + "auth.scopes_required": + "API 需要以下范围。请选择您想要授予 Swagger UI 的范围。", + "auth.scopes_title": "范围:", + "auth.select_all": "全选", + "auth.select_none": "取消全选", + "auth.token_url": "令牌 URL:", + "auth.username": "用户名:", + "auth.username_cap": "用户名:", + + // ── Accessibility (aria-labels / titles) ─────────────────────────────────── + "aria.apply_credentials": "应用凭据", + "aria.apply_oauth2_credentials": "应用给定的 OAuth2 凭据", + "aria.authorization_button_locked": "授权按钮已锁定", + "aria.authorization_button_unlocked": "授权按钮已解锁", + "aria.collapse_operation": "折叠操作", + "aria.expand_operation": "展开操作", + "aria.remove_authorization": "删除授权", + "aria.request_content_type": "请求内容类型", + "aria.response_content_type": "响应内容类型", + + // ── Errors ──────────────────────────────────────────────────────────────── + "errors.jump_to_line": "跳转到第 {{line}} 行", + "errors.title": "错误", + + // ── Placeholders ────────────────────────────────────────────────────────── + "placeholder.filter_by_tag": "按标签过滤", + + // ── Response ────────────────────────────────────────────────────────────── + "response.controls_accept_header_prefix": "控制 ", + "response.controls_accept_header_suffix": " 请求头。", + "response.json_parse_error": "无法解析 JSON。原始结果:\n\n", + "response.no_blob_support": + "检测到下载标头,但您的浏览器不支持通过 XHR (Blob) 下载二进制文件。", + "response.unrecognized_type_display_as_text": + "无法识别的响应类型;将内容显示为文本。", + "response.unrecognized_type_unable_to_display": + "无法识别的响应类型;无法显示。", + + // ── Topbar ──────────────────────────────────────────────────────────────── + "topbar.select_definition": "选择一个定义", +} + +export default zh diff --git a/src/core/plugins/i18n/reducers.js b/src/core/plugins/i18n/reducers.js new file mode 100644 index 00000000000..2f1de565be4 --- /dev/null +++ b/src/core/plugins/i18n/reducers.js @@ -0,0 +1,14 @@ +/** + * @prettier + */ +import { Map } from "immutable" +import { SET_LOCALE, LOAD_MESSAGES } from "./actions" + +export default { + [SET_LOCALE]: (state, { payload: locale }) => state.set("locale", locale), + + [LOAD_MESSAGES]: (state, { payload: { locale, messages } }) => + state.updateIn(["messages", locale], (existing = Map()) => + existing.merge(Map(messages)) + ), +} diff --git a/src/core/plugins/i18n/selectors.js b/src/core/plugins/i18n/selectors.js new file mode 100644 index 00000000000..c9e0c16b8ad --- /dev/null +++ b/src/core/plugins/i18n/selectors.js @@ -0,0 +1,8 @@ +/** + * @prettier + */ +import { Map } from "immutable" + +export const getLocale = (state) => state.get("locale", "en") + +export const getMessages = (state) => state.get("messages", Map()) diff --git a/src/core/plugins/json-schema-5/components/models.jsx b/src/core/plugins/json-schema-5/components/models.jsx index e9023f36f3e..ff77d70b927 100644 --- a/src/core/plugins/json-schema-5/components/models.jsx +++ b/src/core/plugins/json-schema-5/components/models.jsx @@ -1,6 +1,7 @@ import React, { Component } from "react" import Im, { Map } from "immutable" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" /* eslint-disable react/jsx-no-bind */ @@ -11,7 +12,12 @@ export default class Models extends Component { specActions: PropTypes.object.isRequired, layoutSelectors: PropTypes.object, layoutActions: PropTypes.object, - getConfigs: PropTypes.func.isRequired + getConfigs: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } getSchemaBasePath = () => { @@ -45,7 +51,7 @@ export default class Models extends Component { } render(){ - let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props + let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs, t } = this.props let definitions = specSelectors.definitions() let { docExpansion, defaultModelsExpandDepth } = getConfigs() if (!definitions.size || defaultModelsExpandDepth < 0) return null @@ -68,7 +74,7 @@ export default class Models extends Component { className="models-control" onClick={() => layoutActions.show(specPathBase, !showModels)} > - {isOAS3 ? "Schemas" : "Models"} + {isOAS3 ? t("label.schemas") : t("label.models")} {showModels ? : } diff --git a/src/core/plugins/request-snippets/request-snippets.jsx b/src/core/plugins/request-snippets/request-snippets.jsx index 3fbb127a3bc..eb01dcc64f2 100644 --- a/src/core/plugins/request-snippets/request-snippets.jsx +++ b/src/core/plugins/request-snippets/request-snippets.jsx @@ -2,6 +2,7 @@ import React, { useRef, useEffect, useState } from "react" import classNames from "classnames" import PropTypes from "prop-types" import { CopyToClipboard } from "react-copy-to-clipboard" +import { fallbackT } from "core/plugins/i18n/fn" const style = { cursor: "pointer", @@ -33,7 +34,7 @@ const activeStyle = { borderBottom: "none" } -const RequestSnippets = ({ request, requestSnippetsSelectors, getComponent }) => { +const RequestSnippets = ({ request, requestSnippetsSelectors, getComponent, t }) => { const rootRef = useRef(null) const ArrowIcon = getComponent("ArrowUpIcon") @@ -104,11 +105,11 @@ const RequestSnippets = ({ request, requestSnippetsSelectors, getComponent }) =>

handleSetIsExpanded()} style={{ cursor: "pointer" }} - >Snippets

+ >{t("label.snippets")} @@ -158,6 +159,11 @@ RequestSnippets.propTypes = { requestSnippetsSelectors: PropTypes.object.isRequired, getComponent: PropTypes.func.isRequired, requestSnippetsActions: PropTypes.object, + t: PropTypes.func, +} + +RequestSnippets.defaultProps = { + t: fallbackT, } export default RequestSnippets diff --git a/src/core/presets/base/index.js b/src/core/presets/base/index.js index 15b7f6acd24..ff3a8f42104 100644 --- a/src/core/presets/base/index.js +++ b/src/core/presets/base/index.js @@ -6,6 +6,7 @@ import ConfigsPlugin from "core/plugins/configs" import DeepLinkingPlugin from "core/plugins/deep-linking" import ErrPlugin from "core/plugins/err" import FilterPlugin from "core/plugins/filter" +import I18nPlugin from "core/plugins/i18n" import IconsPlugin from "core/plugins/icons" import LayoutPlugin from "core/plugins/layout" import LogsPlugin from "core/plugins/logs" @@ -29,6 +30,7 @@ import FormComponentsPlugin from "core/presets/base/plugins/form-components" const BasePreset = () => [ ConfigsPlugin, UtilPlugin, + I18nPlugin, LogsPlugin, ViewPlugin, ViewLegacyPlugin, diff --git a/src/standalone/plugins/top-bar/components/TopBar.jsx b/src/standalone/plugins/top-bar/components/TopBar.jsx index c19f5af228d..99a1f6e2aaf 100644 --- a/src/standalone/plugins/top-bar/components/TopBar.jsx +++ b/src/standalone/plugins/top-bar/components/TopBar.jsx @@ -1,5 +1,6 @@ import React, { cloneElement } from "react" import PropTypes from "prop-types" +import { fallbackT } from "core/plugins/i18n/fn" import {parseSearch, serializeSearch} from "core/utils" @@ -7,7 +8,16 @@ class TopBar extends React.Component { static propTypes = { layoutActions: PropTypes.object.isRequired, - authActions: PropTypes.object.isRequired + authActions: PropTypes.object.isRequired, + specSelectors: PropTypes.object.isRequired, + specActions: PropTypes.object.isRequired, + getComponent: PropTypes.func.isRequired, + getConfigs: PropTypes.func.isRequired, + t: PropTypes.func, + } + + static defaultProps = { + t: fallbackT, } constructor(props, context) { @@ -133,7 +143,7 @@ class TopBar extends React.Component { }) control.push( -
CodeDescriptionLinks{t("label.code")}{t("label.description")}{t("label.links")}