Compare commits

..

2 Commits

Author SHA1 Message Date
Liyas Thomas
a267e9c411 chore: minor ui improvements 2023-12-12 15:03:04 +05:30
Liyas Thomas
b53ae0cefe fix: use base url instead of hardcoded url 2023-12-12 15:03:04 +05:30
439 changed files with 22742 additions and 19191 deletions

5
.gitignore vendored
View File

@@ -81,7 +81,10 @@ web_modules/
# dotenv environment variable files # dotenv environment variable files
.env .env
.env.* .env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache

View File

@@ -239,7 +239,7 @@ Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) f
📦 **Add-ons:** Official add-ons for hoppscotch. 📦 **Add-ons:** Official add-ons for hoppscotch.
- **[Hoppscotch CLI](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-cli)** - Command-line interface for Hoppscotch. - **[Hoppscotch CLI](https://github.com/hoppscotch/hopp-cli)** - Command-line interface for Hoppscotch.
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch. - **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch.
- **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience. - **[Browser Extensions](https://github.com/hoppscotch/hoppscotch-extension)** - Browser extensions that enhance your Hoppscotch experience.

View File

@@ -66,7 +66,6 @@ services:
# The service that spins up all 3 services at once in one container # The service that spins up all 3 services at once in one container
hoppscotch-aio: hoppscotch-aio:
container_name: hoppscotch-aio container_name: hoppscotch-aio
restart: unless-stopped
build: build:
dockerfile: prod.Dockerfile dockerfile: prod.Dockerfile
context: . context: .
@@ -118,7 +117,7 @@ services:
restart: always restart: always
environment: environment:
# Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well) # Edit the below line to match your PostgresDB URL if you have an outside DB (make sure to update the .env file as well)
# - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300 - DATABASE_URL=postgresql://postgres:testpass@hoppscotch-db:5432/hoppscotch?connect_timeout=300
- PORT=3000 - PORT=3000
volumes: volumes:
# Uncomment the line below when modifying code. Only applicable when using the "dev" target. # Uncomment the line below when modifying code. Only applicable when using the "dev" target.

View File

@@ -25,7 +25,6 @@
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^16.2.3", "@commitlint/cli": "^16.2.3",
"@commitlint/config-conventional": "^16.2.1", "@commitlint/config-conventional": "^16.2.1",
"@hoppscotch/ui": "^0.1.0",
"@types/node": "17.0.27", "@types/node": "17.0.27",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"http-server": "^14.1.1", "http-server": "^14.1.1",

View File

@@ -17,9 +17,9 @@
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"sideEffects": false, "sideEffects": false,
"dependencies": { "dependencies": {
"@codemirror/language": "6.9.3", "@codemirror/language": "6.9.0",
"@lezer/highlight": "1.2.0", "@lezer/highlight": "1.1.4",
"@lezer/lr": "^1.3.14" "@lezer/lr": "^1.3.13"
}, },
"devDependencies": { "devDependencies": {
"@lezer/generator": "^1.5.1", "@lezer/generator": "^1.5.1",

24
packages/dioc/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

141
packages/dioc/README.md Normal file
View File

@@ -0,0 +1,141 @@
# dioc
A small and lightweight dependency injection / inversion of control system.
### About
`dioc` is a really simple **DI/IOC** system where you write services (which are singletons per container) that can depend on each other and emit events that can be listened upon.
### Demo
```ts
import { Service, Container } from "dioc"
// Here is a simple service, which you can define by extending the Service class
// and providing an ID static field (of type string)
export class PersistenceService extends Service {
// This should be unique for each container
public static ID = "PERSISTENCE_SERVICE"
public read(key: string): string | undefined {
// ...
}
public write(key: string, value: string) {
// ...
}
}
type TodoServiceEvent =
| { type: "TODO_CREATED"; index: number }
| { type: "TODO_DELETED"; index: number }
// Services have a built in event system
// Define the generic argument to say what are the possible emitted values
export class TodoService extends Service<TodoServiceEvent> {
public static ID = "TODO_SERVICE"
// Inject persistence service into this service
private readonly persistence = this.bind(PersistenceService)
public todos = []
// Service constructors cannot have arguments
constructor() {
super()
this.todos = JSON.parse(this.persistence.read("todos") ?? "[]")
}
public addTodo(text: string) {
// ...
// You can access services via the bound fields
this.persistence.write("todos", JSON.stringify(this.todos))
// This is how you emit an event
this.emit({
type: "TODO_CREATED",
index,
})
}
public removeTodo(index: number) {
// ...
this.emit({
type: "TODO_DELETED",
index,
})
}
}
// Services need a container to run in
const container = new Container()
// You can initialize and get services using Container#bind
// It will automatically initialize the service (and its dependencies)
const todoService = container.bind(TodoService) // Returns an instance of TodoService
```
### Demo (Unit Test)
`dioc/testing` contains `TestContainer` which lets you bind mocked services to the container.
```ts
import { TestContainer } from "dioc/testing"
import { TodoService, PersistenceService } from "./demo.ts" // The above demo code snippet
import { describe, it, expect, vi } from "vitest"
describe("TodoService", () => {
it("addTodo writes to persistence", () => {
const container = new TestContainer()
const writeFn = vi.fn()
// The first parameter is the service to mock and the second parameter
// is the mocked service fields and functions
container.bindMock(PersistenceService, {
read: () => undefined, // Not really important for this test
write: writeFn,
})
// the peristence service bind in TodoService will now use the
// above defined mocked implementation
const todoService = container.bind(TodoService)
todoService.addTodo("sup")
expect(writeFn).toHaveBeenCalledOnce()
expect(writeFn).toHaveBeenCalledWith("todos", JSON.stringify(["sup"]))
})
})
```
### Demo (Vue)
`dioc/vue` contains a Vue Plugin and a `useService` composable that allows Vue components to use the defined services.
In the app entry point:
```ts
import { createApp } from "vue"
import { diocPlugin } from "dioc/vue"
const app = createApp()
app.use(diocPlugin, {
container: new Container(), // You can pass in the container you want to provide to the components here
})
```
In your Vue components:
```vue
<script setup>
import { TodoService } from "./demo.ts" // The above demo
import { useService } from "dioc/vue"
const todoService = useService(TodoService) // Returns an instance of the TodoService class
</script>
```

2
packages/dioc/index.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export { default } from "./dist/main.d.ts"
export * from "./dist/main.d.ts"

View File

@@ -0,0 +1,147 @@
import { Service } from "./service"
import { Observable, Subject } from 'rxjs'
/**
* Stores the current container instance in the current operating context.
*
* NOTE: This should not be used outside of dioc library code
*/
export let currentContainer: Container | null = null
/**
* The events emitted by the container
*
* `SERVICE_BIND` - emitted when a service is bound to the container directly or as a dependency to another service
* `SERVICE_INIT` - emitted when a service is initialized
*/
export type ContainerEvent =
| {
type: 'SERVICE_BIND';
/** The Service ID of the service being bounded (the dependency) */
boundeeID: string;
/**
* The Service ID of the bounder that is binding the boundee (the dependent)
*
* NOTE: This will be undefined if the service is bound directly to the container
*/
bounderID: string | undefined
}
| {
type: 'SERVICE_INIT';
/** The Service ID of the service being initialized */
serviceID: string
}
/**
* The dependency injection container, allows for services to be initialized and maintains the dependency trees.
*/
export class Container {
/** Used during the `bind` operation to detect circular dependencies */
private bindStack: string[] = []
/** The map of bound services to their IDs */
protected boundMap = new Map<string, Service<unknown>>()
/** The RxJS observable representing the event stream */
protected event$ = new Subject<ContainerEvent>()
/**
* Returns whether a container has the given service bound
* @param service The service to check for
*/
public hasBound<
T extends typeof Service<any> & { ID: string }
>(service: T): boolean {
return this.boundMap.has(service.ID)
}
/**
* Returns the service bound to the container with the given ID or if not found, undefined.
*
* NOTE: This is an advanced method and should not be used as much as possible.
*
* @param serviceID The ID of the service to get
*/
public getBoundServiceWithID(serviceID: string): Service<unknown> | undefined {
return this.boundMap.get(serviceID)
}
/**
* Binds a service to the container. This is equivalent to marking a service as a dependency.
* @param service The class reference of a service to bind
* @param bounder The class reference of the service that is binding the service (if bound directly to the container, this should be undefined)
*/
public bind<T extends typeof Service<any> & { ID: string }>(
service: T,
bounder: ((typeof Service<T>) & { ID: string }) | undefined = undefined
): InstanceType<T> {
// We need to store the current container in a variable so that we can restore it after the bind operation
const oldCurrentContainer = currentContainer;
currentContainer = this;
// If the service is already bound, return the existing instance
if (this.hasBound(service)) {
this.event$.next({
type: 'SERVICE_BIND',
boundeeID: service.ID,
bounderID: bounder?.ID // Return the bounder ID if it is defined, else assume its the container
})
return this.boundMap.get(service.ID) as InstanceType<T> // Casted as InstanceType<T> because service IDs and types are expected to match
}
// Detect circular dependency and throw error
if (this.bindStack.findIndex((serviceID) => serviceID === service.ID) !== -1) {
const circularServices = `${this.bindStack.join(' -> ')} -> ${service.ID}`
throw new Error(`Circular dependency detected.\nChain: ${circularServices}`)
}
// Push the service ID onto the bind stack to detect circular dependencies
this.bindStack.push(service.ID)
// Initialize the service and emit events
// NOTE: We need to cast the service to any as TypeScript thinks that the service is abstract
const instance: Service<any> = new (service as any)()
this.boundMap.set(service.ID, instance)
this.bindStack.pop()
this.event$.next({
type: 'SERVICE_INIT',
serviceID: service.ID,
})
this.event$.next({
type: 'SERVICE_BIND',
boundeeID: service.ID,
bounderID: bounder?.ID
})
// Restore the current container
currentContainer = oldCurrentContainer;
// We expect the return type to match the service definition
return instance as InstanceType<T>
}
/**
* Returns an iterator of the currently bound service IDs and their instances
*/
public getBoundServices(): IterableIterator<[string, Service<any>]> {
return this.boundMap.entries()
}
/**
* Returns the public container event stream
*/
public getEventStream(): Observable<ContainerEvent> {
return this.event$.asObservable()
}
}

View File

@@ -0,0 +1,2 @@
export * from "./container"
export * from "./service"

View File

@@ -0,0 +1,65 @@
import { Observable, Subject } from 'rxjs'
import { Container, currentContainer } from './container'
/**
* A Dioc service that can bound to a container and can bind dependency services.
*
* NOTE: Services cannot have a constructor that takes arguments.
*
* @template EventDef The type of events that can be emitted by the service. These will be accessible by event streams
*/
export abstract class Service<EventDef = {}> {
/**
* The internal event stream of the service
*/
private event$ = new Subject<EventDef>()
/** The container the service is bound to */
#container: Container
constructor() {
if (!currentContainer) {
throw new Error(
`Tried to initialize service with no container (ID: ${ (this.constructor as any).ID })`
)
}
this.#container = currentContainer
}
/**
* Binds a dependency service into this service.
* @param service The class reference of the service to bind
*/
protected bind<T extends typeof Service<any> & { ID: string }>(service: T): InstanceType<T> {
if (!currentContainer) {
throw new Error('No currentContainer defined.')
}
return currentContainer.bind(service, this.constructor as typeof Service<any> & { ID: string })
}
/**
* Returns the container the service is bound to
*/
protected getContainer(): Container {
return this.#container
}
/**
* Emits an event on the service's event stream
* @param event The event to emit
*/
protected emit(event: EventDef) {
this.event$.next(event)
}
/**
* Returns the event stream of the service
*/
public getEventStream(): Observable<EventDef> {
return this.event$.asObservable()
}
}

View File

@@ -0,0 +1,33 @@
import { Container, Service } from "./main";
/**
* A container that can be used for writing tests, contains additional methods
* for binding suitable for writing tests. (see `bindMock`).
*/
export class TestContainer extends Container {
/**
* Binds a mock service to the container.
*
* @param service
* @param mock
*/
public bindMock<
T extends typeof Service<any> & { ID: string },
U extends Partial<InstanceType<T>>
>(service: T, mock: U): U {
if (this.boundMap.has(service.ID)) {
throw new Error(`Service '${service.ID}' already bound to container. Did you already call bindMock on this ?`)
}
this.boundMap.set(service.ID, mock as any)
this.event$.next({
type: "SERVICE_BIND",
boundeeID: service.ID,
bounderID: undefined,
})
return mock
}
}

34
packages/dioc/lib/vue.ts Normal file
View File

@@ -0,0 +1,34 @@
import { Plugin, inject } from "vue"
import { Container } from "./container"
import { Service } from "./service"
const VUE_CONTAINER_KEY = Symbol()
// TODO: Some Vue version issue with plugin generics is breaking type checking
/**
* The Vue Dioc Plugin, this allows the composables to work and access the container
*
* NOTE: Make sure you add `vue` as dependency to be able to use this plugin (duh)
*/
export const diocPlugin: Plugin = {
install(app, { container }) {
app.provide(VUE_CONTAINER_KEY, container)
}
}
/**
* A composable that binds a service to a Vue Component
*
* @param service The class reference of the service to bind
*/
export function useService<
T extends typeof Service<any> & { ID: string }
>(service: T): InstanceType<T> {
const container = inject(VUE_CONTAINER_KEY) as Container | undefined | null
if (!container) {
throw new Error("Container not found, did you forget to install the dioc plugin?")
}
return container.bind(service)
}

View File

@@ -0,0 +1,54 @@
{
"name": "dioc",
"private": true,
"version": "0.1.0",
"type": "module",
"files": [
"dist",
"index.d.ts"
],
"main": "./dist/counter.umd.cjs",
"module": "./dist/counter.js",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./dist/main.d.ts",
"require": "./dist/index.cjs",
"import": "./dist/index.js"
},
"./vue": {
"types": "./dist/vue.d.ts",
"require": "./dist/vue.cjs",
"import": "./dist/vue.js"
},
"./testing": {
"types": "./dist/testing.d.ts",
"require": "./dist/testing.cjs",
"import": "./dist/testing.js"
}
},
"scripts": {
"dev": "vite",
"build": "vite build && tsc --emitDeclarationOnly",
"prepare": "pnpm run build",
"test": "vitest run",
"do-test": "pnpm run test",
"test:watch": "vitest"
},
"devDependencies": {
"typescript": "^4.9.4",
"vite": "^4.0.4",
"vitest": "^0.29.3"
},
"dependencies": {
"rxjs": "^7.8.1"
},
"peerDependencies": {
"vue": "^3.2.25"
},
"peerDependenciesMeta": {
"vue": {
"optional": true
}
}
}

View File

@@ -0,0 +1,262 @@
import { it, expect, describe, vi } from "vitest"
import { Service } from "../lib/service"
import { Container, currentContainer, ContainerEvent } from "../lib/container"
class TestServiceA extends Service {
public static ID = "TestServiceA"
}
class TestServiceB extends Service {
public static ID = "TestServiceB"
// Marked public to allow for testing
public readonly serviceA = this.bind(TestServiceA)
}
describe("Container", () => {
describe("getBoundServiceWithID", () => {
it("returns the service instance if it is bound to the container", () => {
const container = new Container()
const service = container.bind(TestServiceA)
expect(container.getBoundServiceWithID(TestServiceA.ID)).toBe(service)
})
it("returns undefined if the service is not bound to the container", () => {
const container = new Container()
expect(container.getBoundServiceWithID(TestServiceA.ID)).toBeUndefined()
})
})
describe("bind", () => {
it("correctly binds the service to it", () => {
const container = new Container()
const service = container.bind(TestServiceA)
// @ts-expect-error getContainer is defined as a protected property, but we are leveraging it here to check
expect(service.getContainer()).toBe(container)
})
it("after bind, the current container is set back to its previous value", () => {
const originalValue = currentContainer
const container = new Container()
container.bind(TestServiceA)
expect(currentContainer).toBe(originalValue)
})
it("dependent services are registered in the same container", () => {
const container = new Container()
const serviceB = container.bind(TestServiceB)
// @ts-expect-error getContainer is defined as a protected property, but we are leveraging it here to check
expect(serviceB.serviceA.getContainer()).toBe(container)
})
it("binding an already initialized service returns the initialized instance (services are singletons)", () => {
const container = new Container()
const serviceA = container.bind(TestServiceA)
const serviceA2 = container.bind(TestServiceA)
expect(serviceA).toBe(serviceA2)
})
it("binding a service which is a dependency of another service returns the same instance created from the dependency resolution (services are singletons)", () => {
const container = new Container()
const serviceB = container.bind(TestServiceB)
const serviceA = container.bind(TestServiceA)
expect(serviceB.serviceA).toBe(serviceA)
})
it("binding an initialized service as a dependency returns the same instance", () => {
const container = new Container()
const serviceA = container.bind(TestServiceA)
const serviceB = container.bind(TestServiceB)
expect(serviceB.serviceA).toBe(serviceA)
})
it("container emits an init event when an uninitialized service is initialized via bind and event only called once", () => {
const container = new Container()
const serviceFunc = vi.fn<
[ContainerEvent & { type: "SERVICE_INIT" }],
void
>()
container.getEventStream().subscribe((ev) => {
if (ev.type === "SERVICE_INIT") {
serviceFunc(ev)
}
})
const instance = container.bind(TestServiceA)
expect(serviceFunc).toHaveBeenCalledOnce()
expect(serviceFunc).toHaveBeenCalledWith(<ContainerEvent>{
type: "SERVICE_INIT",
serviceID: TestServiceA.ID,
})
})
it("the bind event emitted has an undefined bounderID when the service is bound directly to the container", () => {
const container = new Container()
const serviceFunc = vi.fn<
[ContainerEvent & { type: "SERVICE_BIND" }],
void
>()
container.getEventStream().subscribe((ev) => {
if (ev.type === "SERVICE_BIND") {
serviceFunc(ev)
}
})
container.bind(TestServiceA)
expect(serviceFunc).toHaveBeenCalledOnce()
expect(serviceFunc).toHaveBeenCalledWith(<ContainerEvent>{
type: "SERVICE_BIND",
boundeeID: TestServiceA.ID,
bounderID: undefined,
})
})
it("the bind event emitted has the correct bounderID when the service is bound to another service", () => {
const container = new Container()
const serviceFunc = vi.fn<
[ContainerEvent & { type: "SERVICE_BIND" }],
void
>()
container.getEventStream().subscribe((ev) => {
// We only care about the bind event of TestServiceA
if (ev.type === "SERVICE_BIND" && ev.boundeeID === TestServiceA.ID) {
serviceFunc(ev)
}
})
container.bind(TestServiceB)
expect(serviceFunc).toHaveBeenCalledOnce()
expect(serviceFunc).toHaveBeenCalledWith(<ContainerEvent>{
type: "SERVICE_BIND",
boundeeID: TestServiceA.ID,
bounderID: TestServiceB.ID,
})
})
})
describe("hasBound", () => {
it("returns true if the given service is bound to the container", () => {
const container = new Container()
container.bind(TestServiceA)
expect(container.hasBound(TestServiceA)).toEqual(true)
})
it("returns false if the given service is not bound to the container", () => {
const container = new Container()
expect(container.hasBound(TestServiceA)).toEqual(false)
})
it("returns true when the service is bound because it is a dependency of another service", () => {
const container = new Container()
container.bind(TestServiceB)
expect(container.hasBound(TestServiceA)).toEqual(true)
})
})
describe("getEventStream", () => {
it("returns an observable which emits events correctly when services are initialized", () => {
const container = new Container()
const serviceFunc = vi.fn<
[ContainerEvent & { type: "SERVICE_INIT" }],
void
>()
container.getEventStream().subscribe((ev) => {
if (ev.type === "SERVICE_INIT") {
serviceFunc(ev)
}
})
container.bind(TestServiceB)
expect(serviceFunc).toHaveBeenCalledTimes(2)
expect(serviceFunc).toHaveBeenNthCalledWith(1, <ContainerEvent>{
type: "SERVICE_INIT",
serviceID: TestServiceA.ID,
})
expect(serviceFunc).toHaveBeenNthCalledWith(2, <ContainerEvent>{
type: "SERVICE_INIT",
serviceID: TestServiceB.ID,
})
})
it("returns an observable which emits events correctly when services are bound", () => {
const container = new Container()
const serviceFunc = vi.fn<
[ContainerEvent & { type: "SERVICE_BIND" }],
void
>()
container.getEventStream().subscribe((ev) => {
if (ev.type === "SERVICE_BIND") {
serviceFunc(ev)
}
})
container.bind(TestServiceB)
expect(serviceFunc).toHaveBeenCalledTimes(2)
expect(serviceFunc).toHaveBeenNthCalledWith(1, <ContainerEvent>{
type: "SERVICE_BIND",
boundeeID: TestServiceA.ID,
bounderID: TestServiceB.ID,
})
expect(serviceFunc).toHaveBeenNthCalledWith(2, <ContainerEvent>{
type: "SERVICE_BIND",
boundeeID: TestServiceB.ID,
bounderID: undefined,
})
})
})
describe("getBoundServices", () => {
it("returns an iterator over all services bound to the container in the format [service id, service instance]", () => {
const container = new Container()
const instanceB = container.bind(TestServiceB)
const instanceA = instanceB.serviceA
expect(Array.from(container.getBoundServices())).toEqual([
[TestServiceA.ID, instanceA],
[TestServiceB.ID, instanceB],
])
})
it("returns an empty iterator if no services are bound", () => {
const container = new Container()
expect(Array.from(container.getBoundServices())).toEqual([])
})
})
})

View File

@@ -0,0 +1,66 @@
import { describe, expect, it, vi } from "vitest"
import { Service, Container } from "../lib/main"
class TestServiceA extends Service {
public static ID = "TestServiceA"
}
class TestServiceB extends Service<"test"> {
public static ID = "TestServiceB"
// Marked public to allow for testing
public readonly serviceA = this.bind(TestServiceA)
public emitTestEvent() {
this.emit("test")
}
}
describe("Service", () => {
describe("constructor", () => {
it("throws an error if the service is initialized without a container", () => {
expect(() => new TestServiceA()).toThrowError(
"Tried to initialize service with no container (ID: TestServiceA)"
)
})
})
describe("bind", () => {
it("correctly binds the dependency service using the container", () => {
const container = new Container()
const serviceA = container.bind(TestServiceA)
const serviceB = container.bind(TestServiceB)
expect(serviceB.serviceA).toBe(serviceA)
})
})
describe("getContainer", () => {
it("returns the container the service is bound to", () => {
const container = new Container()
const serviceA = container.bind(TestServiceA)
// @ts-expect-error getContainer is a protected member, we are just using it to help with testing
expect(serviceA.getContainer()).toBe(container)
})
})
describe("getEventStream", () => {
it("returns the valid event stream of the service", () => {
const container = new Container()
const serviceB = container.bind(TestServiceB)
const serviceFunc = vi.fn()
serviceB.getEventStream().subscribe(serviceFunc)
serviceB.emitTestEvent()
expect(serviceFunc).toHaveBeenCalledOnce()
expect(serviceFunc).toHaveBeenCalledWith("test")
})
})
})

View File

@@ -0,0 +1,92 @@
import { describe, expect, it, vi } from "vitest"
import { TestContainer } from "../lib/testing"
import { Service } from "../lib/service"
import { ContainerEvent } from "../lib/container"
class TestServiceA extends Service {
public static ID = "TestServiceA"
public test() {
return "real"
}
}
class TestServiceB extends Service {
public static ID = "TestServiceB"
// declared public to help with testing
public readonly serviceA = this.bind(TestServiceA)
public test() {
return this.serviceA.test()
}
}
describe("TestContainer", () => {
describe("bindMock", () => {
it("returns the fake service defined", () => {
const container = new TestContainer()
const fakeService = {
test: () => "fake",
}
const result = container.bindMock(TestServiceA, fakeService)
expect(result).toBe(fakeService)
})
it("new services bound to the container get the mock service", () => {
const container = new TestContainer()
const fakeServiceA = {
test: () => "fake",
}
container.bindMock(TestServiceA, fakeServiceA)
const serviceB = container.bind(TestServiceB)
expect(serviceB.serviceA).toBe(fakeServiceA)
})
it("container emits SERVICE_BIND event", () => {
const container = new TestContainer()
const fakeServiceA = {
test: () => "fake",
}
const serviceFunc = vi.fn<[ContainerEvent, void]>()
container.getEventStream().subscribe((ev) => {
serviceFunc(ev)
})
container.bindMock(TestServiceA, fakeServiceA)
expect(serviceFunc).toHaveBeenCalledOnce()
expect(serviceFunc).toHaveBeenCalledWith(<ContainerEvent>{
type: "SERVICE_BIND",
boundeeID: TestServiceA.ID,
bounderID: undefined,
})
})
it("throws if service already bound", () => {
const container = new TestContainer()
const fakeServiceA = {
test: () => "fake",
}
container.bindMock(TestServiceA, fakeServiceA)
expect(() => {
container.bindMock(TestServiceA, fakeServiceA)
}).toThrowError(
"Service 'TestServiceA' already bound to container. Did you already call bindMock on this ?"
)
})
})
})

2
packages/dioc/testing.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export { default } from "./dist/testing.d.ts"
export * from "./dist/testing.d.ts"

View File

@@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"lib": ["ESNext", "DOM"],
"moduleResolution": "Node",
"strict": true,
"declaration": true,
"sourceMap": true,
"outDir": "dist",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"skipLibCheck": true
},
"include": ["lib"]
}

View File

@@ -0,0 +1,16 @@
import { defineConfig } from 'vite'
export default defineConfig({
build: {
lib: {
entry: {
index: './lib/main.ts',
vue: './lib/vue.ts',
testing: './lib/testing.ts',
},
},
rollupOptions: {
external: ['vue'],
}
},
})

View File

@@ -0,0 +1,7 @@
import { defineConfig } from "vitest/config"
export default defineConfig({
test: {
}
})

2
packages/dioc/vue.d.ts vendored Normal file
View File

@@ -0,0 +1,2 @@
export { default } from "./dist/vue.d.ts"
export * from "./dist/vue.d.ts"

View File

@@ -1,6 +1,6 @@
{ {
"name": "hoppscotch-backend", "name": "hoppscotch-backend",
"version": "2023.12.3", "version": "2023.8.4-1",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,
@@ -28,14 +28,13 @@
"@nestjs-modules/mailer": "^1.9.1", "@nestjs-modules/mailer": "^1.9.1",
"@nestjs/apollo": "^12.0.9", "@nestjs/apollo": "^12.0.9",
"@nestjs/common": "^10.2.6", "@nestjs/common": "^10.2.6",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.6", "@nestjs/core": "^10.2.6",
"@nestjs/graphql": "^12.0.9", "@nestjs/graphql": "^12.0.9",
"@nestjs/jwt": "^10.1.1", "@nestjs/jwt": "^10.1.1",
"@nestjs/passport": "^10.0.2", "@nestjs/passport": "^10.0.2",
"@nestjs/platform-express": "^10.2.6", "@nestjs/platform-express": "^10.2.6",
"@nestjs/throttler": "^5.0.0", "@nestjs/throttler": "^5.0.0",
"@prisma/client": "^5.8.0", "@prisma/client": "^4.16.2",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"bcrypt": "^5.1.0", "bcrypt": "^5.1.0",
"cookie": "^0.5.0", "cookie": "^0.5.0",
@@ -57,7 +56,7 @@
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"passport-microsoft": "^1.0.0", "passport-microsoft": "^1.0.0",
"prisma": "^5.8.0", "prisma": "^4.16.2",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.6.0" "rxjs": "^7.6.0"

View File

@@ -1,14 +0,0 @@
-- CreateTable
CREATE TABLE "InfraConfig" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"value" TEXT,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdOn" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedOn" TIMESTAMP(3) NOT NULL,
CONSTRAINT "InfraConfig_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "InfraConfig_name_key" ON "InfraConfig"("name");

View File

@@ -209,12 +209,3 @@ enum TeamMemberRole {
VIEWER VIEWER
EDITOR EDITOR
} }
model InfraConfig {
id String @id @default(cuid())
name String @unique
value String?
active Boolean @default(true) // Use case: Let's say, Admin wants to disable Google SSO, but doesn't want to delete the config
createdOn DateTime @default(now()) @db.Timestamp(3)
updatedOn DateTime @updatedAt @db.Timestamp(3)
}

View File

@@ -4,6 +4,7 @@ import { AdminService } from './admin.service';
import { PrismaModule } from '../prisma/prisma.module'; import { PrismaModule } from '../prisma/prisma.module';
import { PubSubModule } from '../pubsub/pubsub.module'; import { PubSubModule } from '../pubsub/pubsub.module';
import { UserModule } from '../user/user.module'; import { UserModule } from '../user/user.module';
import { MailerModule } from '../mailer/mailer.module';
import { TeamModule } from '../team/team.module'; import { TeamModule } from '../team/team.module';
import { TeamInvitationModule } from '../team-invitation/team-invitation.module'; import { TeamInvitationModule } from '../team-invitation/team-invitation.module';
import { TeamEnvironmentsModule } from '../team-environments/team-environments.module'; import { TeamEnvironmentsModule } from '../team-environments/team-environments.module';
@@ -11,20 +12,19 @@ import { TeamCollectionModule } from '../team-collection/team-collection.module'
import { TeamRequestModule } from '../team-request/team-request.module'; import { TeamRequestModule } from '../team-request/team-request.module';
import { InfraResolver } from './infra.resolver'; import { InfraResolver } from './infra.resolver';
import { ShortcodeModule } from 'src/shortcode/shortcode.module'; import { ShortcodeModule } from 'src/shortcode/shortcode.module';
import { InfraConfigModule } from 'src/infra-config/infra-config.module';
@Module({ @Module({
imports: [ imports: [
PrismaModule, PrismaModule,
PubSubModule, PubSubModule,
UserModule, UserModule,
MailerModule,
TeamModule, TeamModule,
TeamInvitationModule, TeamInvitationModule,
TeamEnvironmentsModule, TeamEnvironmentsModule,
TeamCollectionModule, TeamCollectionModule,
TeamRequestModule, TeamRequestModule,
ShortcodeModule, ShortcodeModule,
InfraConfigModule,
], ],
providers: [InfraResolver, AdminResolver, AdminService], providers: [InfraResolver, AdminResolver, AdminService],
exports: [AdminService], exports: [AdminService],

View File

@@ -27,7 +27,9 @@ import {
} from './input-types.args'; } from './input-types.args';
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { UserDeletionResult } from 'src/user/user.model'; import { User } from 'src/user/user.model';
import { PaginationArgs } from 'src/types/input-types.args';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Admin) @Resolver(() => Admin)
@@ -47,6 +49,203 @@ export class AdminResolver {
return admin; return admin;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all admin users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async admins() {
const admins = await this.adminService.fetchAdmins();
return admins;
}
@ResolveField(() => User, {
description: 'Returns a user info by UID',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async userInfo(
@Args({
name: 'userUid',
type: () => ID,
description: 'The user UID',
})
userUid: string,
): Promise<AuthUser> {
const user = await this.adminService.fetchUserInfo(userUid);
if (E.isLeft(user)) throwErr(user.left);
return user.right;
}
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
deprecationReason: 'Use `infra` query instead',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsers(args.cursor, args.take);
return users;
}
@ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users',
deprecationReason: 'Use `infra` query instead',
})
async invitedUsers(@Parent() admin: Admin): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers();
return users;
}
@ResolveField(() => [Team], {
description: 'Returns a list of all the teams in the infra',
deprecationReason: 'Use `infra` query instead',
})
async allTeams(
@Parent() admin: Admin,
@Args() args: PaginationArgs,
): Promise<Team[]> {
const teams = await this.adminService.fetchAllTeams(args.cursor, args.take);
return teams;
}
@ResolveField(() => Team, {
description: 'Returns a team info by ID when requested by Admin',
deprecationReason: 'Use `infra` query instead',
})
async teamInfo(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which info to fetch',
})
teamID: string,
): Promise<Team> {
const team = await this.adminService.getTeamInfo(teamID);
if (E.isLeft(team)) throwErr(team.left);
return team.right;
}
@ResolveField(() => Number, {
description: 'Return count of all the members in a team',
deprecationReason: 'Use `infra` query instead',
})
async membersCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
nullable: false,
})
teamID: string,
): Promise<number> {
const teamMembersCount = await this.adminService.membersCountInTeam(teamID);
return teamMembersCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored collections in a team',
deprecationReason: 'Use `infra` query instead',
})
async collectionCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamCollCount = await this.adminService.collectionCountInTeam(teamID);
return teamCollCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored requests in a team',
deprecationReason: 'Use `infra` query instead',
})
async requestCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const teamReqCount = await this.adminService.requestCountInTeam(teamID);
return teamReqCount;
}
@ResolveField(() => Number, {
description: 'Return count of all the stored environments in a team',
deprecationReason: 'Use `infra` query instead',
})
async environmentCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
): Promise<number> {
const envsCount = await this.adminService.environmentCountInTeam(teamID);
return envsCount;
}
@ResolveField(() => [TeamInvitation], {
description: 'Return all the pending invitations in a team',
deprecationReason: 'Use `infra` query instead',
})
async pendingInvitationCountInTeam(
@Parent() admin: Admin,
@Args({
name: 'teamID',
type: () => ID,
description: 'Team ID for which team members to fetch',
})
teamID: string,
) {
const invitations = await this.adminService.pendingInvitationCountInTeam(
teamID,
);
return invitations;
}
@ResolveField(() => Number, {
description: 'Return total number of Users in organization',
deprecationReason: 'Use `infra` query instead',
})
async usersCount() {
return this.adminService.getUsersCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Teams in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamsCount() {
return this.adminService.getTeamsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Collections in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamCollectionsCount() {
return this.adminService.getTeamCollectionsCount();
}
@ResolveField(() => Number, {
description: 'Return total number of Team Requests in organization',
deprecationReason: 'Use `infra` query instead',
})
async teamRequestsCount() {
return this.adminService.getTeamRequestsCount();
}
/* Mutations */ /* Mutations */
@Mutation(() => InvitedUser, { @Mutation(() => InvitedUser, {
@@ -70,26 +269,8 @@ export class AdminResolver {
return invitedUser.right; return invitedUser.right;
} }
@Mutation(() => Boolean, {
description: 'Revoke a user invites by invitee emails',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async revokeUserInvitationsByAdmin(
@Args({
name: 'inviteeEmails',
description: 'Invitee Emails',
type: () => [String],
})
inviteeEmails: string[],
): Promise<boolean> {
const invite = await this.adminService.revokeUserInvitations(inviteeEmails);
if (E.isLeft(invite)) throwErr(invite.left);
return invite.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Delete an user account from infra', description: 'Delete an user account from infra',
deprecationReason: 'Use removeUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserByAdmin( async removeUserByAdmin(
@@ -100,33 +281,12 @@ export class AdminResolver {
}) })
userUID: string, userUID: string,
): Promise<boolean> { ): Promise<boolean> {
const removedUser = await this.adminService.removeUserAccount(userUID); const invitedUser = await this.adminService.removeUserAccount(userUID);
if (E.isLeft(removedUser)) throwErr(removedUser.left); if (E.isLeft(invitedUser)) throwErr(invitedUser.left);
return removedUser.right; return invitedUser.right;
} }
@Mutation(() => [UserDeletionResult], {
description: 'Delete user accounts from infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<UserDeletionResult[]> {
const deletionResults = await this.adminService.removeUserAccounts(
userUIDs,
);
if (E.isLeft(deletionResults)) throwErr(deletionResults.left);
return deletionResults.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Make user an admin', description: 'Make user an admin',
deprecationReason: 'Use makeUsersAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUserAdmin( async makeUserAdmin(
@@ -142,51 +302,8 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Make users an admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async makeUsersAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.makeUsersAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, {
description: 'Update user display name',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async updateUserDisplayNameByAdmin(
@Args({
name: 'userUID',
description: 'users UID',
type: () => ID,
})
userUID: string,
@Args({
name: 'displayName',
description: 'users display name',
})
displayName: string,
): Promise<boolean> {
const isUpdated = await this.adminService.updateUserDisplayName(
userUID,
displayName,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Boolean, { @Mutation(() => Boolean, {
description: 'Remove user as admin', description: 'Remove user as admin',
deprecationReason: 'Use demoteUsersByAdmin instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async removeUserAsAdmin( async removeUserAsAdmin(
@@ -202,23 +319,6 @@ export class AdminResolver {
return admin.right; return admin.right;
} }
@Mutation(() => Boolean, {
description: 'Remove users as admin',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async demoteUsersByAdmin(
@Args({
name: 'userUIDs',
description: 'users UID',
type: () => [ID],
})
userUIDs: string[],
): Promise<boolean> {
const isUpdated = await this.adminService.demoteUsersByAdmin(userUIDs);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return isUpdated.right;
}
@Mutation(() => Team, { @Mutation(() => Team, {
description: description:
'Create a new team by providing the user uid to nominate as Team owner', 'Create a new team by providing the user uid to nominate as Team owner',

View File

@@ -1,7 +1,7 @@
import { AdminService } from './admin.service'; import { AdminService } from './admin.service';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { mockDeep } from 'jest-mock-extended'; import { mockDeep } from 'jest-mock-extended';
import { InvitedUsers, User as DbUser } from '@prisma/client'; import { InvitedUsers } from '@prisma/client';
import { UserService } from '../user/user.service'; import { UserService } from '../user/user.service';
import { TeamService } from '../team/team.service'; import { TeamService } from '../team/team.service';
import { TeamEnvironmentsService } from '../team-environments/team-environments.service'; import { TeamEnvironmentsService } from '../team-environments/team-environments.service';
@@ -13,15 +13,9 @@ import { PrismaService } from 'src/prisma/prisma.service';
import { import {
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_NOT_FOUND,
} from '../errors'; } from '../errors';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import * as E from 'fp-ts/Either';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockPubSub = mockDeep<PubSubService>(); const mockPubSub = mockDeep<PubSubService>();
@@ -33,7 +27,6 @@ const mockTeamInvitationService = mockDeep<TeamInvitationService>();
const mockTeamCollectionService = mockDeep<TeamCollectionService>(); const mockTeamCollectionService = mockDeep<TeamCollectionService>();
const mockMailerService = mockDeep<MailerService>(); const mockMailerService = mockDeep<MailerService>();
const mockShortcodeService = mockDeep<ShortcodeService>(); const mockShortcodeService = mockDeep<ShortcodeService>();
const mockConfigService = mockDeep<ConfigService>();
const adminService = new AdminService( const adminService = new AdminService(
mockUserService, mockUserService,
@@ -46,7 +39,6 @@ const adminService = new AdminService(
mockPrisma as any, mockPrisma as any,
mockMailerService, mockMailerService,
mockShortcodeService, mockShortcodeService,
mockConfigService,
); );
const invitedUsers: InvitedUsers[] = [ const invitedUsers: InvitedUsers[] = [
@@ -63,87 +55,20 @@ const invitedUsers: InvitedUsers[] = [
invitedOn: new Date(), invitedOn: new Date(),
}, },
]; ];
const dbAdminUsers: DbUser[] = [
{
uid: 'uid 1',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
},
{
uid: 'uid 2',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: true,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
},
];
const dbNonAminUser: DbUser = {
uid: 'uid 3',
displayName: 'displayName',
email: 'email@email.com',
photoURL: 'photoURL',
isAdmin: false,
refreshToken: 'refreshToken',
currentRESTSession: '',
currentGQLSession: '',
createdOn: new Date(),
};
describe('AdminService', () => { describe('AdminService', () => {
describe('fetchInvitedUsers', () => { describe('fetchInvitedUsers', () => {
test('should resolve right and apply pagination correctly', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const paginationArgs: OffsetPaginationArgs = { take: 5, skip: 2 };
const results = await adminService.fetchInvitedUsers(paginationArgs);
expect(mockPrisma.invitedUsers.findMany).toHaveBeenCalledWith({
...paginationArgs,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: [dbAdminUsers[0].email],
},
},
},
});
});
test('should resolve right and return an array of invited users', async () => { test('should resolve right and return an array of invited users', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
mockPrisma.user.findMany.mockResolvedValue([dbAdminUsers[0]]);
// @ts-ignore
mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers); mockPrisma.invitedUsers.findMany.mockResolvedValue(invitedUsers);
const results = await adminService.fetchInvitedUsers(paginationArgs); const results = await adminService.fetchInvitedUsers();
expect(results).toEqual(invitedUsers); expect(results).toEqual(invitedUsers);
}); });
test('should resolve left and return an empty array if invited users not found', async () => { test('should resolve left and return an empty array if invited users not found', async () => {
const paginationArgs: OffsetPaginationArgs = { take: 10, skip: 0 };
mockPrisma.invitedUsers.findMany.mockResolvedValue([]); mockPrisma.invitedUsers.findMany.mockResolvedValue([]);
const results = await adminService.fetchInvitedUsers(paginationArgs); const results = await adminService.fetchInvitedUsers();
expect(results).toEqual([]); expect(results).toEqual([]);
}); });
}); });
@@ -206,58 +131,6 @@ describe('AdminService', () => {
}); });
}); });
describe('revokeUserInvitations', () => {
test('should resolve left and return error if email not invited', async () => {
mockPrisma.invitedUsers.deleteMany.mockRejectedValueOnce(
'RecordNotFound',
);
const result = await adminService.revokeUserInvitations([
'test@gmail.com',
]);
expect(result).toEqualLeft(USER_INVITATION_DELETION_FAILED);
});
test('should resolve right and return deleted invitee email', async () => {
const adminUid = 'adminUid';
mockPrisma.invitedUsers.deleteMany.mockResolvedValueOnce({ count: 1 });
const result = await adminService.revokeUserInvitations([
invitedUsers[0].inviteeEmail,
]);
expect(mockPrisma.invitedUsers.deleteMany).toHaveBeenCalledWith({
where: {
inviteeEmail: { in: [invitedUsers[0].inviteeEmail] },
},
});
expect(result).toEqualRight(true);
});
});
describe('removeUsersAsAdmin', () => {
test('should resolve right and make admins to users', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin([dbAdminUsers[0].uid]),
).toEqualRight(true);
});
test('should resolve left and return error if only one admin in the infra', async () => {
mockUserService.fetchAdminUsers.mockResolvedValueOnce(dbAdminUsers);
mockUserService.removeUsersAsAdmin.mockResolvedValueOnce(E.right(true));
return expect(
await adminService.demoteUsersByAdmin(
dbAdminUsers.map((user) => user.uid),
),
).toEqualLeft(ONLY_ONE_ADMIN_ACCOUNT);
});
});
describe('getUsersCount', () => { describe('getUsersCount', () => {
test('should return count of all users in the organization', async () => { test('should return count of all users in the organization', async () => {
mockUserService.getUsersCount.mockResolvedValueOnce(10); mockUserService.getUsersCount.mockResolvedValueOnce(10);

View File

@@ -6,16 +6,13 @@ import * as E from 'fp-ts/Either';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { validateEmail } from '../utils'; import { validateEmail } from '../utils';
import { import {
ADMIN_CAN_NOT_BE_DELETED,
DUPLICATE_EMAIL, DUPLICATE_EMAIL,
EMAIL_FAILED, EMAIL_FAILED,
INVALID_EMAIL, INVALID_EMAIL,
ONLY_ONE_ADMIN_ACCOUNT, ONLY_ONE_ADMIN_ACCOUNT,
TEAM_INVITE_ALREADY_MEMBER, TEAM_INVITE_ALREADY_MEMBER,
TEAM_INVITE_NO_INVITE_FOUND, TEAM_INVITE_NO_INVITE_FOUND,
USERS_NOT_FOUND,
USER_ALREADY_INVITED, USER_ALREADY_INVITED,
USER_INVITATION_DELETION_FAILED,
USER_IS_ADMIN, USER_IS_ADMIN,
USER_NOT_FOUND, USER_NOT_FOUND,
} from '../errors'; } from '../errors';
@@ -28,9 +25,6 @@ import { TeamEnvironmentsService } from '../team-environments/team-environments.
import { TeamInvitationService } from '../team-invitation/team-invitation.service'; import { TeamInvitationService } from '../team-invitation/team-invitation.service';
import { TeamMemberRole } from '../team/team.model'; import { TeamMemberRole } from '../team/team.model';
import { ShortcodeService } from 'src/shortcode/shortcode.service'; import { ShortcodeService } from 'src/shortcode/shortcode.service';
import { ConfigService } from '@nestjs/config';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
import { UserDeletionResult } from 'src/user/user.model';
@Injectable() @Injectable()
export class AdminService { export class AdminService {
@@ -45,7 +39,6 @@ export class AdminService {
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly mailerService: MailerService, private readonly mailerService: MailerService,
private readonly shortcodeService: ShortcodeService, private readonly shortcodeService: ShortcodeService,
private readonly configService: ConfigService,
) {} ) {}
/** /**
@@ -53,30 +46,12 @@ export class AdminService {
* @param cursorID Users uid * @param cursorID Users uid
* @param take number of users to fetch * @param take number of users to fetch
* @returns an Either of array of user or error * @returns an Either of array of user or error
* @deprecated use fetchUsersV2 instead
*/ */
async fetchUsers(cursorID: string, take: number) { async fetchUsers(cursorID: string, take: number) {
const allUsers = await this.userService.fetchAllUsers(cursorID, take); const allUsers = await this.userService.fetchAllUsers(cursorID, take);
return allUsers; return allUsers;
} }
/**
* Fetch all the users in the infra.
* @param searchString search on users displayName or email
* @param paginationOption pagination options
* @returns an Either of array of user or error
*/
async fetchUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const allUsers = await this.userService.fetchAllUsersV2(
searchString,
paginationOption,
);
return allUsers;
}
/** /**
* Invite a user to join the infra. * Invite a user to join the infra.
* @param adminUID Admin's UID * @param adminUID Admin's UID
@@ -104,7 +79,7 @@ export class AdminService {
template: 'user-invitation', template: 'user-invitation',
variables: { variables: {
inviteeEmail: inviteeEmail, inviteeEmail: inviteeEmail,
magicLink: `${this.configService.get('VITE_BASE_URL')}`, magicLink: `${process.env.VITE_BASE_URL}`,
}, },
}); });
} catch (e) { } catch (e) {
@@ -133,68 +108,14 @@ export class AdminService {
return E.right(invitedUser); return E.right(invitedUser);
} }
/**
* Update the display name of a user
* @param userUid Who's display name is being updated
* @param displayName New display name of the user
* @returns an Either of boolean or error
*/
async updateUserDisplayName(userUid: string, displayName: string) {
const updatedUser = await this.userService.updateUserDisplayName(
userUid,
displayName,
);
if (E.isLeft(updatedUser)) return E.left(updatedUser.left);
return E.right(true);
}
/**
* Revoke infra level user invitations
* @param inviteeEmails Invitee's emails
* @param adminUid Admin Uid
* @returns an Either of boolean or error string
*/
async revokeUserInvitations(inviteeEmails: string[]) {
try {
await this.prisma.invitedUsers.deleteMany({
where: {
inviteeEmail: { in: inviteeEmails },
},
});
return E.right(true);
} catch (error) {
return E.left(USER_INVITATION_DELETION_FAILED);
}
}
/** /**
* Fetch the list of invited users by the admin. * Fetch the list of invited users by the admin.
* @returns an Either of array of `InvitedUser` object or error * @returns an Either of array of `InvitedUser` object or error
*/ */
async fetchInvitedUsers(paginationOption: OffsetPaginationArgs) { async fetchInvitedUsers() {
const userEmailObjs = await this.prisma.user.findMany({ const invitedUsers = await this.prisma.invitedUsers.findMany();
select: {
email: true,
},
});
const pendingInvitedUsers = await this.prisma.invitedUsers.findMany({ const users: InvitedUser[] = invitedUsers.map(
take: paginationOption.take,
skip: paginationOption.skip,
orderBy: {
invitedOn: 'desc',
},
where: {
NOT: {
inviteeEmail: {
in: userEmailObjs.map((user) => user.email),
},
},
},
});
const users: InvitedUser[] = pendingInvitedUsers.map(
(user) => <InvitedUser>{ ...user }, (user) => <InvitedUser>{ ...user },
); );
@@ -414,7 +335,6 @@ export class AdminService {
* Remove a user account by UID * Remove a user account by UID
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use removeUserAccounts instead
*/ */
async removeUserAccount(userUid: string) { async removeUserAccount(userUid: string) {
const user = await this.userService.findUserById(userUid); const user = await this.userService.findUserById(userUid);
@@ -427,73 +347,10 @@ export class AdminService {
return E.right(delUser.right); return E.right(delUser.right);
} }
/**
* Remove user (not Admin) accounts by UIDs
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async removeUserAccounts(userUIDs: string[]) {
const userDeleteResult: UserDeletionResult[] = [];
// step 1: fetch all users
const allUsersList = await this.userService.findUsersByIds(userUIDs);
if (allUsersList.length === 0) return E.left(USERS_NOT_FOUND);
// step 2: admin user can not be deleted without removing admin status/role
allUsersList.forEach((user) => {
if (user.isAdmin) {
userDeleteResult.push({
userUID: user.uid,
isDeleted: false,
errorMessage: ADMIN_CAN_NOT_BE_DELETED,
});
}
});
const nonAdminUsers = allUsersList.filter((user) => !user.isAdmin);
let deletedUserEmails: string[] = [];
// step 3: delete non-admin users
const deletionPromises = nonAdminUsers.map((user) => {
return this.userService
.deleteUserByUID(user)()
.then((res) => {
if (E.isLeft(res)) {
return {
userUID: user.uid,
isDeleted: false,
errorMessage: res.left,
} as UserDeletionResult;
}
deletedUserEmails.push(user.email);
return {
userUID: user.uid,
isDeleted: true,
errorMessage: null,
} as UserDeletionResult;
});
});
const promiseResult = await Promise.allSettled(deletionPromises);
// step 4: revoke all the invites sent to the deleted users
await this.revokeUserInvitations(deletedUserEmails);
// step 5: return the result
promiseResult.forEach((result) => {
if (result.status === 'fulfilled') {
userDeleteResult.push(result.value);
}
});
return E.right(userDeleteResult);
}
/** /**
* Make a user an admin * Make a user an admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use makeUsersAdmin instead
*/ */
async makeUserAdmin(userUID: string) { async makeUserAdmin(userUID: string) {
const admin = await this.userService.makeAdmin(userUID); const admin = await this.userService.makeAdmin(userUID);
@@ -501,22 +358,10 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Make users to admin
* @param userUid User UIDs
* @returns an Either of boolean or error
*/
async makeUsersAdmin(userUIDs: string[]) {
const isUpdated = await this.userService.makeAdmins(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(true);
}
/** /**
* Remove user as admin * Remove user as admin
* @param userUid User UID * @param userUid User UID
* @returns an Either of boolean or error * @returns an Either of boolean or error
* @deprecated use demoteUsersByAdmin instead
*/ */
async removeUserAsAdmin(userUID: string) { async removeUserAsAdmin(userUID: string) {
const adminUsers = await this.userService.fetchAdminUsers(); const adminUsers = await this.userService.fetchAdminUsers();
@@ -527,26 +372,6 @@ export class AdminService {
return E.right(true); return E.right(true);
} }
/**
* Remove users as admin
* @param userUIDs User UIDs
* @returns an Either of boolean or error
*/
async demoteUsersByAdmin(userUIDs: string[]) {
const adminUsers = await this.userService.fetchAdminUsers();
const remainingAdmins = adminUsers.filter(
(adminUser) => !userUIDs.includes(adminUser.uid),
);
if (remainingAdmins.length < 1) {
return E.left(ONLY_ONE_ADMIN_ACCOUNT);
}
const isUpdated = await this.userService.removeUsersAsAdmin(userUIDs);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(isUpdated.right);
}
/** /**
* Fetch list of all the Users in org * Fetch list of all the Users in org
* @returns number of users in the org * @returns number of users in the org

View File

@@ -1,11 +0,0 @@
import { Injectable, ExecutionContext, CanActivate } from '@nestjs/common';
@Injectable()
export class RESTAdminGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const user = request.user;
return user.isAdmin;
}
}

View File

@@ -1,12 +1,5 @@
import { UseGuards } from '@nestjs/common'; import { UseGuards } from '@nestjs/common';
import { import { Args, ID, Query, ResolveField, Resolver } from '@nestjs/graphql';
Args,
ID,
Mutation,
Query,
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard'; import { GqlThrottlerGuard } from 'src/guards/gql-throttler.guard';
import { Infra } from './infra.model'; import { Infra } from './infra.model';
import { AdminService } from './admin.service'; import { AdminService } from './admin.service';
@@ -17,30 +10,17 @@ import { AuthUser } from 'src/types/AuthUser';
import { throwErr } from 'src/utils'; import { throwErr } from 'src/utils';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { Admin } from './admin.model'; import { Admin } from './admin.model';
import { import { PaginationArgs } from 'src/types/input-types.args';
OffsetPaginationArgs,
PaginationArgs,
} from 'src/types/input-types.args';
import { InvitedUser } from './invited-user.model'; import { InvitedUser } from './invited-user.model';
import { Team } from 'src/team/team.model'; import { Team } from 'src/team/team.model';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
import { GqlAdmin } from './decorators/gql-admin.decorator'; import { GqlAdmin } from './decorators/gql-admin.decorator';
import { ShortcodeWithUserEmail } from 'src/shortcode/shortcode.model'; import { ShortcodeWithUserEmail } from 'src/shortcode/shortcode.model';
import { InfraConfig } from 'src/infra-config/infra-config.model';
import { InfraConfigService } from 'src/infra-config/infra-config.service';
import {
EnableAndDisableSSOArgs,
InfraConfigArgs,
} from 'src/infra-config/input-args';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
@UseGuards(GqlThrottlerGuard) @UseGuards(GqlThrottlerGuard)
@Resolver(() => Infra) @Resolver(() => Infra)
export class InfraResolver { export class InfraResolver {
constructor( constructor(private adminService: AdminService) {}
private adminService: AdminService,
private infraConfigService: InfraConfigService,
) {}
@Query(() => Infra, { @Query(() => Infra, {
description: 'Fetch details of the Infrastructure', description: 'Fetch details of the Infrastructure',
@@ -79,7 +59,6 @@ export class InfraResolver {
@ResolveField(() => [User], { @ResolveField(() => [User], {
description: 'Returns a list of all the users in infra', description: 'Returns a list of all the users in infra',
deprecationReason: 'Use allUsersV2 instead',
}) })
@UseGuards(GqlAuthGuard, GqlAdminGuard) @UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> { async allUsers(@Args() args: PaginationArgs): Promise<AuthUser[]> {
@@ -87,33 +66,11 @@ export class InfraResolver {
return users; return users;
} }
@ResolveField(() => [User], {
description: 'Returns a list of all the users in infra',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async allUsersV2(
@Args({
name: 'searchString',
nullable: true,
description: 'Search on users displayName or email',
})
searchString: string,
@Args() paginationOption: OffsetPaginationArgs,
): Promise<AuthUser[]> {
const users = await this.adminService.fetchUsersV2(
searchString,
paginationOption,
);
return users;
}
@ResolveField(() => [InvitedUser], { @ResolveField(() => [InvitedUser], {
description: 'Returns a list of all the invited users', description: 'Returns a list of all the invited users',
}) })
async invitedUsers( async invitedUsers(): Promise<InvitedUser[]> {
@Args() args: OffsetPaginationArgs, const users = await this.adminService.fetchInvitedUsers();
): Promise<InvitedUser[]> {
const users = await this.adminService.fetchInvitedUsers(args);
return users; return users;
} }
@@ -265,78 +222,4 @@ export class InfraResolver {
userEmail, userEmail,
); );
} }
@Query(() => [InfraConfig], {
description: 'Retrieve configuration details for the instance',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async infraConfigs(
@Args({
name: 'configNames',
type: () => [InfraConfigEnumForClient],
description: 'Configs to fetch',
})
names: InfraConfigEnumForClient[],
) {
const infraConfigs = await this.infraConfigService.getMany(names);
if (E.isLeft(infraConfigs)) throwErr(infraConfigs.left);
return infraConfigs.right;
}
@Query(() => [String], {
description: 'Allowed Auth Provider list',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
allowedAuthProviders() {
return this.infraConfigService.getAllowedAuthProviders();
}
/* Mutations */
@Mutation(() => [InfraConfig], {
description: 'Update Infra Configs',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async updateInfraConfigs(
@Args({
name: 'infraConfigs',
type: () => [InfraConfigArgs],
description: 'InfraConfigs to update',
})
infraConfigs: InfraConfigArgs[],
) {
const updatedRes = await this.infraConfigService.updateMany(infraConfigs);
if (E.isLeft(updatedRes)) throwErr(updatedRes.left);
return updatedRes.right;
}
@Mutation(() => Boolean, {
description: 'Reset Infra Configs with default values (.env)',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async resetInfraConfigs() {
const resetRes = await this.infraConfigService.reset();
if (E.isLeft(resetRes)) throwErr(resetRes.left);
return true;
}
@Mutation(() => Boolean, {
description: 'Enable or Disable SSO for login/signup',
})
@UseGuards(GqlAuthGuard, GqlAdminGuard)
async enableAndDisableSSO(
@Args({
name: 'providerInfo',
type: () => [EnableAndDisableSSOArgs],
description: 'SSO provider and status',
})
providerInfo: EnableAndDisableSSOArgs[],
) {
const isUpdated = await this.infraConfigService.enableAndDisableSSO(
providerInfo,
);
if (E.isLeft(isUpdated)) throwErr(isUpdated.left);
return true;
}
} }

View File

@@ -20,69 +20,51 @@ import { ShortcodeModule } from './shortcode/shortcode.module';
import { COOKIES_NOT_FOUND } from './errors'; import { COOKIES_NOT_FOUND } from './errors';
import { ThrottlerModule } from '@nestjs/throttler'; import { ThrottlerModule } from '@nestjs/throttler';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { InfraConfigModule } from './infra-config/infra-config.module';
import { loadInfraConfiguration } from './infra-config/helper';
import { MailerModule } from './mailer/mailer.module';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ GraphQLModule.forRoot<ApolloDriverConfig>({
isGlobal: true, buildSchemaOptions: {
load: [async () => loadInfraConfiguration()], numberScalarMode: 'integer',
}),
GraphQLModule.forRootAsync<ApolloDriverConfig>({
driver: ApolloDriver,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
return {
buildSchemaOptions: {
numberScalarMode: 'integer',
},
playground: configService.get('PRODUCTION') !== 'true',
autoSchemaFile: true,
installSubscriptionHandlers: true,
subscriptions: {
'subscriptions-transport-ws': {
path: '/graphql',
onConnect: (_, websocket) => {
try {
const cookies = subscriptionContextCookieParser(
websocket.upgradeReq.headers.cookie,
);
return {
headers: { ...websocket?.upgradeReq?.headers, cookies },
};
} catch (error) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
},
},
},
context: ({ req, res, connection }) => ({
req,
res,
connection,
}),
};
}, },
}), playground: process.env.PRODUCTION !== 'true',
ThrottlerModule.forRootAsync({ autoSchemaFile: true,
imports: [ConfigModule], installSubscriptionHandlers: true,
inject: [ConfigService], subscriptions: {
useFactory: async (configService: ConfigService) => [ 'subscriptions-transport-ws': {
{ path: '/graphql',
ttl: +configService.get('RATE_LIMIT_TTL'), onConnect: (_, websocket) => {
limit: +configService.get('RATE_LIMIT_MAX'), try {
const cookies = subscriptionContextCookieParser(
websocket.upgradeReq.headers.cookie,
);
return {
headers: { ...websocket?.upgradeReq?.headers, cookies },
};
} catch (error) {
throw new HttpException(COOKIES_NOT_FOUND, 400, {
cause: new Error(COOKIES_NOT_FOUND),
});
}
},
}, },
], },
context: ({ req, res, connection }) => ({
req,
res,
connection,
}),
driver: ApolloDriver,
}), }),
MailerModule.register(), ThrottlerModule.forRoot([
{
ttl: +process.env.RATE_LIMIT_TTL,
limit: +process.env.RATE_LIMIT_MAX,
},
]),
UserModule, UserModule,
AuthModule.register(), AuthModule,
AdminModule, AdminModule,
UserSettingsModule, UserSettingsModule,
UserEnvironmentsModule, UserEnvironmentsModule,
@@ -95,7 +77,6 @@ import { MailerModule } from './mailer/mailer.module';
TeamInvitationModule, TeamInvitationModule,
UserCollectionModule, UserCollectionModule,
ShortcodeModule, ShortcodeModule,
InfraConfigModule,
], ],
providers: [GQLComplexityPlugin], providers: [GQLComplexityPlugin],
controllers: [AppController], controllers: [AppController],

View File

@@ -2,6 +2,7 @@ import {
Body, Body,
Controller, Controller,
Get, Get,
InternalServerErrorException,
Post, Post,
Query, Query,
Request, Request,
@@ -30,21 +31,11 @@ import { MicrosoftSSOGuard } from './guards/microsoft-sso-.guard';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard'; import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { SkipThrottle } from '@nestjs/throttler'; import { SkipThrottle } from '@nestjs/throttler';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config';
@UseGuards(ThrottlerBehindProxyGuard) @UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'auth', version: '1' }) @Controller({ path: 'auth', version: '1' })
export class AuthController { export class AuthController {
constructor( constructor(private authService: AuthService) {}
private authService: AuthService,
private configService: ConfigService,
) {}
@Get('providers')
async getAuthProviders() {
const providers = await this.authService.getAuthProviders();
return { providers };
}
/** /**
** Route to initiate magic-link auth for a users email ** Route to initiate magic-link auth for a users email
@@ -54,14 +45,8 @@ export class AuthController {
@Body() authData: SignInMagicDto, @Body() authData: SignInMagicDto,
@Query('origin') origin: string, @Query('origin') origin: string,
) { ) {
if ( if (!authProviderCheck(AuthProvider.EMAIL))
!authProviderCheck(
AuthProvider.EMAIL,
this.configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS'),
)
) {
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 }); throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
}
const deviceIdToken = await this.authService.signInMagicLink( const deviceIdToken = await this.authService.signInMagicLink(
authData.email, authData.email,

View File

@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { AuthController } from './auth.controller'; import { AuthController } from './auth.controller';
import { UserModule } from 'src/user/user.module'; import { UserModule } from 'src/user/user.module';
import { MailerModule } from 'src/mailer/mailer.module';
import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaModule } from 'src/prisma/prisma.module';
import { PassportModule } from '@nestjs/passport'; import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt'; import { JwtModule } from '@nestjs/jwt';
@@ -11,47 +12,25 @@ import { GoogleStrategy } from './strategies/google.strategy';
import { GithubStrategy } from './strategies/github.strategy'; import { GithubStrategy } from './strategies/github.strategy';
import { MicrosoftStrategy } from './strategies/microsoft.strategy'; import { MicrosoftStrategy } from './strategies/microsoft.strategy';
import { AuthProvider, authProviderCheck } from './helper'; import { AuthProvider, authProviderCheck } from './helper';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { loadInfraConfiguration } from 'src/infra-config/helper';
import { InfraConfigModule } from 'src/infra-config/infra-config.module';
@Module({ @Module({
imports: [ imports: [
PrismaModule, PrismaModule,
UserModule, UserModule,
MailerModule,
PassportModule, PassportModule,
JwtModule.registerAsync({ JwtModule.register({
imports: [ConfigModule], secret: process.env.JWT_SECRET,
inject: [ConfigService],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT_SECRET'),
}),
}), }),
InfraConfigModule,
], ],
providers: [AuthService, JwtStrategy, RTJwtStrategy], providers: [
AuthService,
JwtStrategy,
RTJwtStrategy,
...(authProviderCheck(AuthProvider.GOOGLE) ? [GoogleStrategy] : []),
...(authProviderCheck(AuthProvider.GITHUB) ? [GithubStrategy] : []),
...(authProviderCheck(AuthProvider.MICROSOFT) ? [MicrosoftStrategy] : []),
],
controllers: [AuthController], controllers: [AuthController],
}) })
export class AuthModule { export class AuthModule {}
static async register() {
const env = await loadInfraConfiguration();
const allowedAuthProviders = env.INFRA.VITE_ALLOWED_AUTH_PROVIDERS;
const providers = [
...(authProviderCheck(AuthProvider.GOOGLE, allowedAuthProviders)
? [GoogleStrategy]
: []),
...(authProviderCheck(AuthProvider.GITHUB, allowedAuthProviders)
? [GithubStrategy]
: []),
...(authProviderCheck(AuthProvider.MICROSOFT, allowedAuthProviders)
? [MicrosoftStrategy]
: []),
];
return {
module: AuthModule,
providers,
};
}
}

View File

@@ -21,26 +21,15 @@ import { VerifyMagicDto } from './dto/verify-magic.dto';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import * as argon2 from 'argon2'; import * as argon2 from 'argon2';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
import { InfraConfigService } from 'src/infra-config/infra-config.service';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockUser = mockDeep<UserService>(); const mockUser = mockDeep<UserService>();
const mockJWT = mockDeep<JwtService>(); const mockJWT = mockDeep<JwtService>();
const mockMailer = mockDeep<MailerService>(); const mockMailer = mockDeep<MailerService>();
const mockConfigService = mockDeep<ConfigService>();
const mockInfraConfigService = mockDeep<InfraConfigService>();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
const authService = new AuthService( const authService = new AuthService(mockUser, mockPrisma, mockJWT, mockMailer);
mockUser,
mockPrisma,
mockJWT,
mockMailer,
mockConfigService,
mockInfraConfigService,
);
const currentTime = new Date(); const currentTime = new Date();
@@ -102,8 +91,6 @@ describe('signInMagicLink', () => {
mockUser.createUserViaMagicLink.mockResolvedValue(user); mockUser.createUserViaMagicLink.mockResolvedValue(user);
// create new entry in VerificationToken table // create new entry in VerificationToken table
mockPrisma.verificationToken.create.mockResolvedValueOnce(passwordlessData); mockPrisma.verificationToken.create.mockResolvedValueOnce(passwordlessData);
// Read env variable 'MAGIC_LINK_TOKEN_VALIDITY' from config service
mockConfigService.get.mockReturnValue('3');
const result = await authService.signInMagicLink( const result = await authService.signInMagicLink(
'dwight@dundermifflin.com', 'dwight@dundermifflin.com',

View File

@@ -28,8 +28,6 @@ import { AuthError } from 'src/types/AuthError';
import { AuthUser, IsAdmin } from 'src/types/AuthUser'; import { AuthUser, IsAdmin } from 'src/types/AuthUser';
import { VerificationToken } from '@prisma/client'; import { VerificationToken } from '@prisma/client';
import { Origin } from './helper'; import { Origin } from './helper';
import { ConfigService } from '@nestjs/config';
import { InfraConfigService } from 'src/infra-config/infra-config.service';
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@@ -38,8 +36,6 @@ export class AuthService {
private prismaService: PrismaService, private prismaService: PrismaService,
private jwtService: JwtService, private jwtService: JwtService,
private readonly mailerService: MailerService, private readonly mailerService: MailerService,
private readonly configService: ConfigService,
private infraConfigService: InfraConfigService,
) {} ) {}
/** /**
@@ -50,12 +46,10 @@ export class AuthService {
*/ */
private async generateMagicLinkTokens(user: AuthUser) { private async generateMagicLinkTokens(user: AuthUser) {
const salt = await bcrypt.genSalt( const salt = await bcrypt.genSalt(
parseInt(this.configService.get('TOKEN_SALT_COMPLEXITY')), parseInt(process.env.TOKEN_SALT_COMPLEXITY),
); );
const expiresOn = DateTime.now() const expiresOn = DateTime.now()
.plus({ .plus({ hours: parseInt(process.env.MAGIC_LINK_TOKEN_VALIDITY) })
hours: parseInt(this.configService.get('MAGIC_LINK_TOKEN_VALIDITY')),
})
.toISO() .toISO()
.toString(); .toString();
@@ -101,13 +95,13 @@ export class AuthService {
*/ */
private async generateRefreshToken(userUid: string) { private async generateRefreshToken(userUid: string) {
const refreshTokenPayload: RefreshTokenPayload = { const refreshTokenPayload: RefreshTokenPayload = {
iss: this.configService.get('VITE_BASE_URL'), iss: process.env.VITE_BASE_URL,
sub: userUid, sub: userUid,
aud: [this.configService.get('VITE_BASE_URL')], aud: [process.env.VITE_BASE_URL],
}; };
const refreshToken = await this.jwtService.sign(refreshTokenPayload, { const refreshToken = await this.jwtService.sign(refreshTokenPayload, {
expiresIn: this.configService.get('REFRESH_TOKEN_VALIDITY'), //7 Days expiresIn: process.env.REFRESH_TOKEN_VALIDITY, //7 Days
}); });
const refreshTokenHash = await argon2.hash(refreshToken); const refreshTokenHash = await argon2.hash(refreshToken);
@@ -133,9 +127,9 @@ export class AuthService {
*/ */
async generateAuthTokens(userUid: string) { async generateAuthTokens(userUid: string) {
const accessTokenPayload: AccessTokenPayload = { const accessTokenPayload: AccessTokenPayload = {
iss: this.configService.get('VITE_BASE_URL'), iss: process.env.VITE_BASE_URL,
sub: userUid, sub: userUid,
aud: [this.configService.get('VITE_BASE_URL')], aud: [process.env.VITE_BASE_URL],
}; };
const refreshToken = await this.generateRefreshToken(userUid); const refreshToken = await this.generateRefreshToken(userUid);
@@ -143,7 +137,7 @@ export class AuthService {
return E.right(<AuthTokens>{ return E.right(<AuthTokens>{
access_token: await this.jwtService.sign(accessTokenPayload, { access_token: await this.jwtService.sign(accessTokenPayload, {
expiresIn: this.configService.get('ACCESS_TOKEN_VALIDITY'), //1 Day expiresIn: process.env.ACCESS_TOKEN_VALIDITY, //1 Day
}), }),
refresh_token: refreshToken.right, refresh_token: refreshToken.right,
}); });
@@ -224,14 +218,14 @@ export class AuthService {
let url: string; let url: string;
switch (origin) { switch (origin) {
case Origin.ADMIN: case Origin.ADMIN:
url = this.configService.get('VITE_ADMIN_URL'); url = process.env.VITE_ADMIN_URL;
break; break;
case Origin.APP: case Origin.APP:
url = this.configService.get('VITE_BASE_URL'); url = process.env.VITE_BASE_URL;
break; break;
default: default:
// if origin is invalid by default set URL to Hoppscotch-App // if origin is invalid by default set URL to Hoppscotch-App
url = this.configService.get('VITE_BASE_URL'); url = process.env.VITE_BASE_URL;
} }
await this.mailerService.sendEmail(email, { await this.mailerService.sendEmail(email, {
@@ -383,8 +377,4 @@ export class AuthService {
return E.right(<IsAdmin>{ isAdmin: false }); return E.right(<IsAdmin>{ isAdmin: false });
} }
getAuthProviders() {
return this.infraConfigService.getAllowedAuthProviders();
}
} }

View File

@@ -3,25 +3,14 @@ import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class GithubSSOGuard extends AuthGuard('github') implements CanActivate { export class GithubSSOGuard extends AuthGuard('github') implements CanActivate {
constructor(private readonly configService: ConfigService) {
super();
}
canActivate( canActivate(
context: ExecutionContext, context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> { ): boolean | Promise<boolean> | Observable<boolean> {
if ( if (!authProviderCheck(AuthProvider.GITHUB))
!authProviderCheck(
AuthProvider.GITHUB,
this.configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS'),
)
) {
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 }); throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
}
return super.canActivate(context); return super.canActivate(context);
} }

View File

@@ -3,25 +3,14 @@ import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate { export class GoogleSSOGuard extends AuthGuard('google') implements CanActivate {
constructor(private readonly configService: ConfigService) {
super();
}
canActivate( canActivate(
context: ExecutionContext, context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> { ): boolean | Promise<boolean> | Observable<boolean> {
if ( if (!authProviderCheck(AuthProvider.GOOGLE))
!authProviderCheck(
AuthProvider.GOOGLE,
this.configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS'),
)
) {
throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 }); throwHTTPErr({ message: AUTH_PROVIDER_NOT_SPECIFIED, statusCode: 404 });
}
return super.canActivate(context); return super.canActivate(context);
} }

View File

@@ -3,31 +3,20 @@ import { AuthGuard } from '@nestjs/passport';
import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper'; import { AuthProvider, authProviderCheck, throwHTTPErr } from '../helper';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED } from 'src/errors';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class MicrosoftSSOGuard export class MicrosoftSSOGuard
extends AuthGuard('microsoft') extends AuthGuard('microsoft')
implements CanActivate implements CanActivate
{ {
constructor(private readonly configService: ConfigService) {
super();
}
canActivate( canActivate(
context: ExecutionContext, context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> { ): boolean | Promise<boolean> | Observable<boolean> {
if ( if (!authProviderCheck(AuthProvider.MICROSOFT))
!authProviderCheck(
AuthProvider.MICROSOFT,
this.configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS'),
)
) {
throwHTTPErr({ throwHTTPErr({
message: AUTH_PROVIDER_NOT_SPECIFIED, message: AUTH_PROVIDER_NOT_SPECIFIED,
statusCode: 404, statusCode: 404,
}); });
}
return super.canActivate(context); return super.canActivate(context);
} }

View File

@@ -6,7 +6,6 @@ import { Response } from 'express';
import * as cookie from 'cookie'; import * as cookie from 'cookie';
import { AUTH_PROVIDER_NOT_SPECIFIED, COOKIES_NOT_FOUND } from 'src/errors'; import { AUTH_PROVIDER_NOT_SPECIFIED, COOKIES_NOT_FOUND } from 'src/errors';
import { throwErr } from 'src/utils'; import { throwErr } from 'src/utils';
import { ConfigService } from '@nestjs/config';
enum AuthTokenType { enum AuthTokenType {
ACCESS_TOKEN = 'access_token', ACCESS_TOKEN = 'access_token',
@@ -46,17 +45,15 @@ export const authCookieHandler = (
redirect: boolean, redirect: boolean,
redirectUrl: string | null, redirectUrl: string | null,
) => { ) => {
const configService = new ConfigService();
const currentTime = DateTime.now(); const currentTime = DateTime.now();
const accessTokenValidity = currentTime const accessTokenValidity = currentTime
.plus({ .plus({
milliseconds: parseInt(configService.get('ACCESS_TOKEN_VALIDITY')), milliseconds: parseInt(process.env.ACCESS_TOKEN_VALIDITY),
}) })
.toMillis(); .toMillis();
const refreshTokenValidity = currentTime const refreshTokenValidity = currentTime
.plus({ .plus({
milliseconds: parseInt(configService.get('REFRESH_TOKEN_VALIDITY')), milliseconds: parseInt(process.env.REFRESH_TOKEN_VALIDITY),
}) })
.toMillis(); .toMillis();
@@ -78,12 +75,10 @@ export const authCookieHandler = (
} }
// check to see if redirectUrl is a whitelisted url // check to see if redirectUrl is a whitelisted url
const whitelistedOrigins = configService const whitelistedOrigins = process.env.WHITELISTED_ORIGINS.split(',');
.get('WHITELISTED_ORIGINS')
.split(',');
if (!whitelistedOrigins.includes(redirectUrl)) if (!whitelistedOrigins.includes(redirectUrl))
// if it is not redirect by default to REDIRECT_URL // if it is not redirect by default to REDIRECT_URL
redirectUrl = configService.get('REDIRECT_URL'); redirectUrl = process.env.REDIRECT_URL;
return res.status(HttpStatus.OK).redirect(redirectUrl); return res.status(HttpStatus.OK).redirect(redirectUrl);
}; };
@@ -117,16 +112,13 @@ export const subscriptionContextCookieParser = (rawCookies: string) => {
* @param provider Provider we want to check the presence of * @param provider Provider we want to check the presence of
* @returns Boolean if provider specified is present or not * @returns Boolean if provider specified is present or not
*/ */
export function authProviderCheck( export function authProviderCheck(provider: string) {
provider: string,
VITE_ALLOWED_AUTH_PROVIDERS: string,
) {
if (!provider) { if (!provider) {
throwErr(AUTH_PROVIDER_NOT_SPECIFIED); throwErr(AUTH_PROVIDER_NOT_SPECIFIED);
} }
const envVariables = VITE_ALLOWED_AUTH_PROVIDERS const envVariables = process.env.VITE_ALLOWED_AUTH_PROVIDERS
? VITE_ALLOWED_AUTH_PROVIDERS.split(',').map((provider) => ? process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(',').map((provider) =>
provider.trim().toUpperCase(), provider.trim().toUpperCase(),
) )
: []; : [];

View File

@@ -5,20 +5,18 @@ import { AuthService } from '../auth.service';
import { UserService } from 'src/user/user.service'; import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class GithubStrategy extends PassportStrategy(Strategy) { export class GithubStrategy extends PassportStrategy(Strategy) {
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private usersService: UserService, private usersService: UserService,
private configService: ConfigService,
) { ) {
super({ super({
clientID: configService.get('INFRA.GITHUB_CLIENT_ID'), clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: configService.get('INFRA.GITHUB_CLIENT_SECRET'), clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: configService.get('GITHUB_CALLBACK_URL'), callbackURL: process.env.GITHUB_CALLBACK_URL,
scope: [configService.get('GITHUB_SCOPE')], scope: [process.env.GITHUB_SCOPE],
store: true, store: true,
}); });
} }

View File

@@ -5,20 +5,18 @@ import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { AuthService } from '../auth.service'; import { AuthService } from '../auth.service';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class GoogleStrategy extends PassportStrategy(Strategy) { export class GoogleStrategy extends PassportStrategy(Strategy) {
constructor( constructor(
private usersService: UserService, private usersService: UserService,
private authService: AuthService, private authService: AuthService,
private configService: ConfigService,
) { ) {
super({ super({
clientID: configService.get('INFRA.GOOGLE_CLIENT_ID'), clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: configService.get('INFRA.GOOGLE_CLIENT_SECRET'), clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: configService.get('GOOGLE_CALLBACK_URL'), callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: configService.get('GOOGLE_SCOPE').split(','), scope: process.env.GOOGLE_SCOPE.split(','),
passReqToCallback: true, passReqToCallback: true,
store: true, store: true,
}); });

View File

@@ -15,14 +15,10 @@ import {
INVALID_ACCESS_TOKEN, INVALID_ACCESS_TOKEN,
USER_NOT_FOUND, USER_NOT_FOUND,
} from 'src/errors'; } from 'src/errors';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') { export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor( constructor(private usersService: UserService) {
private usersService: UserService,
private configService: ConfigService,
) {
super({ super({
jwtFromRequest: ExtractJwt.fromExtractors([ jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => { (request: Request) => {
@@ -33,7 +29,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
return ATCookie; return ATCookie;
}, },
]), ]),
secretOrKey: configService.get('JWT_SECRET'), secretOrKey: process.env.JWT_SECRET,
}); });
} }

View File

@@ -5,21 +5,19 @@ import { AuthService } from '../auth.service';
import { UserService } from 'src/user/user.service'; import { UserService } from 'src/user/user.service';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class MicrosoftStrategy extends PassportStrategy(Strategy) { export class MicrosoftStrategy extends PassportStrategy(Strategy) {
constructor( constructor(
private authService: AuthService, private authService: AuthService,
private usersService: UserService, private usersService: UserService,
private configService: ConfigService,
) { ) {
super({ super({
clientID: configService.get('INFRA.MICROSOFT_CLIENT_ID'), clientID: process.env.MICROSOFT_CLIENT_ID,
clientSecret: configService.get('INFRA.MICROSOFT_CLIENT_SECRET'), clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
callbackURL: configService.get('MICROSOFT_CALLBACK_URL'), callbackURL: process.env.MICROSOFT_CALLBACK_URL,
scope: [configService.get('MICROSOFT_SCOPE')], scope: [process.env.MICROSOFT_SCOPE],
tenant: configService.get('MICROSOFT_TENANT'), tenant: process.env.MICROSOFT_TENANT,
store: true, store: true,
}); });
} }

View File

@@ -14,14 +14,10 @@ import {
USER_NOT_FOUND, USER_NOT_FOUND,
} from 'src/errors'; } from 'src/errors';
import * as O from 'fp-ts/Option'; import * as O from 'fp-ts/Option';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class RTJwtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') { export class RTJwtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
constructor( constructor(private usersService: UserService) {
private usersService: UserService,
private configService: ConfigService,
) {
super({ super({
jwtFromRequest: ExtractJwt.fromExtractors([ jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => { (request: Request) => {
@@ -32,7 +28,7 @@ export class RTJwtStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
return RTCookie; return RTCookie;
}, },
]), ]),
secretOrKey: configService.get('JWT_SECRET'), secretOrKey: process.env.JWT_SECRET,
}); });
} }

View File

@@ -10,14 +10,6 @@ export const DUPLICATE_EMAIL = 'email/both_emails_cannot_be_same' as const;
export const ONLY_ONE_ADMIN_ACCOUNT = export const ONLY_ONE_ADMIN_ACCOUNT =
'admin/only_one_admin_account_found' as const; 'admin/only_one_admin_account_found' as const;
/**
* Admin user can not be deleted
* To delete the admin user, first make the Admin user a normal user
* (AdminService)
*/
export const ADMIN_CAN_NOT_BE_DELETED =
'admin/admin_can_not_be_deleted' as const;
/** /**
* Token Authorization failed (Check 'Authorization' Header) * Token Authorization failed (Check 'Authorization' Header)
* (GqlAuthGuard) * (GqlAuthGuard)
@@ -36,13 +28,6 @@ export const JSON_INVALID = 'json_invalid';
*/ */
export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified'; export const AUTH_PROVIDER_NOT_SPECIFIED = 'auth/provider_not_specified';
/**
* Auth Provider not specified
* (Auth)
*/
export const AUTH_PROVIDER_NOT_CONFIGURED =
'auth/provider_not_configured_correctly';
/** /**
* Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file * Environment variable "VITE_ALLOWED_AUTH_PROVIDERS" is not present in .env file
*/ */
@@ -107,13 +92,6 @@ export const USER_IS_OWNER = 'user/is_owner' as const;
*/ */
export const USER_IS_ADMIN = 'user/is_admin' as const; export const USER_IS_ADMIN = 'user/is_admin' as const;
/**
* User invite deletion failure error due to invitation not found
* (AdminService)
*/
export const USER_INVITATION_DELETION_FAILED =
'user/invitation_deletion_failed' as const;
/** /**
* Teams not found * Teams not found
* (TeamsService) * (TeamsService)
@@ -666,48 +644,3 @@ export const SHORTCODE_INVALID_PROPERTIES_JSON =
*/ */
export const SHORTCODE_PROPERTIES_NOT_FOUND = export const SHORTCODE_PROPERTIES_NOT_FOUND =
'shortcode/properties_not_found' as const; 'shortcode/properties_not_found' as const;
/**
* Infra Config not found
* (InfraConfigService)
*/
export const INFRA_CONFIG_NOT_FOUND = 'infra_config/not_found' as const;
/**
* Infra Config update failed
* (InfraConfigService)
*/
export const INFRA_CONFIG_UPDATE_FAILED = 'infra_config/update_failed' as const;
/**
* Infra Config not listed for onModuleInit creation
* (InfraConfigService)
*/
export const INFRA_CONFIG_NOT_LISTED =
'infra_config/properly_not_listed' as const;
/**
* Infra Config reset failed
* (InfraConfigService)
*/
export const INFRA_CONFIG_RESET_FAILED = 'infra_config/reset_failed' as const;
/**
* Infra Config invalid input for Config variable
* (InfraConfigService)
*/
export const INFRA_CONFIG_INVALID_INPUT = 'infra_config/invalid_input' as const;
/**
* Infra Config service (auth provider/mailer/audit logs) not configured
* (InfraConfigService)
*/
export const INFRA_CONFIG_SERVICE_NOT_CONFIGURED =
'infra_config/service_not_configured' as const;
/**
* Error message for when the database table does not exist
* (InfraConfigService)
*/
export const DATABASE_TABLE_NOT_EXIST =
'Database migration not found. Please check the documentation for assistance: https://docs.hoppscotch.io/documentation/self-host/community-edition/install-and-build#running-migrations';

View File

@@ -1,106 +0,0 @@
import { AuthProvider } from 'src/auth/helper';
import { AUTH_PROVIDER_NOT_CONFIGURED } from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigEnum } from 'src/types/InfraConfig';
import { throwErr } from 'src/utils';
export enum ServiceStatus {
ENABLE = 'ENABLE',
DISABLE = 'DISABLE',
}
const AuthProviderConfigurations = {
[AuthProvider.GOOGLE]: [
InfraConfigEnum.GOOGLE_CLIENT_ID,
InfraConfigEnum.GOOGLE_CLIENT_SECRET,
],
[AuthProvider.GITHUB]: [
InfraConfigEnum.GITHUB_CLIENT_ID,
InfraConfigEnum.GITHUB_CLIENT_SECRET,
],
[AuthProvider.MICROSOFT]: [
InfraConfigEnum.MICROSOFT_CLIENT_ID,
InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
],
[AuthProvider.EMAIL]: [
InfraConfigEnum.MAILER_SMTP_URL,
InfraConfigEnum.MAILER_ADDRESS_FROM,
],
};
/**
* Load environment variables from the database and set them in the process
*
* @Description Fetch the 'infra_config' table from the database and return it as an object
* (ConfigModule will set the environment variables in the process)
*/
export async function loadInfraConfiguration() {
try {
const prisma = new PrismaService();
const infraConfigs = await prisma.infraConfig.findMany();
let environmentObject: Record<string, any> = {};
infraConfigs.forEach((infraConfig) => {
environmentObject[infraConfig.name] = infraConfig.value;
});
return { INFRA: environmentObject };
} catch (error) {
// Prisma throw error if 'Can't reach at database server' OR 'Table does not exist'
// Reason for not throwing error is, we want successful build during 'postinstall' and generate dist files
return { INFRA: {} };
}
}
/**
* Stop the app after 5 seconds
* (Docker will re-start the app)
*/
export function stopApp() {
console.log('Stopping app in 5 seconds...');
setTimeout(() => {
console.log('Stopping app now...');
process.kill(process.pid, 'SIGTERM');
}, 5000);
}
/**
* Get the configured SSO providers
* @returns Array of configured SSO providers
*/
export function getConfiguredSSOProviders() {
const allowedAuthProviders: string[] =
process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(',');
let configuredAuthProviders: string[] = [];
const addProviderIfConfigured = (provider) => {
const configParameters: string[] = AuthProviderConfigurations[provider];
const isConfigured = configParameters.every((configParameter) => {
return process.env[configParameter];
});
if (isConfigured) configuredAuthProviders.push(provider);
};
allowedAuthProviders.forEach((provider) => addProviderIfConfigured(provider));
if (configuredAuthProviders.length === 0) {
throwErr(AUTH_PROVIDER_NOT_CONFIGURED);
} else if (allowedAuthProviders.length !== configuredAuthProviders.length) {
const unConfiguredAuthProviders = allowedAuthProviders.filter(
(provider) => {
return !configuredAuthProviders.includes(provider);
},
);
console.log(
`${unConfiguredAuthProviders.join(
',',
)} SSO auth provider(s) are not configured properly. Do configure them from Admin Dashboard.`,
);
}
return configuredAuthProviders.join(',');
}

View File

@@ -1,47 +0,0 @@
import { Controller, Get, HttpStatus, Put, UseGuards } from '@nestjs/common';
import { ThrottlerBehindProxyGuard } from 'src/guards/throttler-behind-proxy.guard';
import { InfraConfigService } from './infra-config.service';
import * as E from 'fp-ts/Either';
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
import { RESTAdminGuard } from 'src/admin/guards/rest-admin.guard';
import { throwHTTPErr } from 'src/auth/helper';
import { AuthError } from 'src/types/AuthError';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
@UseGuards(ThrottlerBehindProxyGuard)
@Controller({ path: 'site', version: '1' })
export class SiteController {
constructor(private infraConfigService: InfraConfigService) {}
@Get('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async fetchSetupInfo() {
const status = await this.infraConfigService.get(
InfraConfigEnumForClient.IS_FIRST_TIME_INFRA_SETUP,
);
if (E.isLeft(status))
throwHTTPErr(<AuthError>{
message: status.left,
statusCode: HttpStatus.NOT_FOUND,
});
return status.right;
}
@Put('setup')
@UseGuards(JwtAuthGuard, RESTAdminGuard)
async setSetupAsComplete() {
const res = await this.infraConfigService.update(
InfraConfigEnumForClient.IS_FIRST_TIME_INFRA_SETUP,
false.toString(),
false,
);
if (E.isLeft(res))
throwHTTPErr(<AuthError>{
message: res.left,
statusCode: HttpStatus.FORBIDDEN,
});
return res.right;
}
}

View File

@@ -1,29 +0,0 @@
import { Field, ObjectType, registerEnumType } from '@nestjs/graphql';
import { AuthProvider } from 'src/auth/helper';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper';
@ObjectType()
export class InfraConfig {
@Field({
description: 'Infra Config Name',
})
name: InfraConfigEnumForClient;
@Field({
description: 'Infra Config Value',
})
value: string;
}
registerEnumType(InfraConfigEnumForClient, {
name: 'InfraConfigEnum',
});
registerEnumType(AuthProvider, {
name: 'AuthProvider',
});
registerEnumType(ServiceStatus, {
name: 'ServiceStatus',
});

View File

@@ -1,12 +0,0 @@
import { Module } from '@nestjs/common';
import { InfraConfigService } from './infra-config.service';
import { PrismaModule } from 'src/prisma/prisma.module';
import { SiteController } from './infra-config.controller';
@Module({
imports: [PrismaModule],
providers: [InfraConfigService],
exports: [InfraConfigService],
controllers: [SiteController],
})
export class InfraConfigModule {}

View File

@@ -1,109 +0,0 @@
import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfigService } from './infra-config.service';
import {
InfraConfigEnum,
InfraConfigEnumForClient,
} from 'src/types/InfraConfig';
import { INFRA_CONFIG_NOT_FOUND, INFRA_CONFIG_UPDATE_FAILED } from 'src/errors';
import { ConfigService } from '@nestjs/config';
import * as helper from './helper';
const mockPrisma = mockDeep<PrismaService>();
const mockConfigService = mockDeep<ConfigService>();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const infraConfigService = new InfraConfigService(
mockPrisma,
mockConfigService,
);
beforeEach(() => {
mockReset(mockPrisma);
});
describe('InfraConfigService', () => {
describe('update', () => {
it('should update the infra config', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
const result = await infraConfigService.update(name, value);
expect(result).toEqualRight({ name, value });
});
it('should pass correct params to prisma update', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
jest.spyOn(helper, 'stopApp').mockReturnValueOnce();
await infraConfigService.update(name, value);
expect(mockPrisma.infraConfig.update).toHaveBeenCalledWith({
where: { name },
data: { value },
});
expect(mockPrisma.infraConfig.update).toHaveBeenCalledTimes(1);
});
it('should throw an error if the infra config update failed', async () => {
const name = InfraConfigEnum.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.update.mockRejectedValueOnce('null');
const result = await infraConfigService.update(name, value);
expect(result).toEqualLeft(INFRA_CONFIG_UPDATE_FAILED);
});
});
describe('get', () => {
it('should get the infra config', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
const value = 'true';
mockPrisma.infraConfig.findUniqueOrThrow.mockResolvedValueOnce({
id: '',
name,
value,
active: true,
createdOn: new Date(),
updatedOn: new Date(),
});
const result = await infraConfigService.get(name);
expect(result).toEqualRight({ name, value });
});
it('should pass correct params to prisma findUnique', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
await infraConfigService.get(name);
expect(mockPrisma.infraConfig.findUniqueOrThrow).toHaveBeenCalledWith({
where: { name },
});
expect(mockPrisma.infraConfig.findUniqueOrThrow).toHaveBeenCalledTimes(1);
});
it('should throw an error if the infra config does not exist', async () => {
const name = InfraConfigEnumForClient.GOOGLE_CLIENT_ID;
mockPrisma.infraConfig.findUniqueOrThrow.mockRejectedValueOnce('null');
const result = await infraConfigService.get(name);
expect(result).toEqualLeft(INFRA_CONFIG_NOT_FOUND);
});
});
});

View File

@@ -1,400 +0,0 @@
import { Injectable, OnModuleInit } from '@nestjs/common';
import { InfraConfig } from './infra-config.model';
import { PrismaService } from 'src/prisma/prisma.service';
import { InfraConfig as DBInfraConfig } from '@prisma/client';
import * as E from 'fp-ts/Either';
import {
InfraConfigEnum,
InfraConfigEnumForClient,
} from 'src/types/InfraConfig';
import {
AUTH_PROVIDER_NOT_SPECIFIED,
DATABASE_TABLE_NOT_EXIST,
INFRA_CONFIG_INVALID_INPUT,
INFRA_CONFIG_NOT_FOUND,
INFRA_CONFIG_NOT_LISTED,
INFRA_CONFIG_RESET_FAILED,
INFRA_CONFIG_UPDATE_FAILED,
INFRA_CONFIG_SERVICE_NOT_CONFIGURED,
} from 'src/errors';
import { throwErr, validateSMTPEmail, validateSMTPUrl } from 'src/utils';
import { ConfigService } from '@nestjs/config';
import { ServiceStatus, getConfiguredSSOProviders, stopApp } from './helper';
import { EnableAndDisableSSOArgs, InfraConfigArgs } from './input-args';
import { AuthProvider } from 'src/auth/helper';
@Injectable()
export class InfraConfigService implements OnModuleInit {
constructor(
private readonly prisma: PrismaService,
private readonly configService: ConfigService,
) {}
async onModuleInit() {
await this.initializeInfraConfigTable();
}
async getDefaultInfraConfigs(): Promise<
{ name: InfraConfigEnum; value: string }[]
> {
// Prepare rows for 'infra_config' table with default values (from .env) for each 'name'
const infraConfigDefaultObjs: { name: InfraConfigEnum; value: string }[] = [
{
name: InfraConfigEnum.MAILER_SMTP_URL,
value: process.env.MAILER_SMTP_URL,
},
{
name: InfraConfigEnum.MAILER_ADDRESS_FROM,
value: process.env.MAILER_ADDRESS_FROM,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_ID,
value: process.env.GOOGLE_CLIENT_ID,
},
{
name: InfraConfigEnum.GOOGLE_CLIENT_SECRET,
value: process.env.GOOGLE_CLIENT_SECRET,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_ID,
value: process.env.GITHUB_CLIENT_ID,
},
{
name: InfraConfigEnum.GITHUB_CLIENT_SECRET,
value: process.env.GITHUB_CLIENT_SECRET,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_ID,
value: process.env.MICROSOFT_CLIENT_ID,
},
{
name: InfraConfigEnum.MICROSOFT_CLIENT_SECRET,
value: process.env.MICROSOFT_CLIENT_SECRET,
},
{
name: InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
value: getConfiguredSSOProviders(),
},
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: (await this.prisma.infraConfig.count()) === 0 ? 'true' : 'false',
},
];
return infraConfigDefaultObjs;
}
/**
* Initialize the 'infra_config' table with values from .env
* @description This function create rows 'infra_config' in very first time (only once)
*/
async initializeInfraConfigTable() {
try {
// Get all the 'names' of the properties to be saved in the 'infra_config' table
const enumValues = Object.values(InfraConfigEnum);
// Fetch the default values (value in .env) for configs to be saved in 'infra_config' table
const infraConfigDefaultObjs = await this.getDefaultInfraConfigs();
// Check if all the 'names' are listed in the default values
if (enumValues.length !== infraConfigDefaultObjs.length) {
throw new Error(INFRA_CONFIG_NOT_LISTED);
}
// Eliminate the rows (from 'infraConfigDefaultObjs') that are already present in the database table
const dbInfraConfigs = await this.prisma.infraConfig.findMany();
const propsToInsert = infraConfigDefaultObjs.filter(
(p) => !dbInfraConfigs.find((e) => e.name === p.name),
);
if (propsToInsert.length > 0) {
await this.prisma.infraConfig.createMany({ data: propsToInsert });
stopApp();
}
} catch (error) {
if (error.code === 'P1001') {
// Prisma error code for 'Can't reach at database server'
// We're not throwing error here because we want to allow the app to run 'pnpm install'
} else if (error.code === 'P2021') {
// Prisma error code for 'Table does not exist'
throwErr(DATABASE_TABLE_NOT_EXIST);
} else {
throwErr(error);
}
}
}
/**
* Typecast a database InfraConfig to a InfraConfig model
* @param dbInfraConfig database InfraConfig
* @returns InfraConfig model
*/
cast(dbInfraConfig: DBInfraConfig) {
return <InfraConfig>{
name: dbInfraConfig.name,
value: dbInfraConfig.value ?? '',
};
}
/**
* Get all the InfraConfigs as map
* @returns InfraConfig map
*/
async getInfraConfigsMap() {
const infraConfigs = await this.prisma.infraConfig.findMany();
const infraConfigMap: Record<string, string> = {};
infraConfigs.forEach((config) => {
infraConfigMap[config.name] = config.value;
});
return infraConfigMap;
}
/**
* Update InfraConfig by name
* @param name Name of the InfraConfig
* @param value Value of the InfraConfig
* @param restartEnabled If true, restart the app after updating the InfraConfig
* @returns InfraConfig model
*/
async update(
name: InfraConfigEnumForClient | InfraConfigEnum,
value: string,
restartEnabled = false,
) {
const isValidate = this.validateEnvValues([{ name, value }]);
if (E.isLeft(isValidate)) return E.left(isValidate.left);
try {
const infraConfig = await this.prisma.infraConfig.update({
where: { name },
data: { value },
});
if (restartEnabled) stopApp();
return E.right(this.cast(infraConfig));
} catch (e) {
return E.left(INFRA_CONFIG_UPDATE_FAILED);
}
}
/**
* Update InfraConfigs by name
* @param infraConfigs InfraConfigs to update
* @returns InfraConfig model
*/
async updateMany(infraConfigs: InfraConfigArgs[]) {
const isValidate = this.validateEnvValues(infraConfigs);
if (E.isLeft(isValidate)) return E.left(isValidate.left);
try {
await this.prisma.$transaction(async (tx) => {
for (let i = 0; i < infraConfigs.length; i++) {
await tx.infraConfig.update({
where: { name: infraConfigs[i].name },
data: { value: infraConfigs[i].value },
});
}
});
stopApp();
return E.right(infraConfigs);
} catch (e) {
return E.left(INFRA_CONFIG_UPDATE_FAILED);
}
}
/**
* Check if the service is configured or not
* @param service Service can be Auth Provider, Mailer, Audit Log etc.
* @param configMap Map of all the infra configs
* @returns Either true or false
*/
isServiceConfigured(
service: AuthProvider,
configMap: Record<string, string>,
) {
switch (service) {
case AuthProvider.GOOGLE:
return configMap.GOOGLE_CLIENT_ID && configMap.GOOGLE_CLIENT_SECRET;
case AuthProvider.GITHUB:
return configMap.GITHUB_CLIENT_ID && configMap.GITHUB_CLIENT_SECRET;
case AuthProvider.MICROSOFT:
return (
configMap.MICROSOFT_CLIENT_ID && configMap.MICROSOFT_CLIENT_SECRET
);
case AuthProvider.EMAIL:
return configMap.MAILER_SMTP_URL && configMap.MAILER_ADDRESS_FROM;
default:
return false;
}
}
/**
* Enable or Disable SSO for login/signup
* @param provider Auth Provider to enable or disable
* @param status Status to enable or disable
* @returns Either true or an error
*/
async enableAndDisableSSO(providerInfo: EnableAndDisableSSOArgs[]) {
const allowedAuthProviders = this.configService
.get<string>('INFRA.VITE_ALLOWED_AUTH_PROVIDERS')
.split(',');
let updatedAuthProviders = allowedAuthProviders;
const infraConfigMap = await this.getInfraConfigsMap();
providerInfo.forEach(({ provider, status }) => {
if (status === ServiceStatus.ENABLE) {
const isConfigured = this.isServiceConfigured(provider, infraConfigMap);
if (!isConfigured) {
throwErr(INFRA_CONFIG_SERVICE_NOT_CONFIGURED);
}
updatedAuthProviders.push(provider);
} else if (status === ServiceStatus.DISABLE) {
updatedAuthProviders = updatedAuthProviders.filter(
(p) => p !== provider,
);
}
});
updatedAuthProviders = [...new Set(updatedAuthProviders)];
if (updatedAuthProviders.length === 0) {
return E.left(AUTH_PROVIDER_NOT_SPECIFIED);
}
const isUpdated = await this.update(
InfraConfigEnum.VITE_ALLOWED_AUTH_PROVIDERS,
updatedAuthProviders.join(','),
true,
);
if (E.isLeft(isUpdated)) return E.left(isUpdated.left);
return E.right(true);
}
/**
* Get InfraConfig by name
* @param name Name of the InfraConfig
* @returns InfraConfig model
*/
async get(name: InfraConfigEnumForClient) {
try {
const infraConfig = await this.prisma.infraConfig.findUniqueOrThrow({
where: { name },
});
return E.right(this.cast(infraConfig));
} catch (e) {
return E.left(INFRA_CONFIG_NOT_FOUND);
}
}
/**
* Get InfraConfigs by names
* @param names Names of the InfraConfigs
* @returns InfraConfig model
*/
async getMany(names: InfraConfigEnumForClient[]) {
try {
const infraConfigs = await this.prisma.infraConfig.findMany({
where: { name: { in: names } },
});
return E.right(infraConfigs.map((p) => this.cast(p)));
} catch (e) {
return E.left(INFRA_CONFIG_NOT_FOUND);
}
}
/**
* Get allowed auth providers for login/signup
* @returns string[]
*/
getAllowedAuthProviders() {
return this.configService
.get<string>('INFRA.VITE_ALLOWED_AUTH_PROVIDERS')
.split(',');
}
/**
* Reset all the InfraConfigs to their default values (from .env)
*/
async reset() {
try {
const infraConfigDefaultObjs = await this.getDefaultInfraConfigs();
await this.prisma.infraConfig.deleteMany({
where: { name: { in: infraConfigDefaultObjs.map((p) => p.name) } },
});
// Hardcode t
const updatedInfraConfigDefaultObjs = infraConfigDefaultObjs.filter(
(obj) => obj.name !== InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
);
await this.prisma.infraConfig.createMany({
data: [
...updatedInfraConfigDefaultObjs,
{
name: InfraConfigEnum.IS_FIRST_TIME_INFRA_SETUP,
value: 'true',
},
],
});
stopApp();
return E.right(true);
} catch (e) {
return E.left(INFRA_CONFIG_RESET_FAILED);
}
}
/**
* Validate the values of the InfraConfigs
*/
validateEnvValues(
infraConfigs: {
name: InfraConfigEnumForClient | InfraConfigEnum;
value: string;
}[],
) {
for (let i = 0; i < infraConfigs.length; i++) {
switch (infraConfigs[i].name) {
case InfraConfigEnumForClient.MAILER_SMTP_URL:
const isValidUrl = validateSMTPUrl(infraConfigs[i].value);
if (!isValidUrl) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MAILER_ADDRESS_FROM:
const isValidEmail = validateSMTPEmail(infraConfigs[i].value);
if (!isValidEmail) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GOOGLE_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GITHUB_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.GITHUB_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_ID:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
case InfraConfigEnumForClient.MICROSOFT_CLIENT_SECRET:
if (!infraConfigs[i].value) return E.left(INFRA_CONFIG_INVALID_INPUT);
break;
default:
break;
}
}
return E.right(true);
}
}

View File

@@ -1,30 +0,0 @@
import { Field, InputType } from '@nestjs/graphql';
import { InfraConfigEnumForClient } from 'src/types/InfraConfig';
import { ServiceStatus } from './helper';
import { AuthProvider } from 'src/auth/helper';
@InputType()
export class InfraConfigArgs {
@Field(() => InfraConfigEnumForClient, {
description: 'Infra Config Name',
})
name: InfraConfigEnumForClient;
@Field({
description: 'Infra Config Value',
})
value: string;
}
@InputType()
export class EnableAndDisableSSOArgs {
@Field(() => AuthProvider, {
description: 'Auth Provider',
})
provider: AuthProvider;
@Field(() => ServiceStatus, {
description: 'Auth Provider Status',
})
status: ServiceStatus;
}

View File

@@ -1,4 +1,4 @@
import { Global, Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer'; import { MailerModule as NestMailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { MailerService } from './mailer.service'; import { MailerService } from './mailer.service';
@@ -7,42 +7,24 @@ import {
MAILER_FROM_ADDRESS_UNDEFINED, MAILER_FROM_ADDRESS_UNDEFINED,
MAILER_SMTP_URL_UNDEFINED, MAILER_SMTP_URL_UNDEFINED,
} from 'src/errors'; } from 'src/errors';
import { ConfigService } from '@nestjs/config';
import { loadInfraConfiguration } from 'src/infra-config/helper';
@Global()
@Module({ @Module({
imports: [], imports: [
NestMailerModule.forRoot({
transport:
process.env.MAILER_SMTP_URL ?? throwErr(MAILER_SMTP_URL_UNDEFINED),
defaults: {
from:
process.env.MAILER_ADDRESS_FROM ??
throwErr(MAILER_FROM_ADDRESS_UNDEFINED),
},
template: {
dir: __dirname + '/templates',
adapter: new HandlebarsAdapter(),
},
}),
],
providers: [MailerService], providers: [MailerService],
exports: [MailerService], exports: [MailerService],
}) })
export class MailerModule { export class MailerModule {}
static async register() {
const env = await loadInfraConfiguration();
let mailerSmtpUrl = env.INFRA.MAILER_SMTP_URL;
let mailerAddressFrom = env.INFRA.MAILER_ADDRESS_FROM;
if (!env.INFRA.MAILER_SMTP_URL || !env.INFRA.MAILER_ADDRESS_FROM) {
const config = new ConfigService();
mailerSmtpUrl = config.get('MAILER_SMTP_URL');
mailerAddressFrom = config.get('MAILER_ADDRESS_FROM');
}
return {
module: MailerModule,
imports: [
NestMailerModule.forRoot({
transport: mailerSmtpUrl ?? throwErr(MAILER_SMTP_URL_UNDEFINED),
defaults: {
from: mailerAddressFrom ?? throwErr(MAILER_FROM_ADDRESS_UNDEFINED),
},
template: {
dir: __dirname + '/templates',
adapter: new HandlebarsAdapter(),
},
}),
],
};
}
}

View File

@@ -6,24 +6,18 @@ import { VersioningType } from '@nestjs/common';
import * as session from 'express-session'; import * as session from 'express-session';
import { emitGQLSchemaFile } from './gql-schema'; import { emitGQLSchemaFile } from './gql-schema';
import { checkEnvironmentAuthProvider } from './utils'; import { checkEnvironmentAuthProvider } from './utils';
import { ConfigService } from '@nestjs/config';
async function bootstrap() { async function bootstrap() {
console.log(`Running in production: ${process.env.PRODUCTION}`);
console.log(`Port: ${process.env.PORT}`);
checkEnvironmentAuthProvider();
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
const configService = app.get(ConfigService);
console.log(`Running in production: ${configService.get('PRODUCTION')}`);
console.log(`Port: ${configService.get('PORT')}`);
checkEnvironmentAuthProvider(
configService.get('INFRA.VITE_ALLOWED_AUTH_PROVIDERS') ??
configService.get('VITE_ALLOWED_AUTH_PROVIDERS'),
);
app.use( app.use(
session({ session({
secret: configService.get('SESSION_SECRET'), secret: process.env.SESSION_SECRET,
}), }),
); );
@@ -34,18 +28,18 @@ async function bootstrap() {
}), }),
); );
if (configService.get('PRODUCTION') === 'false') { if (process.env.PRODUCTION === 'false') {
console.log('Enabling CORS with development settings'); console.log('Enabling CORS with development settings');
app.enableCors({ app.enableCors({
origin: configService.get('WHITELISTED_ORIGINS').split(','), origin: process.env.WHITELISTED_ORIGINS.split(','),
credentials: true, credentials: true,
}); });
} else { } else {
console.log('Enabling CORS with production settings'); console.log('Enabling CORS with production settings');
app.enableCors({ app.enableCors({
origin: configService.get('WHITELISTED_ORIGINS').split(','), origin: process.env.WHITELISTED_ORIGINS.split(','),
credentials: true, credentials: true,
}); });
} }
@@ -53,13 +47,7 @@ async function bootstrap() {
type: VersioningType.URI, type: VersioningType.URI,
}); });
app.use(cookieParser()); app.use(cookieParser());
await app.listen(configService.get('PORT') || 3170); await app.listen(process.env.PORT || 3170);
// Graceful shutdown
process.on('SIGTERM', async () => {
console.info('SIGTERM signal received');
await app.close();
});
} }
if (!process.env.GENERATE_GQL_SCHEMA) { if (!process.env.GENERATE_GQL_SCHEMA) {

View File

@@ -504,24 +504,20 @@ describe('ShortcodeService', () => {
); );
expect(result).toEqual(<ShortcodeWithUserEmail[]>[ expect(result).toEqual(<ShortcodeWithUserEmail[]>[
{ {
id: shortcodesWithUserEmail[0].id, id: shortcodes[0].id,
request: JSON.stringify(shortcodesWithUserEmail[0].request), request: JSON.stringify(shortcodes[0].request),
properties: JSON.stringify( properties: JSON.stringify(shortcodes[0].embedProperties),
shortcodesWithUserEmail[0].embedProperties, createdOn: shortcodes[0].createdOn,
),
createdOn: shortcodesWithUserEmail[0].createdOn,
creator: { creator: {
uid: user.uid, uid: user.uid,
email: user.email, email: user.email,
}, },
}, },
{ {
id: shortcodesWithUserEmail[1].id, id: shortcodes[1].id,
request: JSON.stringify(shortcodesWithUserEmail[1].request), request: JSON.stringify(shortcodes[1].request),
properties: JSON.stringify( properties: JSON.stringify(shortcodes[1].embedProperties),
shortcodesWithUserEmail[1].embedProperties, createdOn: shortcodes[1].createdOn,
),
createdOn: shortcodesWithUserEmail[1].createdOn,
creator: { creator: {
uid: user.uid, uid: user.uid,
email: user.email, email: user.email,

View File

@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { MailerModule } from 'src/mailer/mailer.module';
import { PrismaModule } from 'src/prisma/prisma.module'; import { PrismaModule } from 'src/prisma/prisma.module';
import { PubSubModule } from 'src/pubsub/pubsub.module'; import { PubSubModule } from 'src/pubsub/pubsub.module';
import { TeamModule } from 'src/team/team.module'; import { TeamModule } from 'src/team/team.module';
@@ -11,7 +12,7 @@ import { TeamInviteeGuard } from './team-invitee.guard';
import { TeamTeamInviteExtResolver } from './team-teaminvite-ext.resolver'; import { TeamTeamInviteExtResolver } from './team-teaminvite-ext.resolver';
@Module({ @Module({
imports: [PrismaModule, TeamModule, PubSubModule, UserModule], imports: [PrismaModule, TeamModule, PubSubModule, UserModule, MailerModule],
providers: [ providers: [
TeamInvitationService, TeamInvitationService,
TeamInvitationResolver, TeamInvitationResolver,

View File

@@ -20,7 +20,6 @@ import { UserService } from 'src/user/user.service';
import { PubSubService } from 'src/pubsub/pubsub.service'; import { PubSubService } from 'src/pubsub/pubsub.service';
import { validateEmail } from '../utils'; import { validateEmail } from '../utils';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class TeamInvitationService { export class TeamInvitationService {
@@ -29,8 +28,8 @@ export class TeamInvitationService {
private readonly userService: UserService, private readonly userService: UserService,
private readonly teamService: TeamService, private readonly teamService: TeamService,
private readonly mailerService: MailerService, private readonly mailerService: MailerService,
private readonly pubsub: PubSubService, private readonly pubsub: PubSubService,
private readonly configService: ConfigService,
) {} ) {}
/** /**
@@ -151,9 +150,7 @@ export class TeamInvitationService {
template: 'team-invitation', template: 'team-invitation',
variables: { variables: {
invitee: creator.displayName ?? 'A Hoppscotch User', invitee: creator.displayName ?? 'A Hoppscotch User',
action_url: `${this.configService.get('VITE_BASE_URL')}/join-team?id=${ action_url: `${process.env.VITE_BASE_URL}/join-team?id=${dbInvitation.id}`,
dbInvitation.id
}`,
invite_team_name: team.name, invite_team_name: team.name,
}, },
}); });

View File

@@ -1,33 +0,0 @@
export enum InfraConfigEnum {
MAILER_SMTP_URL = 'MAILER_SMTP_URL',
MAILER_ADDRESS_FROM = 'MAILER_ADDRESS_FROM',
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
VITE_ALLOWED_AUTH_PROVIDERS = 'VITE_ALLOWED_AUTH_PROVIDERS',
IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP',
}
export enum InfraConfigEnumForClient {
MAILER_SMTP_URL = 'MAILER_SMTP_URL',
MAILER_ADDRESS_FROM = 'MAILER_ADDRESS_FROM',
GOOGLE_CLIENT_ID = 'GOOGLE_CLIENT_ID',
GOOGLE_CLIENT_SECRET = 'GOOGLE_CLIENT_SECRET',
GITHUB_CLIENT_ID = 'GITHUB_CLIENT_ID',
GITHUB_CLIENT_SECRET = 'GITHUB_CLIENT_SECRET',
MICROSOFT_CLIENT_ID = 'MICROSOFT_CLIENT_ID',
MICROSOFT_CLIENT_SECRET = 'MICROSOFT_CLIENT_SECRET',
IS_FIRST_TIME_INFRA_SETUP = 'IS_FIRST_TIME_INFRA_SETUP',
}

View File

@@ -17,21 +17,3 @@ export class PaginationArgs {
}) })
take: number; take: number;
} }
@ArgsType()
@InputType()
export class OffsetPaginationArgs {
@Field({
nullable: true,
defaultValue: 0,
description: 'Number of items to skip',
})
skip: number;
@Field({
nullable: true,
defaultValue: 10,
description: 'Number of items to fetch',
})
take: number;
}

View File

@@ -56,22 +56,3 @@ export enum SessionType {
registerEnumType(SessionType, { registerEnumType(SessionType, {
name: 'SessionType', name: 'SessionType',
}); });
@ObjectType()
export class UserDeletionResult {
@Field(() => ID, {
description: 'UID of the user',
})
userUID: string;
@Field(() => Boolean, {
description: 'Flag to determine if user deletion was successful or not',
})
isDeleted: Boolean;
@Field({
nullable: true,
description: 'Error message if user deletion was not successful',
})
errorMessage: String;
}

View File

@@ -1,4 +1,4 @@
import { JSON_INVALID, USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors'; import { JSON_INVALID, USER_NOT_FOUND } from 'src/errors';
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
@@ -176,26 +176,6 @@ describe('UserService', () => {
}); });
}); });
describe('findUsersByIds', () => {
test('should successfully return users given valid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.findUsersByIds([
'123344',
'5555',
'6666',
]);
expect(result).toEqual(users);
});
test('should return empty array of users given a invalid user UIDs', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.findUsersByIds(['sdcvbdbr']);
expect(result).toEqual([]);
});
});
describe('createUserViaMagicLink', () => { describe('createUserViaMagicLink', () => {
test('should successfully create user and account for magic-link given valid inputs', async () => { test('should successfully create user and account for magic-link given valid inputs', async () => {
mockPrisma.user.create.mockResolvedValueOnce(user); mockPrisma.user.create.mockResolvedValueOnce(user);
@@ -434,54 +414,6 @@ describe('UserService', () => {
}); });
}); });
describe('updateUserDisplayName', () => {
test('should resolve right and update user display name', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
const result = await userService.updateUserDisplayName(
user.uid,
newDisplayName,
);
expect(result).toEqualRight({
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
});
});
test('should resolve right and publish user updated subscription', async () => {
const newDisplayName = 'New Name';
mockPrisma.user.update.mockResolvedValueOnce({
...user,
displayName: newDisplayName,
});
await userService.updateUserDisplayName(user.uid, user.displayName);
expect(mockPubSub.publish).toHaveBeenCalledWith(
`user/${user.uid}/updated`,
{
...user,
displayName: newDisplayName,
currentGQLSession: JSON.stringify(user.currentGQLSession),
currentRESTSession: JSON.stringify(user.currentRESTSession),
},
);
});
test('should resolve left and error when invalid user uid is passed', async () => {
mockPrisma.user.update.mockRejectedValueOnce('NotFoundError');
const result = await userService.updateUserDisplayName(
'invalidUserUid',
user.displayName,
);
expect(result).toEqualLeft(USER_NOT_FOUND);
});
});
describe('fetchAllUsers', () => { describe('fetchAllUsers', () => {
test('should resolve right and return 20 users when cursor is null', async () => { test('should resolve right and return 20 users when cursor is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users); mockPrisma.user.findMany.mockResolvedValueOnce(users);
@@ -503,36 +435,6 @@ describe('UserService', () => {
}); });
}); });
describe('fetchAllUsersV2', () => {
test('should resolve right and return first 20 users when searchString is null', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2(null, {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve right and return next 20 users when searchString is provided', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(users);
const result = await userService.fetchAllUsersV2('.com', {
take: 20,
skip: 0,
});
expect(result).toEqual(users);
});
test('should resolve left and return an empty array when users not found', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce([]);
const result = await userService.fetchAllUsersV2('Unknown entry', {
take: 20,
skip: 0,
});
expect(result).toEqual([]);
});
});
describe('fetchAdminUsers', () => { describe('fetchAdminUsers', () => {
test('should return a list of admin users', async () => { test('should return a list of admin users', async () => {
mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers); mockPrisma.user.findMany.mockResolvedValueOnce(adminUsers);
@@ -654,17 +556,4 @@ describe('UserService', () => {
expect(result).toEqual(10); expect(result).toEqual(10);
}); });
}); });
describe('removeUsersAsAdmin', () => {
test('should resolve right and return true for valid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 1 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualRight(true);
});
test('should resolve right and return false for invalid user UIDs', async () => {
mockPrisma.user.updateMany.mockResolvedValueOnce({ count: 0 });
const result = await userService.removeUsersAsAdmin(['123344']);
expect(result).toEqualLeft(USERS_NOT_FOUND);
});
});
}); });

View File

@@ -8,14 +8,13 @@ import * as T from 'fp-ts/Task';
import * as A from 'fp-ts/Array'; import * as A from 'fp-ts/Array';
import { pipe, constVoid } from 'fp-ts/function'; import { pipe, constVoid } from 'fp-ts/function';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { USERS_NOT_FOUND, USER_NOT_FOUND } from 'src/errors'; import { USER_NOT_FOUND } from 'src/errors';
import { SessionType, User } from './user.model'; import { SessionType, User } from './user.model';
import { USER_UPDATE_FAILED } from 'src/errors'; import { USER_UPDATE_FAILED } from 'src/errors';
import { PubSubService } from 'src/pubsub/pubsub.service'; import { PubSubService } from 'src/pubsub/pubsub.service';
import { stringToJson, taskEitherValidateArraySeq } from 'src/utils'; import { stringToJson, taskEitherValidateArraySeq } from 'src/utils';
import { UserDataHandler } from './user.data.handler'; import { UserDataHandler } from './user.data.handler';
import { User as DbUser } from '@prisma/client'; import { User as DbUser } from '@prisma/client';
import { OffsetPaginationArgs } from 'src/types/input-types.args';
@Injectable() @Injectable()
export class UserService { export class UserService {
@@ -89,20 +88,6 @@ export class UserService {
} }
} }
/**
* Find users with given IDs
* @param userUIDs User IDs
* @returns Array of found Users
*/
async findUsersByIds(userUIDs: string[]): Promise<AuthUser[]> {
const users = await this.prisma.user.findMany({
where: {
uid: { in: userUIDs },
},
});
return users;
}
/** /**
* Update User with new generated hashed refresh token * Update User with new generated hashed refresh token
* *
@@ -284,30 +269,6 @@ export class UserService {
} }
} }
/**
* Update a user's data
* @param userUID User UID
* @param displayName User's displayName
* @returns a Either of User or error
*/
async updateUserDisplayName(userUID: string, displayName: string) {
try {
const dbUpdatedUser = await this.prisma.user.update({
where: { uid: userUID },
data: { displayName },
});
const updatedUser = this.convertDbUserToUser(dbUpdatedUser);
// Publish subscription for user updates
await this.pubsub.publish(`user/${updatedUser.uid}/updated`, updatedUser);
return E.right(updatedUser);
} catch (error) {
return E.left(USER_NOT_FOUND);
}
}
/** /**
* Validate and parse currentRESTSession and currentGQLSession * Validate and parse currentRESTSession and currentGQLSession
* @param sessionData string of the session * @param sessionData string of the session
@@ -325,7 +286,6 @@ export class UserService {
* @param cursorID string of userUID or null * @param cursorID string of userUID or null
* @param take number of users to query * @param take number of users to query
* @returns an array of `User` object * @returns an array of `User` object
* @deprecated use fetchAllUsersV2 instead
*/ */
async fetchAllUsers(cursorID: string, take: number) { async fetchAllUsers(cursorID: string, take: number) {
const fetchedUsers = await this.prisma.user.findMany({ const fetchedUsers = await this.prisma.user.findMany({
@@ -336,43 +296,6 @@ export class UserService {
return fetchedUsers; return fetchedUsers;
} }
/**
* Fetch all the users in the `User` table based on cursor
* @param searchString search on user's displayName or email
* @param paginationOption pagination options
* @returns an array of `User` object
*/
async fetchAllUsersV2(
searchString: string,
paginationOption: OffsetPaginationArgs,
) {
const fetchedUsers = await this.prisma.user.findMany({
skip: paginationOption.skip,
take: paginationOption.take,
where: searchString
? {
OR: [
{
displayName: {
contains: searchString,
mode: 'insensitive',
},
},
{
email: {
contains: searchString,
mode: 'insensitive',
},
},
],
}
: undefined,
orderBy: [{ isAdmin: 'desc' }, { displayName: 'asc' }],
});
return fetchedUsers;
}
/** /**
* Fetch the number of users in db * Fetch the number of users in db
* @returns a count (Int) of user records in DB * @returns a count (Int) of user records in DB
@@ -403,23 +326,6 @@ export class UserService {
} }
} }
/**
* Change users to admins by toggling isAdmin param to true
* @param userUID user UIDs
* @returns a Either of true or error
*/
async makeAdmins(userUIDs: string[]) {
try {
await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: true },
});
return E.right(true);
} catch (error) {
return E.left(USER_UPDATE_FAILED);
}
}
/** /**
* Fetch all the admin users * Fetch all the admin users
* @returns an array of admin users * @returns an array of admin users
@@ -538,22 +444,4 @@ export class UserService {
return E.left(USER_NOT_FOUND); return E.left(USER_NOT_FOUND);
} }
} }
/**
* Change users from an admin by toggling isAdmin param to false
* @param userUIDs user UIDs
* @returns a Either of true or error
*/
async removeUsersAsAdmin(userUIDs: string[]) {
const data = await this.prisma.user.updateMany({
where: { uid: { in: userUIDs } },
data: { isAdmin: false },
});
if (data.count === 0) {
return E.left(USERS_NOT_FOUND);
}
return E.right(true);
}
} }

View File

@@ -131,48 +131,6 @@ export const validateEmail = (email: string) => {
).test(email); ).test(email);
}; };
// Regular expressions for supported address object formats by nodemailer
// check out for more info https://nodemailer.com/message/addresses
const emailRegex1 = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const emailRegex2 =
/^[\w\s]* <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
const emailRegex3 =
/^"[\w\s]+" <([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})>$/;
/**
* Checks to see if the SMTP email is valid or not
* @param email
* @returns A Boolean depending on the format of the email
*/
export const validateSMTPEmail = (email: string) => {
// Check if the input matches any of the formats
return (
emailRegex1.test(email) ||
emailRegex2.test(email) ||
emailRegex3.test(email)
);
};
/**
* Checks to see if the URL is valid or not
* @param url The URL to validate
* @returns boolean
*/
export const validateSMTPUrl = (url: string) => {
// Possible valid formats
// smtp(s)://mail.example.com
// smtp(s)://user:pass@mail.example.com
// smtp(s)://mail.example.com:587
// smtp(s)://user:pass@mail.example.com:587
if (!url || url.length === 0) return false;
const regex =
/^(smtp|smtps):\/\/(?:([^:]+):([^@]+)@)?((?!\.)[^:]+)(?::(\d+))?$/;
if (regex.test(url)) return true;
return false;
};
/** /**
* String to JSON parser * String to JSON parser
* @param {str} str The string to parse * @param {str} str The string to parse
@@ -203,23 +161,21 @@ export function isValidLength(title: string, length: number) {
/** /**
* This function is called by bootstrap() in main.ts * This function is called by bootstrap() in main.ts
* It checks if the "VITE_ALLOWED_AUTH_PROVIDERS" environment variable is properly set or not. * It checks if the "VITE_ALLOWED_AUTH_PROVIDERS" environment variable is properly set or not.
* If not, it throws an error. * If not, it throws an error.
*/ */
export function checkEnvironmentAuthProvider( export function checkEnvironmentAuthProvider() {
VITE_ALLOWED_AUTH_PROVIDERS: string, if (!process.env.hasOwnProperty('VITE_ALLOWED_AUTH_PROVIDERS')) {
) {
if (!VITE_ALLOWED_AUTH_PROVIDERS) {
throw new Error(ENV_NOT_FOUND_KEY_AUTH_PROVIDERS); throw new Error(ENV_NOT_FOUND_KEY_AUTH_PROVIDERS);
} }
if (VITE_ALLOWED_AUTH_PROVIDERS === '') { if (process.env.VITE_ALLOWED_AUTH_PROVIDERS === '') {
throw new Error(ENV_EMPTY_AUTH_PROVIDERS); throw new Error(ENV_EMPTY_AUTH_PROVIDERS);
} }
const givenAuthProviders = VITE_ALLOWED_AUTH_PROVIDERS.split(',').map( const givenAuthProviders = process.env.VITE_ALLOWED_AUTH_PROVIDERS.split(
(provider) => provider.toLocaleUpperCase(), ',',
); ).map((provider) => provider.toLocaleUpperCase());
const supportedAuthProviders = Object.values(AuthProvider).map( const supportedAuthProviders = Object.values(AuthProvider).map(
(provider: string) => provider.toLocaleUpperCase(), (provider: string) => provider.toLocaleUpperCase(),
); );

View File

@@ -0,0 +1,3 @@
#!/usr/bin/env node
// * The entry point of the CLI
require("../dist").cli(process.argv);

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env node
// * The entry point of the CLI
import { cli } from "../dist/index.js";
cli(process.argv);

View File

@@ -1,12 +1,11 @@
{ {
"name": "@hoppscotch/cli", "name": "@hoppscotch/cli",
"version": "0.6.0", "version": "0.4.0",
"description": "A CLI to run Hoppscotch test scripts in CI environments.", "description": "A CLI to run Hoppscotch test scripts in CI environments.",
"homepage": "https://hoppscotch.io", "homepage": "https://hoppscotch.io",
"type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
"hopp": "bin/hopp.js" "hopp": "bin/hopp"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
@@ -40,27 +39,27 @@
}, },
"license": "MIT", "license": "MIT",
"private": false, "private": false,
"dependencies": {
"axios": "^1.6.6",
"chalk": "^5.3.0",
"commander": "^11.1.0",
"lodash-es": "^4.17.21",
"qs": "^6.11.2",
"zod": "^3.22.4"
},
"devDependencies": { "devDependencies": {
"@hoppscotch/data": "workspace:^", "@hoppscotch/data": "workspace:^",
"@hoppscotch/js-sandbox": "workspace:^", "@hoppscotch/js-sandbox": "workspace:^",
"@relmify/jest-fp-ts": "^2.1.1", "@relmify/jest-fp-ts": "^2.1.1",
"@swc/core": "^1.3.105", "@swc/core": "^1.3.92",
"@types/jest": "^29.5.11", "@types/jest": "^29.5.5",
"@types/lodash-es": "^4.17.12", "@types/lodash": "^4.14.199",
"@types/qs": "^6.9.11", "@types/qs": "^6.9.8",
"fp-ts": "^2.16.2", "axios": "^0.21.4",
"chalk": "^4.1.2",
"commander": "^11.0.0",
"esm": "^3.2.25",
"fp-ts": "^2.16.1",
"io-ts": "^2.2.20",
"jest": "^29.7.0", "jest": "^29.7.0",
"prettier": "^3.2.4", "lodash": "^4.17.21",
"ts-jest": "^29.1.2", "prettier": "^3.0.3",
"tsup": "^8.0.1", "qs": "^6.11.2",
"typescript": "^5.3.3" "ts-jest": "^29.1.1",
"tsup": "^7.2.0",
"typescript": "^5.2.2",
"zod": "^3.22.4"
} }
} }

View File

@@ -1,64 +1,63 @@
import { ExecException } from "child_process"; import { ExecException } from "child_process";
import { HoppErrorCode } from "../../types/errors"; import { HoppErrorCode } from "../../types/errors";
import { runCLI, getErrorCode, getTestJsonFilePath } from "../utils"; import { execAsync, getErrorCode, getTestJsonFilePath } from "../utils";
describe("Test 'hopp test <file>' command:", () => { describe("Test 'hopp test <file>' command:", () => {
test("No collection file path provided.", async () => { test("No collection file path provided.", async () => {
const args = "test"; const cmd = `node ./bin/hopp test`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Collection file not found.", async () => { test("Collection file not found.", async () => {
const args = "test notfound.json"; const cmd = `node ./bin/hopp test notfound.json`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND"); expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
}); });
test("Collection file is invalid JSON.", async () => { test("Collection file is invalid JSON.", async () => {
const args = `test ${getTestJsonFilePath( const cmd = `node ./bin/hopp test ${getTestJsonFilePath(
"malformed-collection.json" "malformed-collection.json"
)}`; )}`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR"); expect(out).toBe<HoppErrorCode>("UNKNOWN_ERROR");
}); });
test("Malformed collection file.", async () => { test("Malformed collection file.", async () => {
const args = `test ${getTestJsonFilePath( const cmd = `node ./bin/hopp test ${getTestJsonFilePath(
"malformed-collection2.json" "malformed-collection2.json"
)}`; )}`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION"); expect(out).toBe<HoppErrorCode>("MALFORMED_COLLECTION");
}); });
test("Invalid arguement.", async () => { test("Invalid arguement.", async () => {
const args = "invalid-arg"; const cmd = `node ./bin/hopp invalid-arg`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Collection file not JSON type.", async () => { test("Collection file not JSON type.", async () => {
const args = `test ${getTestJsonFilePath("notjson.txt")}`; const cmd = `node ./bin/hopp test ${getTestJsonFilePath("notjson.txt")}`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE"); expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
}); });
test("Some errors occured (exit code 1).", async () => { test("Some errors occured (exit code 1).", async () => {
const args = `test ${getTestJsonFilePath("fails.json")}`; const cmd = `node ./bin/hopp test ${getTestJsonFilePath("fails.json")}`;
const { error } = await runCLI(args); const { error } = await execAsync(cmd);
expect(error).not.toBeNull(); expect(error).not.toBeNull();
expect(error).toMatchObject(<ExecException>{ expect(error).toMatchObject(<ExecException>{
@@ -67,92 +66,75 @@ describe("Test 'hopp test <file>' command:", () => {
}); });
test("No errors occured (exit code 0).", async () => { test("No errors occured (exit code 0).", async () => {
const args = `test ${getTestJsonFilePath("passes.json")}`; const cmd = `node ./bin/hopp test ${getTestJsonFilePath("passes.json")}`;
const { error } = await runCLI(args); const { error } = await execAsync(cmd);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
test("Supports inheriting headers and authorization set at the root collection", async () => {
const args = `test ${getTestJsonFilePath("collection-level-headers-auth.json")}`;
const { error } = await runCLI(args);
expect(error).toBeNull();
})
}); });
describe("Test 'hopp test <file> --env <file>' command:", () => { describe("Test 'hopp test <file> --env <file>' command:", () => {
const VALID_TEST_ARGS = `test ${getTestJsonFilePath( const VALID_TEST_CMD = `node ./bin/hopp test ${getTestJsonFilePath(
"passes.json" "passes.json"
)}`; )}`;
test("No env file path provided.", async () => { test("No env file path provided.", async () => {
const args = `${VALID_TEST_ARGS} --env`; const cmd = `${VALID_TEST_CMD} --env`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("ENV file not JSON type.", async () => { test("ENV file not JSON type.", async () => {
const args = `${VALID_TEST_ARGS} --env ${getTestJsonFilePath("notjson.txt")}`; const cmd = `${VALID_TEST_CMD} --env ${getTestJsonFilePath("notjson.txt")}`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE"); expect(out).toBe<HoppErrorCode>("INVALID_FILE_TYPE");
}); });
test("ENV file not found.", async () => { test("ENV file not found.", async () => {
const args = `${VALID_TEST_ARGS} --env notfound.json`; const cmd = `${VALID_TEST_CMD} --env notfound.json`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND"); expect(out).toBe<HoppErrorCode>("FILE_NOT_FOUND");
}); });
test("No errors occured (exit code 0).", async () => { test("No errors occured (exit code 0).", async () => {
const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json"); const TESTS_PATH = getTestJsonFilePath("env-flag-tests.json");
const ENV_PATH = getTestJsonFilePath("env-flag-envs.json"); const ENV_PATH = getTestJsonFilePath("env-flag-envs.json");
const args = `test ${TESTS_PATH} --env ${ENV_PATH}`; const cmd = `node ./bin/hopp test ${TESTS_PATH} --env ${ENV_PATH}`;
const { error, stdout } = await execAsync(cmd);
const { error } = await runCLI(args);
expect(error).toBeNull();
});
test("Correctly resolves environment variables referenced in the request body", async () => {
const COLL_PATH = getTestJsonFilePath("req-body-env-vars-coll.json");
const ENVS_PATH = getTestJsonFilePath("req-body-env-vars-envs.json");
const args = `test ${COLL_PATH} --env ${ENVS_PATH}`;
const { error } = await runCLI(args);
expect(error).toBeNull(); expect(error).toBeNull();
}); });
}); });
describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => { describe("Test 'hopp test <file> --delay <delay_in_ms>' command:", () => {
const VALID_TEST_ARGS = `test ${getTestJsonFilePath( const VALID_TEST_CMD = `node ./bin/hopp test ${getTestJsonFilePath(
"passes.json" "passes.json"
)}`; )}`;
test("No value passed to delay flag.", async () => { test("No value passed to delay flag.", async () => {
const args = `${VALID_TEST_ARGS} --delay`; const cmd = `${VALID_TEST_CMD} --delay`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Invalid value passed to delay flag.", async () => { test("Invalid value passed to delay flag.", async () => {
const args = `${VALID_TEST_ARGS} --delay 'NaN'`; const cmd = `${VALID_TEST_CMD} --delay 'NaN'`;
const { stderr } = await runCLI(args); const { stderr } = await execAsync(cmd);
const out = getErrorCode(stderr); const out = getErrorCode(stderr);
expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT"); expect(out).toBe<HoppErrorCode>("INVALID_ARGUMENT");
}); });
test("Valid value passed to delay flag.", async () => { test("Valid value passed to delay flag.", async () => {
const args = `${VALID_TEST_ARGS} --delay 1`; const cmd = `${VALID_TEST_CMD} --delay 1`;
const { error } = await runCLI(args); const { error } = await execAsync(cmd);
expect(error).toBeNull(); expect(error).toBeNull();
}); });

View File

@@ -1,221 +0,0 @@
[
{
"v": 1,
"name": "CollectionA",
"folders": [
{
"v": 1,
"name": "FolderA",
"folders": [
{
"v": 1,
"name": "FolderB",
"folders": [
{
"v": 1,
"name": "FolderC",
"folders": [],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestD",
"params": [],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Overriden at RequestD"
}
],
"method": "GET",
"auth": {
"authType": "basic",
"authActive": true,
"username": "username",
"password": "password"
},
"preRequestScript": "",
"testScript": "pw.test(\"Overrides auth and headers set at the parent folder\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Overriden at RequestD\");\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Basic dXNlcm5hbWU6cGFzc3dvcmQ=\");\n});",
"body": {
"contentType": null,
"body": null
}
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestC",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"testScript": "pw.test(\"Correctly inherits auth and headers from the parent folder\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Overriden at FolderB\");\n pw.expect(pw.response.body.headers[\"key\"]).toBe(\"test-key\");\n});",
"body": {
"contentType": null,
"body": null
}
}
],
"auth": {
"authType": "api-key",
"authActive": true,
"addTo": "Headers",
"key": "key",
"value": "test-key"
},
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Overriden at FolderB"
}
]
}
],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestB",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"testScript": "pw.test(\"Correctly inherits auth and headers from the parent folder\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Set at root collection\");\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer BearerToken\");\n});",
"body": {
"contentType": null,
"body": null
},
"id": "clpttpdq00003qp16kut6doqv"
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestA",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"testScript": "pw.test(\"Correctly inherits auth and headers from the root collection\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Set at root collection\");\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer BearerToken\");\n});",
"body": {
"contentType": null,
"body": null
},
"id": "clpttpdq00003qp16kut6doqv"
}
],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Set at root collection"
}
],
"auth": {
"authType": "bearer",
"authActive": true,
"token": "BearerToken"
}
},
{
"v": 1,
"name": "CollectionB",
"folders": [
{
"v": 1,
"name": "FolderA",
"folders": [],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestB",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"testScript": "pw.test(\"Correctly inherits auth and headers from the parent folder\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Set at root collection\");\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer BearerToken\");\n});",
"body": {
"contentType": null,
"body": null
},
"id": "clpttpdq00003qp16kut6doqv"
}
],
"auth": {
"authType": "inherit",
"authActive": true
},
"headers": []
}
],
"requests": [
{
"v": "1",
"endpoint": "https://echo.hoppscotch.io",
"name": "RequestA",
"params": [],
"headers": [],
"method": "GET",
"auth": {
"authType": "inherit",
"authActive": true
},
"preRequestScript": "",
"testScript": "pw.test(\"Correctly inherits auth and headers from the root collection\", ()=> {\n pw.expect(pw.response.body.headers[\"x-test-header\"]).toBe(\"Set at root collection\");\n pw.expect(pw.response.body.headers[\"authorization\"]).toBe(\"Bearer BearerToken\");\n});",
"body": {
"contentType": null,
"body": null
},
"id": "clpttpdq00003qp16kut6doqv"
}
],
"headers": [
{
"active": true,
"key": "X-Test-Header",
"value": "Set at root collection"
}
],
"auth": {
"authType": "bearer",
"authActive": true,
"token": "BearerToken"
}
}
]

View File

@@ -1,30 +0,0 @@
{
"v": 2,
"name": "Test environment variables in request body",
"folders": [],
"requests": [
{
"v": "1",
"name": "test-request",
"endpoint": "https://echo.hoppscotch.io",
"method": "POST",
"headers": [],
"params": [],
"auth": {
"authType": "none",
"authActive": true
},
"body": {
"contentType": "application/json",
"body": "{\n \"firstName\": \"<<firstName>>\",\n \"lastName\": \"<<lastName>>\",\n \"greetText\": \"<<salutation>>, <<fullName>>\",\n \"fullName\": \"<<fullName>>\",\n \"id\": \"<<id>>\"\n}"
},
"preRequestScript": "",
"testScript": "pw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\npw.test(\"Successfully resolves environments recursively\", ()=> {\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n});\n\npw.test(\"Successfully resolves environments referenced in the request body\", () => {\n const expectedId = \"7\"\n const expectedFirstName = \"John\"\n const expectedLastName = \"Doe\"\n const expectedFullName = `${expectedFirstName} ${expectedLastName}`\n const expectedGreetText = `Hello, ${expectedFullName}`\n\n pw.expect(pw.env.getResolve(\"recursiveVarX\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"recursiveVarY\")).toBe(\"Hello\")\n pw.expect(pw.env.getResolve(\"salutation\")).toBe(\"Hello\")\n\n const { id, firstName, lastName, fullName, greetText } = JSON.parse(pw.response.body.data)\n\n pw.expect(id).toBe(expectedId)\n pw.expect(expectedFirstName).toBe(firstName)\n pw.expect(expectedLastName).toBe(lastName)\n pw.expect(fullName).toBe(expectedFullName)\n pw.expect(greetText).toBe(expectedGreetText)\n});"
}
],
"auth": {
"authType": "none",
"authActive": true
},
"headers": []
}

View File

@@ -1,37 +0,0 @@
{
"name": "Response body sample",
"variables": [
{
"key": "firstName",
"value": "John"
},
{
"key": "lastName",
"value": "Doe"
},
{
"key": "id",
"value": "7"
},
{
"key": "fullName",
"value": "<<firstName>> <<lastName>>"
},
{
"key": "recursiveVarX",
"value": "<<recursiveVarY>>"
},
{
"key": "recursiveVarY",
"value": "<<salutation>>"
},
{
"key": "salutation",
"value": "Hello"
},
{
"key": "greetText",
"value": "<<salutation>> <<fullName>>"
}
]
}

View File

@@ -1,17 +1,10 @@
import { exec } from "child_process"; import { exec } from "child_process";
import { resolve } from "path";
import { ExecResponse } from "./types"; import { ExecResponse } from "./types";
export const runCLI = (args: string): Promise<ExecResponse> => export const execAsync = (command: string): Promise<ExecResponse> =>
{ new Promise((resolve) =>
const CLI_PATH = resolve(__dirname, "../../bin/hopp"); exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr }))
const command = `node ${CLI_PATH} ${args}` );
return new Promise((resolve) =>
exec(command, (error, stdout, stderr) => resolve({ error, stdout, stderr }))
);
}
export const trimAnsi = (target: string) => { export const trimAnsi = (target: string) => {
const ansiRegex = const ansiRegex =
@@ -22,10 +15,12 @@ export const trimAnsi = (target: string) => {
export const getErrorCode = (out: string) => { export const getErrorCode = (out: string) => {
const ansiTrimmedStr = trimAnsi(out); const ansiTrimmedStr = trimAnsi(out);
return ansiTrimmedStr.split(" ")[0]; return ansiTrimmedStr.split(" ")[0];
}; };
export const getTestJsonFilePath = (file: string) => { export const getTestJsonFilePath = (file: string) => {
const filePath = resolve(__dirname, `../../src/__tests__/samples/${file}`); const filePath = `${process.cwd()}/src/__tests__/samples/${file}`;
return filePath; return filePath;
}; };

View File

@@ -1,5 +1,5 @@
import chalk from "chalk"; import chalk from "chalk";
import { Command } from "commander"; import { program } from "commander";
import * as E from "fp-ts/Either"; import * as E from "fp-ts/Either";
import { version } from "../package.json"; import { version } from "../package.json";
import { test } from "./commands/test"; import { test } from "./commands/test";
@@ -20,8 +20,6 @@ const CLI_AFTER_ALL_TXT = `\nFor more help, head on to ${accent(
"https://docs.hoppscotch.io/documentation/clients/cli" "https://docs.hoppscotch.io/documentation/clients/cli"
)}`; )}`;
const program = new Command()
program program
.name("hopp") .name("hopp")
.version(version, "-v, --ver", "see the current version of hopp-cli") .version(version, "-v, --ver", "see the current version of hopp-cli")

View File

@@ -37,7 +37,8 @@ export async function parseEnvsData(path: string) {
envPairs.push({ key, value }); envPairs.push({ key, value });
} }
} else if (HoppEnvExportObjectResult.success) { } else if (HoppEnvExportObjectResult.success) {
envPairs.push(...HoppEnvExportObjectResult.data.variables); const { key, value } = HoppEnvExportObjectResult.data.variables[0];
envPairs.push({ key, value });
} }
return <HoppEnvs>{ global: [], selected: envPairs }; return <HoppEnvs>{ global: [], selected: envPairs };

View File

@@ -1,8 +1,8 @@
import { HoppCollection } from "@hoppscotch/data"; import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import { HoppEnvs } from "./request"; import { HoppEnvs } from "./request";
export type CollectionRunnerParam = { export type CollectionRunnerParam = {
collections: HoppCollection[]; collections: HoppCollection<HoppRESTRequest>[];
envs: HoppEnvs; envs: HoppEnvs;
delay?: number; delay?: number;
}; };

View File

@@ -33,7 +33,7 @@ export type HoppEnvs = {
export type CollectionStack = { export type CollectionStack = {
path: string; path: string;
collection: HoppCollection; collection: HoppCollection<HoppRESTRequest>;
}; };
export type RequestReport = { export type RequestReport = {

View File

@@ -1,4 +1,8 @@
import { HoppCollection, isHoppRESTRequest } from "@hoppscotch/data"; import {
HoppCollection,
HoppRESTRequest,
isHoppRESTRequest,
} from "@hoppscotch/data";
import * as A from "fp-ts/Array"; import * as A from "fp-ts/Array";
import { CommanderError } from "commander"; import { CommanderError } from "commander";
import { HoppCLIError, HoppErrnoException } from "../types/errors"; import { HoppCLIError, HoppErrnoException } from "../types/errors";
@@ -20,7 +24,9 @@ export const hasProperty = <P extends PropertyKey>(
* @returns True, if unknown parameter is valid Hoppscotch REST Collection; * @returns True, if unknown parameter is valid Hoppscotch REST Collection;
* False, otherwise. * False, otherwise.
*/ */
export const isRESTCollection = (param: unknown): param is HoppCollection => { export const isRESTCollection = (
param: unknown
): param is HoppCollection<HoppRESTRequest> => {
if (!!param && typeof param === "object") { if (!!param && typeof param === "object") {
if (!hasProperty(param, "v") || typeof param.v !== "number") { if (!hasProperty(param, "v") || typeof param.v !== "number") {
return false; return false;
@@ -56,6 +62,7 @@ export const isRESTCollection = (param: unknown): param is HoppCollection => {
return false; return false;
}; };
/** /**
* Checks if given error data is of type HoppCLIError, based on existence * Checks if given error data is of type HoppCLIError, based on existence
* of code property. * of code property.

View File

@@ -1,23 +1,21 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import chalk from "chalk";
import { log } from "console";
import * as A from "fp-ts/Array"; import * as A from "fp-ts/Array";
import { pipe } from "fp-ts/function"; import { pipe } from "fp-ts/function";
import { round } from "lodash-es"; import { bold } from "chalk";
import { log } from "console";
import { CollectionRunnerParam } from "../types/collections"; import round from "lodash/round";
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import { import {
CollectionStack,
HoppEnvs, HoppEnvs,
ProcessRequestParams, CollectionStack,
RequestReport, RequestReport,
ProcessRequestParams,
} from "../types/request"; } from "../types/request";
import { import {
PreRequestMetrics, getRequestMetrics,
RequestMetrics, preProcessRequest,
TestMetrics, processRequest,
} from "../types/response"; } from "./request";
import { DEFAULT_DURATION_PRECISION } from "./constants"; import { exceptionColors } from "./getters";
import { import {
printErrorsReport, printErrorsReport,
printFailedTestsReport, printFailedTestsReport,
@@ -25,14 +23,15 @@ import {
printRequestsMetrics, printRequestsMetrics,
printTestsMetrics, printTestsMetrics,
} from "./display"; } from "./display";
import { exceptionColors } from "./getters";
import { getPreRequestMetrics } from "./pre-request";
import { import {
getRequestMetrics, PreRequestMetrics,
preProcessRequest, RequestMetrics,
processRequest, TestMetrics,
} from "./request"; } from "../types/response";
import { getTestMetrics } from "./test"; import { getTestMetrics } from "./test";
import { DEFAULT_DURATION_PRECISION } from "./constants";
import { getPreRequestMetrics } from "./pre-request";
import { CollectionRunnerParam } from "../types/collections";
const { WARN, FAIL } = exceptionColors; const { WARN, FAIL } = exceptionColors;
@@ -42,23 +41,23 @@ const { WARN, FAIL } = exceptionColors;
* @param param Data of hopp-collection with hopp-requests, envs to be processed. * @param param Data of hopp-collection with hopp-requests, envs to be processed.
* @returns List of report for each processed request. * @returns List of report for each processed request.
*/ */
export const collectionsRunner = async ( export const collectionsRunner =
param: CollectionRunnerParam async (param: CollectionRunnerParam): Promise<RequestReport[]> =>
): Promise<RequestReport[]> => { {
const envs: HoppEnvs = param.envs; const envs: HoppEnvs = param.envs;
const delay = param.delay ?? 0; const delay = param.delay ?? 0;
const requestsReport: RequestReport[] = []; const requestsReport: RequestReport[] = [];
const collectionStack: CollectionStack[] = getCollectionStack( const collectionStack: CollectionStack[] = getCollectionStack(
param.collections param.collections
); );
while (collectionStack.length) { while (collectionStack.length) {
// Pop out top-most collection from stack to be processed. // Pop out top-most collection from stack to be processed.
const { collection, path } = <CollectionStack>collectionStack.pop(); const { collection, path } = <CollectionStack>collectionStack.pop();
// Processing each request in collection // Processing each request in collection
for (const request of collection.requests) { for (const request of collection.requests) {
const _request = preProcessRequest(request as HoppRESTRequest, collection); const _request = preProcessRequest(request);
const requestPath = `${path}/${_request.name}`; const requestPath = `${path}/${_request.name}`;
const processRequestParams: ProcessRequestParams = { const processRequestParams: ProcessRequestParams = {
path: requestPath, path: requestPath,
@@ -68,15 +67,15 @@ export const collectionsRunner = async (
}; };
// Request processing initiated message. // Request processing initiated message.
log(WARN(`\nRunning: ${chalk.bold(requestPath)}`)); log(WARN(`\nRunning: ${bold(requestPath)}`));
// Processing current request. // Processing current request.
const result = await processRequest(processRequestParams)(); const result = await processRequest(processRequestParams)();
// Updating global & selected envs with new envs from processed-request output. // Updating global & selected envs with new envs from processed-request output.
const { global, selected } = result.envs; const { global, selected } = result.envs;
envs.global = global; envs.global = global;
envs.selected = selected; envs.selected = selected;
// Storing current request's report. // Storing current request's report.
const requestReport = result.report; const requestReport = result.report;
@@ -85,30 +84,15 @@ export const collectionsRunner = async (
// Pushing remaining folders realted collection to stack. // Pushing remaining folders realted collection to stack.
for (const folder of collection.folders) { for (const folder of collection.folders) {
const updatedFolder: HoppCollection = { ...folder }
if (updatedFolder.auth?.authType === "inherit") {
updatedFolder.auth = collection.auth;
}
if (collection.headers?.length) {
// Filter out header entries present in the parent collection under the same name
// This ensures the folder headers take precedence over the collection headers
const filteredHeaders = collection.headers.filter((collectionHeaderEntries) => {
return !updatedFolder.headers.some((folderHeaderEntries) => folderHeaderEntries.key === collectionHeaderEntries.key)
})
updatedFolder.headers.push(...filteredHeaders);
}
collectionStack.push({ collectionStack.push({
path: `${path}/${updatedFolder.name}`, path: `${path}/${folder.name}`,
collection: updatedFolder, collection: folder,
}); });
} }
} }
return requestsReport; return requestsReport;
}; };
/** /**
* Transforms collections to generate collection-stack which describes each collection's * Transforms collections to generate collection-stack which describes each collection's
@@ -116,7 +100,9 @@ export const collectionsRunner = async (
* @param collections Hopp-collection objects to be mapped to collection-stack type. * @param collections Hopp-collection objects to be mapped to collection-stack type.
* @returns Mapped collections to collection-stack. * @returns Mapped collections to collection-stack.
*/ */
const getCollectionStack = (collections: HoppCollection[]): CollectionStack[] => const getCollectionStack = (
collections: HoppCollection<HoppRESTRequest>[]
): CollectionStack[] =>
pipe( pipe(
collections, collections,
A.map( A.map(

View File

@@ -1,4 +1,4 @@
import chalk from "chalk"; import { bold } from "chalk";
import { groupEnd, group, log } from "console"; import { groupEnd, group, log } from "console";
import { handleError } from "../handlers/error"; import { handleError } from "../handlers/error";
import { RequestConfig } from "../interfaces/request"; import { RequestConfig } from "../interfaces/request";
@@ -120,7 +120,7 @@ export const printErrorsReport = (
errorsReport: HoppCLIError[] errorsReport: HoppCLIError[]
) => { ) => {
if (errorsReport.length > 0) { if (errorsReport.length > 0) {
const REPORTED_ERRORS_TITLE = FAIL(`\n${chalk.bold(path)} reported errors:`); const REPORTED_ERRORS_TITLE = FAIL(`\n${bold(path)} reported errors:`);
group(REPORTED_ERRORS_TITLE); group(REPORTED_ERRORS_TITLE);
for (const errorReport of errorsReport) { for (const errorReport of errorsReport) {
@@ -143,7 +143,7 @@ export const printFailedTestsReport = (
// Only printing test-reports with failed test-cases. // Only printing test-reports with failed test-cases.
if (failedTestsReport.length > 0) { if (failedTestsReport.length > 0) {
const FAILED_TESTS_PATH = FAIL(`\n${chalk.bold(path)} failed tests:`); const FAILED_TESTS_PATH = FAIL(`\n${bold(path)} failed tests:`);
group(FAILED_TESTS_PATH); group(FAILED_TESTS_PATH);
for (const failedTestReport of failedTestsReport) { for (const failedTestReport of failedTestsReport) {

View File

@@ -1,4 +1,4 @@
import { clone } from "lodash-es"; import { clone } from "lodash";
/** /**
* Sorts the array based on the sort func. * Sorts the array based on the sort func.

View File

@@ -11,7 +11,7 @@ import * as E from "fp-ts/Either";
import * as S from "fp-ts/string"; import * as S from "fp-ts/string";
import * as O from "fp-ts/Option"; import * as O from "fp-ts/Option";
import { error } from "../types/errors"; import { error } from "../types/errors";
import { round } from "lodash-es"; import round from "lodash/round";
import { DEFAULT_DURATION_PRECISION } from "./constants"; import { DEFAULT_DURATION_PRECISION } from "./constants";
/** /**

View File

@@ -2,7 +2,7 @@ import fs from "fs/promises";
import { FormDataEntry } from "../types/request"; import { FormDataEntry } from "../types/request";
import { error } from "../types/errors"; import { error } from "../types/errors";
import { isRESTCollection, isHoppErrnoException } from "./checks"; import { isRESTCollection, isHoppErrnoException } from "./checks";
import { HoppCollection } from "@hoppscotch/data"; import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
/** /**
* Parses array of FormDataEntry to FormData. * Parses array of FormDataEntry to FormData.
@@ -35,20 +35,20 @@ export const parseErrorMessage = (e: unknown) => {
}; };
export async function readJsonFile(path: string): Promise<unknown> { export async function readJsonFile(path: string): Promise<unknown> {
if (!path.endsWith(".json")) { if(!path.endsWith('.json')) {
throw error({ code: "INVALID_FILE_TYPE", data: path }); throw error({ code: "INVALID_FILE_TYPE", data: path })
} }
try { try {
await fs.access(path); await fs.access(path)
} catch (e) { } catch (e) {
throw error({ code: "FILE_NOT_FOUND", path: path }); throw error({ code: "FILE_NOT_FOUND", path: path })
} }
try { try {
return JSON.parse((await fs.readFile(path)).toString()); return JSON.parse((await fs.readFile(path)).toString())
} catch (e) { } catch(e) {
throw error({ code: "UNKNOWN_ERROR", data: e }); throw error({ code: "UNKNOWN_ERROR", data: e })
} }
} }
@@ -56,24 +56,22 @@ export async function readJsonFile(path: string): Promise<unknown> {
* Parses collection json file for given path:context.path, and validates * Parses collection json file for given path:context.path, and validates
* the parsed collectiona array. * the parsed collectiona array.
* @param path Collection json file path. * @param path Collection json file path.
* @returns For successful parsing we get array of HoppCollection, * @returns For successful parsing we get array of HoppCollection<HoppRESTRequest>,
*/ */
export async function parseCollectionData( export async function parseCollectionData(
path: string path: string
): Promise<HoppCollection[]> { ): Promise<HoppCollection<HoppRESTRequest>[]> {
let contents = await readJsonFile(path); let contents = await readJsonFile(path)
const maybeArrayOfCollections: unknown[] = Array.isArray(contents) const maybeArrayOfCollections: unknown[] = Array.isArray(contents) ? contents : [contents]
? contents
: [contents];
if (maybeArrayOfCollections.some((x) => !isRESTCollection(x))) { if(maybeArrayOfCollections.some((x) => !isRESTCollection(x))) {
throw error({ throw error({
code: "MALFORMED_COLLECTION", code: "MALFORMED_COLLECTION",
path, path,
data: "Please check the collection data.", data: "Please check the collection data.",
}); })
} }
return maybeArrayOfCollections as HoppCollection[]; return maybeArrayOfCollections as HoppCollection<HoppRESTRequest>[]
} };

View File

@@ -1,31 +1,31 @@
import { HoppCollection, HoppRESTRequest } from "@hoppscotch/data";
import axios, { Method } from "axios"; import axios, { Method } from "axios";
import * as A from "fp-ts/Array";
import * as E from "fp-ts/Either";
import * as T from "fp-ts/Task";
import * as TE from "fp-ts/TaskEither";
import { pipe } from "fp-ts/function";
import * as S from "fp-ts/string";
import { hrtime } from "process";
import { URL } from "url"; import { URL } from "url";
import { EffectiveHoppRESTRequest, RequestConfig } from "../interfaces/request"; import * as S from "fp-ts/string";
import * as A from "fp-ts/Array";
import * as T from "fp-ts/Task";
import * as E from "fp-ts/Either";
import * as TE from "fp-ts/TaskEither";
import { HoppRESTRequest } from "@hoppscotch/data";
import { responseErrors } from "./constants";
import { getDurationInSeconds, getMetaDataPairs } from "./getters";
import { testRunner, getTestScriptParams, hasFailedTestCases } from "./test";
import { RequestConfig, EffectiveHoppRESTRequest } from "../interfaces/request";
import { RequestRunnerResponse } from "../interfaces/response"; import { RequestRunnerResponse } from "../interfaces/response";
import { HoppCLIError, error } from "../types/errors"; import { preRequestScriptRunner } from "./pre-request";
import { import {
HoppEnvs, HoppEnvs,
ProcessRequestParams, ProcessRequestParams,
RequestReport, RequestReport,
} from "../types/request"; } from "../types/request";
import { RequestMetrics } from "../types/response";
import { responseErrors } from "./constants";
import { import {
printPreRequestRunner, printPreRequestRunner,
printRequestRunner, printRequestRunner,
printTestRunner, printTestRunner,
} from "./display"; } from "./display";
import { getDurationInSeconds, getMetaDataPairs } from "./getters"; import { error, HoppCLIError } from "../types/errors";
import { preRequestScriptRunner } from "./pre-request"; import { hrtime } from "process";
import { getTestScriptParams, hasFailedTestCases, testRunner } from "./test"; import { RequestMetrics } from "../types/response";
import { pipe } from "fp-ts/function";
// !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported // !NOTE: The `config.supported` checks are temporary until OAuth2 and Multipart Forms are supported
@@ -309,12 +309,9 @@ export const processRequest =
* @returns Updated request object free of invalid/missing data. * @returns Updated request object free of invalid/missing data.
*/ */
export const preProcessRequest = ( export const preProcessRequest = (
request: HoppRESTRequest, request: HoppRESTRequest
collection: HoppCollection,
): HoppRESTRequest => { ): HoppRESTRequest => {
const tempRequest = Object.assign({}, request); const tempRequest = Object.assign({}, request);
const { headers: parentHeaders, auth: parentAuth } = collection;
if (!tempRequest.v) { if (!tempRequest.v) {
tempRequest.v = "1"; tempRequest.v = "1";
} }
@@ -330,31 +327,18 @@ export const preProcessRequest = (
if (!tempRequest.params) { if (!tempRequest.params) {
tempRequest.params = []; tempRequest.params = [];
} }
if (!tempRequest.headers) {
if (parentHeaders?.length) {
// Filter out header entries present in the parent (folder/collection) under the same name
// This ensures the child headers take precedence over the parent headers
const filteredEntries = parentHeaders.filter((parentHeaderEntries) => {
return !tempRequest.headers.some((reqHeaderEntries) => reqHeaderEntries.key === parentHeaderEntries.key)
})
tempRequest.headers.push(...filteredEntries);
} else if (!tempRequest.headers) {
tempRequest.headers = []; tempRequest.headers = [];
} }
if (!tempRequest.preRequestScript) { if (!tempRequest.preRequestScript) {
tempRequest.preRequestScript = ""; tempRequest.preRequestScript = "";
} }
if (!tempRequest.testScript) { if (!tempRequest.testScript) {
tempRequest.testScript = ""; tempRequest.testScript = "";
} }
if (!tempRequest.auth) {
if (tempRequest.auth?.authType === "inherit") {
tempRequest.auth = parentAuth;
} else if (!tempRequest.auth) {
tempRequest.auth = { authActive: false, authType: "none" }; tempRequest.auth = { authActive: false, authType: "none" };
} }
if (!tempRequest.body) { if (!tempRequest.body) {
tempRequest.body = { contentType: null, body: null }; tempRequest.body = { contentType: null, body: null };
} }

View File

@@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ES6",
"module": "ESNext", "module": "commonjs",
"outDir": ".", "outDir": ".",
"rootDir": ".", "rootDir": ".",
"strict": true, "strict": true,

View File

@@ -3,14 +3,17 @@ import { defineConfig } from "tsup";
export default defineConfig({ export default defineConfig({
entry: [ "./src/index.ts" ], entry: [ "./src/index.ts" ],
outDir: "./dist/", outDir: "./dist/",
format: ["esm"], format: ["cjs"],
platform: "node", platform: "node",
sourcemap: true, sourcemap: true,
bundle: true, bundle: true,
target: "esnext", target: "node12",
skipNodeModulesBundle: false, skipNodeModulesBundle: false,
esbuildOptions(options) { esbuildOptions(options) {
options.bundle = true options.bundle = true
}, },
noExternal: [
/\w+/
],
clean: true, clean: true,
}); });

View File

@@ -158,7 +158,7 @@ a {
@apply shadow-none #{!important}; @apply shadow-none #{!important};
@apply fixed; @apply fixed;
@apply inline-flex; @apply inline-flex;
@apply -mt-7; @apply -mt-8;
} }
} }
@@ -368,7 +368,6 @@ pre.ace_editor {
.toasted-container { .toasted-container {
@apply max-w-md; @apply max-w-md;
@apply z-[10000];
.toasted { .toasted {
&.toasted-primary { &.toasted-primary {
@@ -517,10 +516,9 @@ pre.ace_editor {
@apply bg-dividerLight; @apply bg-dividerLight;
@apply rounded; @apply rounded;
@apply ml-2; @apply ml-2;
@apply px-0.5; @apply px-1;
@apply min-w-[1rem]; @apply min-w-[1.25rem];
@apply min-h-[1rem]; @apply min-h-[1.25rem];
@apply leading-none;
@apply items-center; @apply items-center;
@apply justify-center; @apply justify-center;
@apply border border-dividerDark; @apply border border-dividerDark;

View File

@@ -17,7 +17,6 @@
--lower-tertiary-sticky-fold: 7.125rem; --lower-tertiary-sticky-fold: 7.125rem;
--lower-fourth-sticky-fold: 9.188rem; --lower-fourth-sticky-fold: 9.188rem;
--sidebar-primary-sticky-fold: 2rem; --sidebar-primary-sticky-fold: 2rem;
--properties-primary-sticky-fold: 2.063rem;
} }
@mixin light-theme { @mixin light-theme {

View File

@@ -78,6 +78,12 @@
"iso": "he-HE", "iso": "he-HE",
"name": "עִברִית" "name": "עִברִית"
}, },
{
"code": "hi",
"file": "hi.json",
"iso": "hi-HI",
"name": "हिन्दी"
},
{ {
"code": "hu", "code": "hu",
"file": "hu.json", "file": "hu.json",

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Kanselleer", "cancel": "Kanselleer",
"choose_file": "Kies 'n lêer", "choose_file": "Kies 'n lêer",
@@ -11,7 +10,6 @@
"connect": "Koppel", "connect": "Koppel",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopieer", "copy": "Kopieer",
"create": "Create",
"delete": "Vee uit", "delete": "Vee uit",
"disconnect": "Ontkoppel", "disconnect": "Ontkoppel",
"dismiss": "Weier", "dismiss": "Weier",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Verwyder", "remove": "Verwyder",
"rename": "Rename", "rename": "Rename",
"restore": "Herstel", "restore": "Herstel",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Soek", "search": "Soek",
"send": "Stuur", "send": "Stuur",
"share": "Share",
"start": "Begin", "start": "Begin",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Gesels met ons", "chat_with_us": "Gesels met ons",
"contact_us": "Kontak Ons", "contact_us": "Kontak Ons",
"cookies": "Cookies",
"copy": "Kopieer", "copy": "Kopieer",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Sleutelbord kortpaaie", "keyboard_shortcuts": "Sleutelbord kortpaaie",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Nuwe weergawe gevind. Herlaai om op te dateer.", "new_version_found": "Nuwe weergawe gevind. Herlaai om op te dateer.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Volmag privaatheidsbeleid", "proxy_privacy_policy": "Volmag privaatheidsbeleid",
"reload": "Herlaai", "reload": "Herlaai",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Genereer teken", "generate_token": "Genereer teken",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Sluit in by URL", "include_in_url": "Sluit in by URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Leer hoe", "learn": "Leer hoe",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Wagwoord", "password": "Wagwoord",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Teken", "token": "Teken",
"type": "Magtigingstipe", "type": "Magtigingstipe",
"username": "Gebruikersnaam" "username": "Gebruikersnaam"
@@ -148,7 +124,6 @@
"created": "Versameling geskep", "created": "Versameling geskep",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Wysig versameling", "edit": "Wysig versameling",
"import_or_create": "Import or create a collection",
"invalid_name": "Gee 'n geldige naam vir die versameling", "invalid_name": "Gee 'n geldige naam vir die versameling",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Nuwe versameling", "new": "Nuwe versameling",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Versameling hernoem", "renamed": "Versameling hernoem",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Stoor as", "save_as": "Stoor as",
@@ -178,7 +151,6 @@
"remove_folder": "Weet u seker dat u hierdie vouer permanent wil uitvee?", "remove_folder": "Weet u seker dat u hierdie vouer permanent wil uitvee?",
"remove_history": "Is u seker dat u alle geskiedenis permanent wil uitvee?", "remove_history": "Is u seker dat u alle geskiedenis permanent wil uitvee?",
"remove_request": "Is u seker dat u hierdie versoek permanent wil uitvee?", "remove_request": "Is u seker dat u hierdie versoek permanent wil uitvee?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Weet u seker dat u hierdie span wil uitvee?", "remove_team": "Weet u seker dat u hierdie span wil uitvee?",
"remove_telemetry": "Weet u seker dat u van Telemetry wil afskakel?", "remove_telemetry": "Weet u seker dat u van Telemetry wil afskakel?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Koptekst {count}", "header": "Koptekst {count}",
"message": "Boodskap {count}", "message": "Boodskap {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokolle is leeg", "protocols": "Protokolle is leeg",
"schema": "Koppel aan 'n GraphQL -eindpunt", "schema": "Koppel aan 'n GraphQL -eindpunt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Spannaam leeg", "team_name": "Spannaam leeg",
"teams": "Spanne is leeg", "teams": "Spanne is leeg",
"tests": "Daar is geen toetse vir hierdie versoek nie", "tests": "Daar is geen toetse vir hierdie versoek nie"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Gee 'n geldige naam vir die omgewing", "invalid_name": "Gee 'n geldige naam vir die omgewing",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Veranderlike lys" "variable_list": "Veranderlike lys"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.", "browser_support_sse": "Dit lyk nie asof hierdie blaaier ondersteuning vir bedieners gestuurde geleenthede het nie.",
"check_console_details": "Kyk na die konsole -log vir meer inligting.", "check_console_details": "Kyk na die konsole -log vir meer inligting.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL is nie behoorlik geformateer nie", "curl_invalid_format": "cURL is nie behoorlik geformateer nie",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Kon nie 'n ongeldige liggaam mooi maak nie, los json -sintaksisfoute op en probeer weer", "json_prettify_invalid_body": "Kon nie 'n ongeldige liggaam mooi maak nie, los json -sintaksisfoute op en probeer weer",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Kon nie versoek stuur nie", "network_fail": "Kon nie versoek stuur nie",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Geen duur nie", "no_duration": "Geen duur nie",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Kon nie voorafversoekskrip uitvoer nie", "script_fail": "Kon nie voorafversoekskrip uitvoer nie",
"something_went_wrong": "Iets het verkeerd geloop", "something_went_wrong": "Iets het verkeerd geloop",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Uitvoer as JSON", "as_json": "Uitvoer as JSON",
"create_secret_gist": "Skep geheime Gist", "create_secret_gist": "Skep geheime Gist",
"failed": "Something went wrong while exporting",
"gist_created": "Gis geskep", "gist_created": "Gis geskep",
"require_github": "Teken in met GitHub om 'n geheime idee te skep", "require_github": "Teken in met GitHub om 'n geheime idee te skep",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Inskrywings", "subscriptions": "Inskrywings",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Die magtigingskop sal outomaties gegenereer word wanneer u die versoek stuur.", "authorization": "Die magtigingskop sal outomaties gegenereer word wanneer u die versoek stuur.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Genereer eers dokumentasie", "generate_documentation_first": "Genereer eers dokumentasie",
"network_fail": "Kon nie die API -eindpunt bereik nie. Kontroleer u netwerkverbinding en probeer weer.", "network_fail": "Kon nie die API -eindpunt bereik nie. Kontroleer u netwerkverbinding en probeer weer.",
"offline": "Dit lyk asof u vanlyn is. Data in hierdie werkruimte is moontlik nie op datum nie.", "offline": "Dit lyk asof u vanlyn is. Data in hierdie werkruimte is moontlik nie op datum nie.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Voer versamelings in", "collections": "Voer versamelings in",
"curl": "Voer cURL in", "curl": "Voer cURL in",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Invoer misluk", "failed": "Invoer misluk",
"from_file": "Import from File",
"from_gist": "Invoer vanaf Gist", "from_gist": "Invoer vanaf Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Voer Gist URL in", "gist_url": "Voer Gist URL in",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Invoer" "title": "Invoer"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Versamelings", "collections": "Versamelings",
"confirm": "Bevestig", "confirm": "Bevestig",
"customize_request": "Customize Request",
"edit_request": "Wysig versoek", "edit_request": "Wysig versoek",
"import_export": "Invoer uitvoer", "import_export": "Invoer uitvoer"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopieer skakel",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Duur", "duration": "Duur",
"enter_curl": "Voer cURL in", "enter_curl": "Voer cURL in",
"generate_code": "Genereer kode", "generate_code": "Genereer kode",
"generated_code": "Kode gegenereer", "generated_code": "Kode gegenereer",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Koplys", "header_list": "Koplys",
"invalid_name": "Gee 'n naam vir die versoek", "invalid_name": "Gee 'n naam vir die versoek",
"method": "Metode", "method": "Metode",
@@ -552,14 +480,12 @@
"saved": "Versoek gestoor", "saved": "Versoek gestoor",
"share": "Deel", "share": "Deel",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Versoek", "title": "Versoek",
"type": "Soort versoek", "type": "Soort versoek",
"url": "URL", "url": "URL",
"variables": "Veranderlikes", "variables": "Veranderlikes",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopieer skakel"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Pas u rekeninginstellings aan.", "account_description": "Pas u rekeninginstellings aan.",
"account_email_description": "Jou primêre e -posadres.", "account_email_description": "Jou primêre e -posadres.",
"account_name_description": "Dit is u vertoonnaam.", "account_name_description": "Dit is u vertoonnaam.",
"additional": "Additional Settings",
"background": "Agtergrond", "background": "Agtergrond",
"black_mode": "Swart", "black_mode": "Swart",
"choose_language": "Kies taal", "choose_language": "Kies taal",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopieer versoekskakel",
"delete_method": "Kies DELETE metode", "delete_method": "Kies DELETE metode",
"get_method": "Kies GET -metode", "get_method": "Kies GET -metode",
"head_method": "Kies HOOF metode", "head_method": "Kies HOOF metode",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Stoor in versamelings", "save_to_collections": "Stoor in versamelings",
"send_request": "Stuur versoek", "send_request": "Stuur versoek",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Versoek", "title": "Versoek"
"copy_request_link": "Kopieer versoekskakel"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Na knipbord gekopieer", "copied_to_clipboard": "Na knipbord gekopieer",
"deleted": "Uitgevee", "deleted": "Uitgevee",
"deprecated": "GEDRAGTEER", "deprecated": "GEDRAGTEER",
@@ -836,12 +742,10 @@
"disconnected": "Ontkoppel", "disconnected": "Ontkoppel",
"disconnected_from": "Ontkoppel van {name}", "disconnected_from": "Ontkoppel van {name}",
"docs_generated": "Dokumentasie gegenereer", "docs_generated": "Dokumentasie gegenereer",
"download_failed": "Download failed",
"download_started": "Aflaai begin", "download_started": "Aflaai begin",
"enabled": "Geaktiveer", "enabled": "Geaktiveer",
"file_imported": "Lêer ingevoer", "file_imported": "Lêer ingevoer",
"finished_in": "Klaar in {duration} ms", "finished_in": "Klaar in {duration} ms",
"hide": "Hide",
"history_deleted": "Geskiedenis uitgevee", "history_deleted": "Geskiedenis uitgevee",
"linewrap": "Draai lyne toe", "linewrap": "Draai lyne toe",
"loading": "Laai tans ...", "loading": "Laai tans ...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Navrae", "queries": "Navrae",
"query": "Navraag", "query": "Navraag",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Toetse", "tests": "Toetse",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Verlaat span", "exit": "Verlaat span",
"exit_disabled": "Slegs eienaar kan nie die span verlaat nie", "exit_disabled": "Slegs eienaar kan nie die span verlaat nie",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Die e -posformaat is ongeldig", "invalid_email_format": "Die e -posformaat is ongeldig",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Span gered", "saved": "Span gered",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Spanne", "title": "Spanne",
"we_sent_invite_link": "We sent an invite link to all invitees!", "we_sent_invite_link": "We sent an invite link to all invitees!",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "الغاء", "cancel": "الغاء",
"choose_file": "اختيار ملف", "choose_file": "اختيار ملف",
@@ -11,7 +10,6 @@
"connect": "الاتصال", "connect": "الاتصال",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "نسخ", "copy": "نسخ",
"create": "Create",
"delete": "حذف", "delete": "حذف",
"disconnect": "قطع الاتصال", "disconnect": "قطع الاتصال",
"dismiss": "رفض", "dismiss": "رفض",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "لصق", "paste": "لصق",
"prettify": "جمال", "prettify": "جمال",
"properties": "Properties",
"remove": "ازالة", "remove": "ازالة",
"rename": "Rename", "rename": "Rename",
"restore": "اعادة", "restore": "اعادة",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "بحث", "search": "بحث",
"send": "ارسل", "send": "ارسل",
"share": "Share",
"start": "ابدأ", "start": "ابدأ",
"starting": "Starting", "starting": "Starting",
"stop": "قف", "stop": "قف",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "دردش معنا", "chat_with_us": "دردش معنا",
"contact_us": "اتصل بنا", "contact_us": "اتصل بنا",
"cookies": "Cookies",
"copy": "انسخ", "copy": "انسخ",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "اختصارات لوحة المفاتيح", "keyboard_shortcuts": "اختصارات لوحة المفاتيح",
"name": "هوبسكوتش", "name": "هوبسكوتش",
"new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث للتحديث.", "new_version_found": "تم العثور على نسخة جديدة. قم بالتحديث للتحديث.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "سياسة خصوصية الوكيل", "proxy_privacy_policy": "سياسة خصوصية الوكيل",
"reload": "إعادة تحميل", "reload": "إعادة تحميل",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "توليد رمز", "generate_token": "توليد رمز",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "تضمين في URL", "include_in_url": "تضمين في URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "تعلم كيف", "learn": "تعلم كيف",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "كلمة المرور", "password": "كلمة المرور",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "رمز", "token": "رمز",
"type": "نوع التفويض", "type": "نوع التفويض",
"username": "اسم المستخدم" "username": "اسم المستخدم"
@@ -148,7 +124,6 @@
"created": "تم إنشاء المجموعة", "created": "تم إنشاء المجموعة",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "تحرير المجموعة", "edit": "تحرير المجموعة",
"import_or_create": "Import or create a collection",
"invalid_name": "الرجاء تقديم اسم صالح للمجموعة", "invalid_name": "الرجاء تقديم اسم صالح للمجموعة",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "اسم المجموعة يجب ان لايقل على 3 رموز", "name_length_insufficient": "اسم المجموعة يجب ان لايقل على 3 رموز",
"new": "مجموعة جديدة", "new": "مجموعة جديدة",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "تمت إعادة تسمية المجموعة", "renamed": "تمت إعادة تسمية المجموعة",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "حفظ باسم", "save_as": "حفظ باسم",
@@ -178,7 +151,6 @@
"remove_folder": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟", "remove_folder": "هل أنت متأكد أنك تريد حذف هذا المجلد نهائيًا؟",
"remove_history": "هل أنت متأكد أنك تريد حذف كل المحفوظات بشكل دائم؟", "remove_history": "هل أنت متأكد أنك تريد حذف كل المحفوظات بشكل دائم؟",
"remove_request": "هل أنت متأكد أنك تريد حذف هذا الطلب نهائيًا؟", "remove_request": "هل أنت متأكد أنك تريد حذف هذا الطلب نهائيًا؟",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "هل أنت متأكد أنك تريد حذف هذا الفريق؟", "remove_team": "هل أنت متأكد أنك تريد حذف هذا الفريق؟",
"remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟", "remove_telemetry": "هل أنت متأكد أنك تريد الانسحاب من القياس عن بعد؟",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "رأس {count}", "header": "رأس {count}",
"message": "الرسالة {count}", "message": "الرسالة {count}",
@@ -238,13 +192,11 @@
"profile": "سجل الدخول لرؤية فريقك", "profile": "سجل الدخول لرؤية فريقك",
"protocols": "البروتوكولات فارغة", "protocols": "البروتوكولات فارغة",
"schema": "اتصل بنقطة نهاية GraphQL", "schema": "اتصل بنقطة نهاية GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "اسم الفريق فارغ", "team_name": "اسم الفريق فارغ",
"teams": "الفرق فارغة", "teams": "الفرق فارغة",
"tests": "لا توجد اختبارات لهذا الطلب", "tests": "لا توجد اختبارات لهذا الطلب"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "الرجاء تقديم اسم صالح للبيئة", "invalid_name": "الرجاء تقديم اسم صالح للبيئة",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "قائمة متغيرة" "variable_list": "قائمة متغيرة"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.", "browser_support_sse": "يبدو أن هذا المستعرض لا يدعم أحداث إرسال الخادم.",
"check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.", "check_console_details": "تحقق من سجل وحدة التحكم للحصول على التفاصيل.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح", "curl_invalid_format": "لم يتم تنسيق cURL بشكل صحيح",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى", "json_prettify_invalid_body": "تعذر تجميل جسم غير صالح وحل أخطاء بناء جملة json وحاول مرة أخرى",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "تعذر إرسال الطلب", "network_fail": "تعذر إرسال الطلب",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "لا مدة", "no_duration": "لا مدة",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "تعذر تنفيذ نص الطلب المسبق", "script_fail": "تعذر تنفيذ نص الطلب المسبق",
"something_went_wrong": "هناك خطأ ما", "something_went_wrong": "هناك خطأ ما",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "تصدير بتنسيق JSON", "as_json": "تصدير بتنسيق JSON",
"create_secret_gist": "إنشاء جوهر سري", "create_secret_gist": "إنشاء جوهر سري",
"failed": "Something went wrong while exporting",
"gist_created": "خلقت الجست", "gist_created": "خلقت الجست",
"require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري", "require_github": "تسجيل الدخول باستخدام GitHub لإنشاء جوهر سري",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "الاشتراكات", "subscriptions": "الاشتراكات",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "سيتم إنشاء رأس التفويض تلقائيًا عند إرسال الطلب.", "authorization": "سيتم إنشاء رأس التفويض تلقائيًا عند إرسال الطلب.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "قم بإنشاء الوثائق أولاً", "generate_documentation_first": "قم بإنشاء الوثائق أولاً",
"network_fail": "تعذر الوصول إلى نقطة نهاية API. تحقق من اتصالك بالشبكة وحاول مرة أخرى.", "network_fail": "تعذر الوصول إلى نقطة نهاية API. تحقق من اتصالك بالشبكة وحاول مرة أخرى.",
"offline": "يبدو أنك غير متصل بالإنترنت. قد لا تكون البيانات الموجودة في مساحة العمل هذه محدثة.", "offline": "يبدو أنك غير متصل بالإنترنت. قد لا تكون البيانات الموجودة في مساحة العمل هذه محدثة.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "مجموعات الاستيراد", "collections": "مجموعات الاستيراد",
"curl": "استيراد cURL", "curl": "استيراد cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "فشل الاستيراد", "failed": "فشل الاستيراد",
"from_file": "Import from File",
"from_gist": "الاستيراد من Gist", "from_gist": "الاستيراد من Gist",
"from_gist_description": "استيراد من Gist URL", "from_gist_description": "استيراد من Gist URL",
"from_insomnia": "استيراد من Insomnia", "from_insomnia": "استيراد من Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "استيراد من مجموعة Postman", "from_postman_description": "استيراد من مجموعة Postman",
"from_url": "استيراد من رابط", "from_url": "استيراد من رابط",
"gist_url": "أدخل عنوان URL لـ Gist", "gist_url": "أدخل عنوان URL لـ Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file", "json_description": "استيراد مجموعة من ملفHoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "يستورد" "title": "يستورد"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "المجموعات", "collections": "المجموعات",
"confirm": "يتأكد", "confirm": "يتأكد",
"customize_request": "Customize Request",
"edit_request": "تحرير الطلب", "edit_request": "تحرير الطلب",
"import_export": "استيراد و تصدير", "import_export": "استيراد و تصدير"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "نسخ الوصلة",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "مدة", "duration": "مدة",
"enter_curl": "أدخل cURL", "enter_curl": "أدخل cURL",
"generate_code": "إنشاء التعليمات البرمجية", "generate_code": "إنشاء التعليمات البرمجية",
"generated_code": "رمز تم إنشاؤه", "generated_code": "رمز تم إنشاؤه",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "قائمة الرأس", "header_list": "قائمة الرأس",
"invalid_name": "يرجى تقديم اسم للطلب", "invalid_name": "يرجى تقديم اسم للطلب",
"method": "طريقة", "method": "طريقة",
@@ -552,14 +480,12 @@
"saved": "تم حفظ الطلب", "saved": "تم حفظ الطلب",
"share": "يشارك", "share": "يشارك",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "طلب", "title": "طلب",
"type": "نوع الطلب", "type": "نوع الطلب",
"url": "URL", "url": "URL",
"variables": "المتغيرات", "variables": "المتغيرات",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "نسخ الوصلة"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "تخصيص إعدادات حسابك.", "account_description": "تخصيص إعدادات حسابك.",
"account_email_description": "عنوان بريدك الإلكتروني الأساسي.", "account_email_description": "عنوان بريدك الإلكتروني الأساسي.",
"account_name_description": "هذا هو اسم العرض الخاص بك.", "account_name_description": "هذا هو اسم العرض الخاص بك.",
"additional": "Additional Settings",
"background": "خلفية", "background": "خلفية",
"black_mode": "أسود", "black_mode": "أسود",
"choose_language": "اختر اللغة", "choose_language": "اختر اللغة",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "تأكيد البريد الإلكتروني" "verify_email": "تأكيد البريد الإلكتروني"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "نسخ ارتباط الطلب",
"delete_method": "حدد طريقة الحذف", "delete_method": "حدد طريقة الحذف",
"get_method": "حدد طريقة GET", "get_method": "حدد طريقة GET",
"head_method": "حدد طريقة HEAD", "head_method": "حدد طريقة HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "حفظ في المجموعات", "save_to_collections": "حفظ في المجموعات",
"send_request": "ارسل طلب", "send_request": "ارسل طلب",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "طلب", "title": "طلب"
"copy_request_link": "نسخ ارتباط الطلب"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "نسخ إلى الحافظة", "copied_to_clipboard": "نسخ إلى الحافظة",
"deleted": "تم الحذف", "deleted": "تم الحذف",
"deprecated": "إهمال", "deprecated": "إهمال",
@@ -836,12 +742,10 @@
"disconnected": "انقطع الاتصال", "disconnected": "انقطع الاتصال",
"disconnected_from": "انقطع الاتصال بـ {name}", "disconnected_from": "انقطع الاتصال بـ {name}",
"docs_generated": "تم إنشاء الوثائق", "docs_generated": "تم إنشاء الوثائق",
"download_failed": "Download failed",
"download_started": "بدأ التنزيل", "download_started": "بدأ التنزيل",
"enabled": "ممكن", "enabled": "ممكن",
"file_imported": "تم استيراد الملف", "file_imported": "تم استيراد الملف",
"finished_in": "انتهى في {duration} مللي ثانية", "finished_in": "انتهى في {duration} مللي ثانية",
"hide": "Hide",
"history_deleted": "تم حذف السجل", "history_deleted": "تم حذف السجل",
"linewrap": "خطوط الالتفاف", "linewrap": "خطوط الالتفاف",
"loading": "تحميل...", "loading": "تحميل...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "استفسارات", "queries": "استفسارات",
"query": "استفسار", "query": "استفسار",
"schema": "مخطط", "schema": "مخطط",
"shared_requests": "Shared Requests",
"socketio": "مقبس", "socketio": "مقبس",
"sse": "SSE", "sse": "SSE",
"tests": "الاختبارات", "tests": "الاختبارات",
@@ -905,7 +807,6 @@
"email_do_not_match": "البريد الإلكتروني لا يتوافق مع معلومات حسابك. اتصل بمدير الفريق.", "email_do_not_match": "البريد الإلكتروني لا يتوافق مع معلومات حسابك. اتصل بمدير الفريق.",
"exit": "فريق الخروج", "exit": "فريق الخروج",
"exit_disabled": "فقط المالك لا يمكنه الخروج من الفريق", "exit_disabled": "فقط المالك لا يمكنه الخروج من الفريق",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "تنسيق البريد الإلكتروني غير صالح", "invalid_email_format": "تنسيق البريد الإلكتروني غير صالح",
"invalid_id": "معرف الفريق غير صالح. اتصل بمدير الفريق.", "invalid_id": "معرف الفريق غير صالح. اتصل بمدير الفريق.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "فريق حفظ", "saved": "فريق حفظ",
"select_a_team": "اختر فريق", "select_a_team": "اختر فريق",
"success_invites": "Success invites",
"title": "فرق", "title": "فرق",
"we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!", "we_sent_invite_link": "لقد أرسلنا رابط دعوة لجميع المدعوين!",
"we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق." "we_sent_invite_link_description": "اطلب من جميع المدعوين التحقق من صندوق الوارد الخاص بهم. انقر على الرابط للانضمام إلى الفريق."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Cancel·lar", "cancel": "Cancel·lar",
"choose_file": "Triar un fitxer", "choose_file": "Triar un fitxer",
@@ -11,7 +10,6 @@
"connect": "Connectar", "connect": "Connectar",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Copiar", "copy": "Copiar",
"create": "Create",
"delete": "Eliminar", "delete": "Eliminar",
"disconnect": "Desconnectar", "disconnect": "Desconnectar",
"dismiss": "Tancar", "dismiss": "Tancar",
@@ -33,7 +31,6 @@
"open_workspace": "Obrir espai de treball", "open_workspace": "Obrir espai de treball",
"paste": "Enganxar", "paste": "Enganxar",
"prettify": "Fes-ho bonic", "prettify": "Fes-ho bonic",
"properties": "Properties",
"remove": "Eliminar", "remove": "Eliminar",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurar", "restore": "Restaurar",
@@ -42,7 +39,6 @@
"scroll_to_top": "Desplaceu-vos cap a dalt", "scroll_to_top": "Desplaceu-vos cap a dalt",
"search": "Cercar", "search": "Cercar",
"send": "Enviar", "send": "Enviar",
"share": "Share",
"start": "Començar", "start": "Començar",
"starting": "Starting", "starting": "Starting",
"stop": "Aturar", "stop": "Aturar",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Xateja amb nosaltres", "chat_with_us": "Xateja amb nosaltres",
"contact_us": "Contacta amb nosaltres", "contact_us": "Contacta amb nosaltres",
"cookies": "Cookies",
"copy": "Copiar", "copy": "Copiar",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copiar User Auth Token", "copy_user_id": "Copiar User Auth Token",
"developer_option": "Opcions de desenvolupador", "developer_option": "Opcions de desenvolupador",
"developer_option_description": "Eines de desenvolupament que ajuden en el desenvolupament i manteniment de Hoppscotch.", "developer_option_description": "Eines de desenvolupament que ajuden en el desenvolupament i manteniment de Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Dreceres de teclat", "keyboard_shortcuts": "Dreceres de teclat",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "S'ha trobat una nova versió. Refresca per actualitzar.", "new_version_found": "S'ha trobat una nova versió. Refresca per actualitzar.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Opcions", "options": "Opcions",
"proxy_privacy_policy": "Política de privadesa del servidor intermediari (proxy)", "proxy_privacy_policy": "Política de privadesa del servidor intermediari (proxy)",
"reload": "Recarregar", "reload": "Recarregar",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generar Token", "generate_token": "Generar Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Inclou a l'URL", "include_in_url": "Inclou a l'URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Aprèn com", "learn": "Aprèn com",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Passar per", "pass_key_by": "Passar per",
"password": "Contrasenya", "password": "Contrasenya",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Tipus d'autorització", "type": "Tipus d'autorització",
"username": "Nom d'usuari" "username": "Nom d'usuari"
@@ -148,7 +124,6 @@
"created": "Col·lecció creada", "created": "Col·lecció creada",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Editar la col·lecció", "edit": "Editar la col·lecció",
"import_or_create": "Import or create a collection",
"invalid_name": "Proporcioneu un nom vàlid per a la col·lecció", "invalid_name": "Proporcioneu un nom vàlid per a la col·lecció",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "El nom de la col·lecció ha de tenir almenys 3 caràcters", "name_length_insufficient": "El nom de la col·lecció ha de tenir almenys 3 caràcters",
"new": "Nova col · lecció", "new": "Nova col · lecció",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "S'ha canviat el nom de la col·lecció", "renamed": "S'ha canviat el nom de la col·lecció",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Guardar com", "save_as": "Guardar com",
@@ -178,7 +151,6 @@
"remove_folder": "Està segur que vol suprimir definitivament aquesta carpeta?", "remove_folder": "Està segur que vol suprimir definitivament aquesta carpeta?",
"remove_history": "Està segur que vol suprimir definitivament tot l'historial?", "remove_history": "Està segur que vol suprimir definitivament tot l'historial?",
"remove_request": "Està segur que vol suprimir definitivament aquesta sol·licitud?", "remove_request": "Està segur que vol suprimir definitivament aquesta sol·licitud?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Està segur que vol suprimir aquest equip?", "remove_team": "Està segur que vol suprimir aquest equip?",
"remove_telemetry": "Està segur que vol desactivar Telemetry?", "remove_telemetry": "Està segur que vol desactivar Telemetry?",
"request_change": "Està segur que vol descartar la sol·licitud actual, els canvis no desats es perdran.", "request_change": "Està segur que vol descartar la sol·licitud actual, els canvis no desats es perdran.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Capçalera {count}", "header": "Capçalera {count}",
"message": "Missatges {count}", "message": "Missatges {count}",
@@ -238,13 +192,11 @@
"profile": "Inicia sessió per veure el vostre perfil", "profile": "Inicia sessió per veure el vostre perfil",
"protocols": "Els protocols estan buits", "protocols": "Els protocols estan buits",
"schema": "Connecta't a un endpoint GraphQL", "schema": "Connecta't a un endpoint GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Els shortcodes estan buits",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "El nom de l'equip és buit", "team_name": "El nom de l'equip és buit",
"teams": "Els equips estan buits", "teams": "Els equips estan buits",
"tests": "No hi ha proves per a aquesta sol·licitud", "tests": "No hi ha proves per a aquesta sol·licitud"
"shortcodes": "Els shortcodes estan buits"
}, },
"environment": { "environment": {
"add_to_global": "Afegir-ho a Global", "add_to_global": "Afegir-ho a Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Proporcioneu un nom vàlid per a l'entorn", "invalid_name": "Proporcioneu un nom vàlid per a l'entorn",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Llista de variables" "variable_list": "Llista de variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).", "browser_support_sse": "Sembla que aquest navegador no és compatible amb els Esdeveniments Enviats pel Servidor (Server Sent Events).",
"check_console_details": "Consulta el registre de la consola per obtenir més informació.", "check_console_details": "Consulta el registre de la consola per obtenir més informació.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL no està formatat correctament", "curl_invalid_format": "cURL no està formatat correctament",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "No s'ha pogut personalitzar un cos no vàlid, resol els errors de sintaxi json i tornar-ho a provar", "json_prettify_invalid_body": "No s'ha pogut personalitzar un cos no vàlid, resol els errors de sintaxi json i tornar-ho a provar",
"network_error": "Sembla que hi ha un error de xarxa. Si us plau torna-ho a provar.", "network_error": "Sembla que hi ha un error de xarxa. Si us plau torna-ho a provar.",
"network_fail": "No s'ha pogut enviar la sol·licitud", "network_fail": "No s'ha pogut enviar la sol·licitud",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Sense durada", "no_duration": "Sense durada",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No s'ha trobat cap coincidència", "no_results_found": "No s'ha trobat cap coincidència",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "No s'ha pogut executar l'script de sol·licitud prèvia", "script_fail": "No s'ha pogut executar l'script de sol·licitud prèvia",
"something_went_wrong": "Alguna cosa ha anat malament", "something_went_wrong": "Alguna cosa ha anat malament",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exporta com a JSON", "as_json": "Exporta com a JSON",
"create_secret_gist": "Crear un Gist secret", "create_secret_gist": "Crear un Gist secret",
"failed": "Something went wrong while exporting",
"gist_created": "Gist creat", "gist_created": "Gist creat",
"require_github": "Inicieu la sessió amb GitHub per crear un Gisst secret", "require_github": "Inicieu la sessió amb GitHub per crear un Gisst secret",
"title": "Exportar" "title": "Exportar"
@@ -341,9 +286,6 @@
"subscriptions": "Subscripcions", "subscriptions": "Subscripcions",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "La capçalera de l'autorització es generarà automàticament quan envieu la sol·licitud.", "authorization": "La capçalera de l'autorització es generarà automàticament quan envieu la sol·licitud.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Genereu documentació primer", "generate_documentation_first": "Genereu documentació primer",
"network_fail": "No es pot arribar al punt final de l'API. Comproveu la connexió de xarxa i torneu-ho a provar.", "network_fail": "No es pot arribar al punt final de l'API. Comproveu la connexió de xarxa i torneu-ho a provar.",
"offline": "Sembla que estàs fora de línia. És possible que les dades d'aquest espai de treball no estiguin actualitzades.", "offline": "Sembla que estàs fora de línia. És possible que les dades d'aquest espai de treball no estiguin actualitzades.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importar col·leccions", "collections": "Importar col·leccions",
"curl": "Importar cURL", "curl": "Importar cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "La importació ha fallat", "failed": "La importació ha fallat",
"from_file": "Import from File",
"from_gist": "Importar des de Gist", "from_gist": "Importar des de Gist",
"from_gist_description": "Importar des de l'URL de Gist", "from_gist_description": "Importar des de l'URL de Gist",
"from_insomnia": "Importar des d'Insomnia", "from_insomnia": "Importar des d'Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Importar des de la col·lecció de Postman", "from_postman_description": "Importar des de la col·lecció de Postman",
"from_url": "Importar des de l'URL", "from_url": "Importar des de l'URL",
"gist_url": "Introduïu l'URL del Gist", "gist_url": "Introduïu l'URL del Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "No s'han pogut obtenir dades de l'URL", "import_from_url_invalid_fetch": "No s'han pogut obtenir dades de l'URL",
"import_from_url_invalid_file_format": "S'ha produït un error en importar les col·leccions", "import_from_url_invalid_file_format": "S'ha produït un error en importar les col·leccions",
"import_from_url_invalid_type": "Tipus no compatible. Els valors acceptats són 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Tipus no compatible. Els valors acceptats són 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Col·leccions importades", "import_from_url_success": "Col·leccions importades",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Importar col·leccions des d'un fitxer JSON de col·leccions Hoppscotch", "json_description": "Importar col·leccions des d'un fitxer JSON de col·leccions Hoppscotch",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importació" "title": "Importació"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Col·leccions", "collections": "Col·leccions",
"confirm": "Confirmar", "confirm": "Confirmar",
"customize_request": "Customize Request",
"edit_request": "Sol·licitud d'edició", "edit_request": "Sol·licitud d'edició",
"import_export": "Importar / Exportar", "import_export": "Importar / Exportar"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Estructurat", "structured": "Estructurat",
"text": "Text" "text": "Text"
}, },
"copy_link": "Copia l'enllaç",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Durada", "duration": "Durada",
"enter_curl": "Introduïu cURL", "enter_curl": "Introduïu cURL",
"generate_code": "Generar codi", "generate_code": "Generar codi",
"generated_code": "Codi generat", "generated_code": "Codi generat",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Llista de capçaleres", "header_list": "Llista de capçaleres",
"invalid_name": "Proporcioneu un nom per a la sol·licitud", "invalid_name": "Proporcioneu un nom per a la sol·licitud",
"method": "Mètode", "method": "Mètode",
@@ -552,14 +480,12 @@
"saved": "S'ha desat la sol·licitud", "saved": "S'ha desat la sol·licitud",
"share": "Compartir", "share": "Compartir",
"share_description": "Comparteix Hoppscotch amb els teus amics", "share_description": "Comparteix Hoppscotch amb els teus amics",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Sol·licitud", "title": "Sol·licitud",
"type": "Tipus de sol·licitud", "type": "Tipus de sol·licitud",
"url": "URL", "url": "URL",
"variables": "Variables", "variables": "Variables",
"view_my_links": "Visualitzar els meus enllaços", "view_my_links": "Visualitzar els meus enllaços"
"copy_link": "Copia l'enllaç"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Personalitzeu la configuració del compte.", "account_description": "Personalitzeu la configuració del compte.",
"account_email_description": "La vostra adreça de correu electrònic principal.", "account_email_description": "La vostra adreça de correu electrònic principal.",
"account_name_description": "Aquest és el vostre nom d'exposició", "account_name_description": "Aquest és el vostre nom d'exposició",
"additional": "Additional Settings",
"background": "Fons", "background": "Fons",
"black_mode": "Negre", "black_mode": "Negre",
"choose_language": "Tria l'idioma", "choose_language": "Tria l'idioma",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verificar correu electronic" "verify_email": "Verificar correu electronic"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Accions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Creat el",
"copy_html": "Copy HTML", "deleted": "S'ha suprimit el shortcode",
"copy_link": "Copy Link", "method": "Mètode",
"copy_markdown": "Copy Markdown", "not_found": "No s'ha trobat el shortcode",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Copiar l'enllaç de la sol·licitud",
"delete_method": "Seleccionar el mètode DELETE", "delete_method": "Seleccionar el mètode DELETE",
"get_method": "Seleccionar el mètode GET", "get_method": "Seleccionar el mètode GET",
"head_method": "Seleccionar el mètode HEAD", "head_method": "Seleccionar el mètode HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Guardar a les col·leccions", "save_to_collections": "Guardar a les col·leccions",
"send_request": "Enviar sol.licitud", "send_request": "Enviar sol.licitud",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Sol·licitud", "title": "Sol·licitud"
"copy_request_link": "Copiar l'enllaç de la sol·licitud"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "No s'ha pogut connectar", "connection_error": "No s'ha pogut connectar",
"connection_failed": "Connexió fallida", "connection_failed": "Connexió fallida",
"connection_lost": "Connexió perduda", "connection_lost": "Connexió perduda",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copiat al porta-retalls", "copied_to_clipboard": "Copiat al porta-retalls",
"deleted": "Eliminat", "deleted": "Eliminat",
"deprecated": "Obsolet", "deprecated": "Obsolet",
@@ -836,12 +742,10 @@
"disconnected": "Desconnectat", "disconnected": "Desconnectat",
"disconnected_from": "Desconnectat de {name}", "disconnected_from": "Desconnectat de {name}",
"docs_generated": "Documentació generada", "docs_generated": "Documentació generada",
"download_failed": "Download failed",
"download_started": "S'ha iniciat la baixada", "download_started": "S'ha iniciat la baixada",
"enabled": "Activat", "enabled": "Activat",
"file_imported": "Fitxer importat", "file_imported": "Fitxer importat",
"finished_in": "Acabat en {duration} ms", "finished_in": "Acabat en {duration} ms",
"hide": "Hide",
"history_deleted": "S'ha suprimit l'historial", "history_deleted": "S'ha suprimit l'historial",
"linewrap": "Embolcar línies", "linewrap": "Embolcar línies",
"loading": "S'està carregant...", "loading": "S'està carregant...",
@@ -852,7 +756,6 @@
"published_error": "S'ha produït un error en publicar el missatge: {topic} al tema: {message}", "published_error": "S'ha produït un error en publicar el missatge: {topic} al tema: {message}",
"published_message": "Missatge publicat: {missatge} al tema: {tema}", "published_message": "Missatge publicat: {missatge} al tema: {tema}",
"reconnection_error": "No s'ha pogut tornar a connectar", "reconnection_error": "No s'ha pogut tornar a connectar",
"show": "Show",
"subscribed_failed": "No s'ha pogut subscriure al tema: {topic}", "subscribed_failed": "No s'ha pogut subscriure al tema: {topic}",
"subscribed_success": "S'ha subscrit correctament al tema: {topic}", "subscribed_success": "S'ha subscrit correctament al tema: {topic}",
"unsubscribed_failed": "No s'ha pogut cancel·lar la subscripció al tema: {topic}", "unsubscribed_failed": "No s'ha pogut cancel·lar la subscripció al tema: {topic}",
@@ -888,7 +791,6 @@
"queries": "Consultes", "queries": "Consultes",
"query": "Consulta", "query": "Consulta",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Proves", "tests": "Proves",
@@ -905,7 +807,6 @@
"email_do_not_match": "El correu electrònic no coincideix amb les dades del vostre compte. Contacta amb el propietari del teu equip.", "email_do_not_match": "El correu electrònic no coincideix amb les dades del vostre compte. Contacta amb el propietari del teu equip.",
"exit": "Sortir de l'equip", "exit": "Sortir de l'equip",
"exit_disabled": "L'únic propietari no pot sortir de l'equip", "exit_disabled": "L'únic propietari no pot sortir de l'equip",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "El format del correu electrònic no és vàlid", "invalid_email_format": "El format del correu electrònic no és vàlid",
"invalid_id": "Identificador d'equip no vàlid. Contacta amb el propietari del teu equip.", "invalid_id": "Identificador d'equip no vàlid. Contacta amb el propietari del teu equip.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "S'ha guardat l'equip", "saved": "S'ha guardat l'equip",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Equips", "title": "Equips",
"we_sent_invite_link": "Hem enviat un enllaç d'invitació a tots els convidats!", "we_sent_invite_link": "Hem enviat un enllaç d'invitació a tots els convidats!",
"we_sent_invite_link_description": "Demaneu a tots els convidats que comprovin la seva safata d'entrada. Feu clic a l'enllaç per unir-vos a l'equip." "we_sent_invite_link_description": "Demaneu a tots els convidats que comprovin la seva safata d'entrada. Feu clic a l'enllaç per unir-vos a l'equip."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Accions",
"created_on": "Creat el",
"deleted": "S'ha suprimit el shortcode",
"method": "Mètode",
"not_found": "No s'ha trobat el shortcode",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,17 +1,15 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "自动滚动", "autoscroll": "自动滚动",
"cancel": "取消", "cancel": "取消",
"choose_file": "选择文件", "choose_file": "选择文件",
"clear": "清除", "clear": "清除",
"clear_all": "全部清除", "clear_all": "全部清除",
"clear_history": "清除全部历史记录", "clear_history": "Clear all History",
"close": "关闭", "close": "关闭",
"connect": "连接", "connect": "连接",
"connecting": "连接中", "connecting": "连接中",
"copy": "复制", "copy": "复制",
"create": "Create",
"delete": "删除", "delete": "删除",
"disconnect": "断开连接", "disconnect": "断开连接",
"dismiss": "忽略", "dismiss": "忽略",
@@ -33,16 +31,14 @@
"open_workspace": "打开工作区", "open_workspace": "打开工作区",
"paste": "粘贴", "paste": "粘贴",
"prettify": "美化", "prettify": "美化",
"properties": "Properties",
"remove": "移除", "remove": "移除",
"rename": "重命名", "rename": "Rename",
"restore": "恢复", "restore": "恢复",
"save": "保存", "save": "保存",
"scroll_to_bottom": "滚动至底部", "scroll_to_bottom": "滚动至底部",
"scroll_to_top": "滚动至顶部", "scroll_to_top": "滚动至顶部",
"search": "搜索", "search": "搜索",
"send": "发送", "send": "发送",
"share": "Share",
"start": "开始", "start": "开始",
"starting": "正在开始", "starting": "正在开始",
"stop": "停止", "stop": "停止",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "与我们交谈", "chat_with_us": "与我们交谈",
"contact_us": "联系我们", "contact_us": "联系我们",
"cookies": "Cookies",
"copy": "复制", "copy": "复制",
"copy_interface_type": "Copy interface type",
"copy_user_id": "复制认证 Token", "copy_user_id": "复制认证 Token",
"developer_option": "开发者选项", "developer_option": "开发者选项",
"developer_option_description": "开发者工具,有助于开发和维护 Hoppscotch。", "developer_option_description": "开发者工具,有助于开发和维护 Hoppscotch。",
@@ -79,15 +73,14 @@
"keyboard_shortcuts": "键盘快捷键", "keyboard_shortcuts": "键盘快捷键",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "已发现新版本。刷新页面以更新。", "new_version_found": "已发现新版本。刷新页面以更新。",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "选项", "options": "选项",
"proxy_privacy_policy": "代理隐私政策", "proxy_privacy_policy": "代理隐私政策",
"reload": "重新加载", "reload": "重新加载",
"search": "搜索", "search": "搜索",
"share": "分享", "share": "分享",
"shortcuts": "快捷方式", "shortcuts": "快捷方式",
"social_description": "在社交媒体上关注我们,了解最新新闻、更新和发布。", "social_description": "Follow us on social media to stay updated with the latest news, updates and releases.",
"social_links": "社交媒体链接", "social_links": "Social links",
"spotlight": "聚光灯", "spotlight": "聚光灯",
"status": "状态", "status": "状态",
"status_description": "检查网站状态", "status_description": "检查网站状态",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "生成令牌", "generate_token": "生成令牌",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "包含在 URL 内", "include_in_url": "包含在 URL 内",
"inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "了解更多", "learn": "了解更多",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "传递方式", "pass_key_by": "传递方式",
"password": "密码", "password": "密码",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "令牌", "token": "令牌",
"type": "授权类型", "type": "授权类型",
"username": "用户名" "username": "用户名"
@@ -148,7 +124,6 @@
"created": "集合已创建", "created": "集合已创建",
"different_parent": "不能用不同的父类来重新排序集合", "different_parent": "不能用不同的父类来重新排序集合",
"edit": "编辑集合", "edit": "编辑集合",
"import_or_create": "Import or create a collection",
"invalid_name": "请提供有效的集合名称", "invalid_name": "请提供有效的集合名称",
"invalid_root_move": "该集合已经在根级了", "invalid_root_move": "该集合已经在根级了",
"moved": "移动完成", "moved": "移动完成",
@@ -157,20 +132,18 @@
"name_length_insufficient": "集合名字至少需要 3 个字符", "name_length_insufficient": "集合名字至少需要 3 个字符",
"new": "新建集合", "new": "新建集合",
"order_changed": "集合顺序已更新", "order_changed": "集合顺序已更新",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "集合已更名", "renamed": "集合已更名",
"request_in_use": "请求正在使用中", "request_in_use": "请求正在使用中",
"save_as": "另存为", "save_as": "另存为",
"save_to_collection": "保存至集合", "save_to_collection": "Save to Collection",
"select": "选择一个集合", "select": "选择一个集合",
"select_location": "选择位置", "select_location": "选择位置",
"select_team": "选择一个团队", "select_team": "选择一个团队",
"team_collections": "团队集合" "team_collections": "团队集合"
}, },
"confirm": { "confirm": {
"close_unsaved_tab": "你确定要关闭此标签页吗?", "close_unsaved_tab": "Are you sure you want to close this tab?",
"close_unsaved_tabs": "你确定要关闭所有标签页吗? {count} 个未保存的标签页将被丢失。", "close_unsaved_tabs": "Are you sure you want to close all tabs? {count} unsaved tabs will be lost.",
"exit_team": "你确定要离开此团队吗?", "exit_team": "你确定要离开此团队吗?",
"logout": "你确定要登出吗?", "logout": "你确定要登出吗?",
"remove_collection": "你确定要永久删除该集合吗?", "remove_collection": "你确定要永久删除该集合吗?",
@@ -178,7 +151,6 @@
"remove_folder": "你确定要永久删除该文件夹吗?", "remove_folder": "你确定要永久删除该文件夹吗?",
"remove_history": "你确定要永久删除全部历史记录吗?", "remove_history": "你确定要永久删除全部历史记录吗?",
"remove_request": "你确定要永久删除该请求吗?", "remove_request": "你确定要永久删除该请求吗?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "你确定要删除该团队吗?", "remove_team": "你确定要删除该团队吗?",
"remove_telemetry": "你确定要退出遥测服务吗?", "remove_telemetry": "你确定要退出遥测服务吗?",
"request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。", "request_change": "你确定你要放弃当前的请求,未保存的修改将被丢失。",
@@ -186,27 +158,9 @@
"sync": "您确定要同步该工作区吗?" "sync": "您确定要同步该工作区吗?"
}, },
"context_menu": { "context_menu": {
"add_parameters": "添加至参数", "add_parameters": "Add to parameters",
"open_request_in_new_tab": "在新标签页中打开请求", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "设置为变量" "set_environment_variable": "Set as variable"
},
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
}, },
"count": { "count": {
"header": "请求头 {count}", "header": "请求头 {count}",
@@ -238,13 +192,11 @@
"profile": "登录以查看你的个人资料", "profile": "登录以查看你的个人资料",
"protocols": "协议为空", "protocols": "协议为空",
"schema": "连接至 GraphQL 端点", "schema": "连接至 GraphQL 端点",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes 为空",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "订阅为空", "subscription": "订阅为空",
"team_name": "团队名称为空", "team_name": "团队名称为空",
"teams": "团队为空", "teams": "团队为空",
"tests": "没有针对该请求的测试", "tests": "没有针对该请求的测试"
"shortcodes": "短链接为空"
}, },
"environment": { "environment": {
"add_to_global": "添加到全局环境", "add_to_global": "添加到全局环境",
@@ -252,39 +204,36 @@
"create_new": "创建新环境", "create_new": "创建新环境",
"created": "环境已创建", "created": "环境已创建",
"deleted": "环境已删除", "deleted": "环境已删除",
"duplicated": "环境已复制", "duplicated": "Environment duplicated",
"edit": "编辑环境", "edit": "编辑环境",
"empty_variables": "没有变量", "empty_variables": "No variables",
"global": "全局", "global": "Global",
"global_variables": "全局变量", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "请提供有效的环境名称", "invalid_name": "请提供有效的环境名称",
"list": "环境变量", "list": "Environment variables",
"my_environments": "我的环境", "my_environments": "我的环境",
"name": "名称", "name": "Name",
"nested_overflow": "环境嵌套深度超过限制10层", "nested_overflow": "环境嵌套深度超过限制10层",
"new": "新建环境", "new": "新建环境",
"no_active_environment": "没有激活的环境", "no_active_environment": "No active environment",
"no_environment": "无环境", "no_environment": "无环境",
"no_environment_description": "没有选择环境。选择如何处理以下变量。", "no_environment_description": "没有选择环境。选择如何处理以下变量。",
"quick_peek": "快速浏览环境", "quick_peek": "Environment Quick Peek",
"replace_with_variable": "替换为变量", "replace_with_variable": "Replace with variable",
"scope": "范围", "scope": "Scope",
"select": "选择环境", "select": "选择环境",
"set": "设置环境", "set": "Set environment",
"set_as_environment": "设置为环境", "set_as_environment": "Set as environment",
"team_environments": "团队环境", "team_environments": "团队环境",
"title": "环境", "title": "环境",
"updated": "环境已更新", "updated": "环境已更新",
"value": "", "value": "Value",
"variable": "变量", "variable": "Variable",
"variable_list": "变量列表" "variable_list": "变量列表"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "该浏览器似乎不支持 SSE。", "browser_support_sse": "该浏览器似乎不支持 SSE。",
"check_console_details": "检查控制台日志以获悉详情", "check_console_details": "检查控制台日志以获悉详情",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL 格式不正确", "curl_invalid_format": "cURL 格式不正确",
"danger_zone": "危险区域", "danger_zone": "危险区域",
"delete_account": "您的帐号目前为这些团队的拥有者:", "delete_account": "您的帐号目前为这些团队的拥有者:",
@@ -296,18 +245,14 @@
"incorrect_email": "电子邮箱错误", "incorrect_email": "电子邮箱错误",
"invalid_link": "无效链接", "invalid_link": "无效链接",
"invalid_link_description": "你点击的链接无效或已过期。", "invalid_link_description": "你点击的链接无效或已过期。",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "不合法的 JSON", "json_parsing_failed": "不合法的 JSON",
"json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试", "json_prettify_invalid_body": "无法美化无效的请求头,处理 JSON 语法错误并重试",
"network_error": "好像发生了网络错误,请重试。", "network_error": "好像发生了网络错误,请重试。",
"network_fail": "无法发送请求", "network_fail": "无法发送请求",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "无持续时间", "no_duration": "无持续时间",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "找不到结果", "no_results_found": "找不到结果",
"page_not_found": "找不到此頁面", "page_not_found": "找不到此頁面",
"please_install_extension": "Please install the extension and add origin to the extension.", "proxy_error": "Proxy error",
"proxy_error": "代理错误",
"script_fail": "无法执行预请求脚本", "script_fail": "无法执行预请求脚本",
"something_went_wrong": "发生了一些错误", "something_went_wrong": "发生了一些错误",
"test_script_fail": "无法执行请求脚本" "test_script_fail": "无法执行请求脚本"
@@ -315,13 +260,9 @@
"export": { "export": {
"as_json": "导出为 JSON", "as_json": "导出为 JSON",
"create_secret_gist": "创建私密 Gist", "create_secret_gist": "创建私密 Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist", "gist_created": "已创建 Gist",
"failed": "Something went wrong while exporting",
"secret_gist_success": "Successfully exported as secret Gist",
"require_github": "使用 GitHub 登录以创建私密 Gist", "require_github": "使用 GitHub 登录以创建私密 Gist",
"title": "导出", "title": "导出"
"success": "Successfully exported",
"gist_created": "已创建 Gist"
}, },
"filter": { "filter": {
"all": "全部", "all": "全部",
@@ -337,16 +278,13 @@
"renamed": "文件夹已更名" "renamed": "文件夹已更名"
}, },
"graphql": { "graphql": {
"connection_switch_confirm": "您想连接最新的 GraphQL 端点吗?", "connection_switch_confirm": "Do you want to connect with the latest GraphQL endpoint?",
"connection_switch_new_url": "切换到标签页将使您与活动的 GraphQL 连接断开。新的连接 URL ", "connection_switch_new_url": "Switching to a tab will disconnected you from the active GraphQL connection. New connection URL is",
"connection_switch_url": "您已连接到 GraphQL 端点,连接 URL ", "connection_switch_url": "You're connected to a GraphQL endpoint the connection URL is",
"mutations": "变更", "mutations": "变更",
"schema": "模式", "schema": "模式",
"subscriptions": "订阅", "subscriptions": "订阅",
"switch_connection": "切换连接" "switch_connection": "Switch connection"
},
"graphql_collections": {
"title": "GraphQL Collections"
}, },
"group": { "group": {
"time": "时间", "time": "时间",
@@ -359,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "授权头将会在你发送请求时自动生成。", "authorization": "授权头将会在你发送请求时自动生成。",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "请先生成文档", "generate_documentation_first": "请先生成文档",
"network_fail": "无法到达 API 端点。请检查网络连接并重试。", "network_fail": "无法到达 API 端点。请检查网络连接并重试。",
"offline": "你似乎处于离线状态,该工作区中的数据可能不是最新。", "offline": "你似乎处于离线状态,该工作区中的数据可能不是最新。",
@@ -380,10 +316,7 @@
"import": { "import": {
"collections": "导入集合", "collections": "导入集合",
"curl": "导入 cURL", "curl": "导入 cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "导入失败", "failed": "导入失败",
"from_file": "Import from File",
"from_gist": "从 Gist 导入", "from_gist": "从 Gist 导入",
"from_gist_description": "从 Gist URL 导入", "from_gist_description": "从 Gist URL 导入",
"from_insomnia": "从 Insomnia 导入", "from_insomnia": "从 Insomnia 导入",
@@ -398,41 +331,35 @@
"from_postman_description": "从 Postman 集合中导入", "from_postman_description": "从 Postman 集合中导入",
"from_url": "从 URL 导入", "from_url": "从 URL 导入",
"gist_url": "输入 Gist URL", "gist_url": "输入 Gist URL",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "无法从网址取得资料", "import_from_url_invalid_fetch": "无法从网址取得资料",
"import_from_url_invalid_file_format": "导入组合时发生错误", "import_from_url_invalid_file_format": "导入组合时发生错误",
"import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'", "import_from_url_invalid_type": "不支持此类型。可接受的值为 'hoppscotch'、'openapi'、'postman'、'insomnia'",
"import_from_url_success": "已导入组合", "import_from_url_success": "已导入组合",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "从 Hoppscotch 的集合文件导入JSON", "json_description": "从 Hoppscotch 的集合文件导入JSON",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "导入" "title": "导入"
}, },
"inspections": { "inspections": {
"description": "查可能的错误", "description": "Inspect possible errors",
"environment": { "environment": {
"add_environment": "添加到环境", "add_environment": "Add to Environment",
"not_found": "环境变量“{environment}”未找到。" "not_found": "Environment variable “{environment}” not found."
}, },
"header": { "header": {
"cookie": "浏览器不允许 Hoppscotch 设置 Cookie 标头。当前我们正在开发 Hoppscotch 桌面应用程序(即将推出),与此同时请改用授权标头。" "cookie": "The browser doesn't allow Hoppscotch to set the Cookie Header. While we're working on the Hoppscotch Desktop App (coming soon), please use the Authorization Header instead."
}, },
"response": { "response": {
"401_error": "请检查您的身份验证凭据。", "401_error": "Please check your authentication credentials.",
"404_error": "请检查您的请求 URL 和方法类型。", "404_error": "Please check your request URL and method type.",
"cors_error": "请检查您的跨源资源共享配置。", "cors_error": "Please check your Cross-Origin Resource Sharing configuration.",
"default_error": "请检查您的请求。", "default_error": "Please check your request.",
"network_error": "请检查您的网络连接。" "network_error": "Please check your network connection."
}, },
"title": "Inspector", "title": "Inspector",
"url": { "url": {
"extension_not_installed": "未安装扩展。", "extension_not_installed": "Extension not installed.",
"extension_unknown_origin": "确保您已将 API 端点的源添加到 Hoppscotch 浏览器扩展列表中。", "extension_unknown_origin": "Make sure you've added the API endpoint's origin to the Hoppscotch Browser Extension list.",
"extention_enable_action": "启用浏览器扩展", "extention_enable_action": "Enable Browser Extension",
"extention_not_enabled": "扩展未启用。" "extention_not_enabled": "Extension not enabled."
} }
}, },
"layout": { "layout": {
@@ -446,10 +373,8 @@
"close_unsaved_tab": "有未保存的变更", "close_unsaved_tab": "有未保存的变更",
"collections": "集合", "collections": "集合",
"confirm": "确认", "confirm": "确认",
"customize_request": "Customize Request",
"edit_request": "编辑请求", "edit_request": "编辑请求",
"import_export": "导入/导出", "import_export": "导入/导出"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "您已经订阅了此主题。", "already_subscribed": "您已经订阅了此主题。",
@@ -464,10 +389,10 @@
"invalid_topic": "请提供该订阅的主题", "invalid_topic": "请提供该订阅的主题",
"keep_alive": "Keep Alive", "keep_alive": "Keep Alive",
"log": "日志", "log": "日志",
"lw_message": "遗嘱消息", "lw_message": "Last-Will Message",
"lw_qos": "遗嘱消息QoS", "lw_qos": "Last-Will QoS",
"lw_retain": "遗嘱消息保留", "lw_retain": "Last-Will Retain",
"lw_topic": "遗嘱消息主题", "lw_topic": "Last-Will Topic",
"message": "消息", "message": "消息",
"new": "新订阅", "new": "新订阅",
"not_connected": "请先启动MQTT连接。", "not_connected": "请先启动MQTT连接。",
@@ -524,14 +449,13 @@
"structured": "结构", "structured": "结构",
"text": "文字" "text": "文字"
}, },
"copy_link": "复制链接",
"different_collection": "不能对来自不同集合的请求进行重新排序", "different_collection": "不能对来自不同集合的请求进行重新排序",
"duplicated": "重复的请求", "duplicated": "重复的请求",
"duration": "持续时间", "duration": "持续时间",
"enter_curl": "输入 cURL", "enter_curl": "输入 cURL",
"generate_code": "生成代码", "generate_code": "生成代码",
"generated_code": "已生成代码", "generated_code": "已生成代码",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "请求头列表", "header_list": "请求头列表",
"invalid_name": "请提供请求名称", "invalid_name": "请提供请求名称",
"method": "方法", "method": "方法",
@@ -548,7 +472,7 @@
"payload": "负载", "payload": "负载",
"query": "查询", "query": "查询",
"raw_body": "原始请求体", "raw_body": "原始请求体",
"rename": "重命名请求", "rename": "Rename Request",
"renamed": "请求重命名", "renamed": "请求重命名",
"run": "运行", "run": "运行",
"save": "保存", "save": "保存",
@@ -556,14 +480,12 @@
"saved": "请求已保存", "saved": "请求已保存",
"share": "分享", "share": "分享",
"share_description": "分享 Hoppscotch 给你的朋友", "share_description": "分享 Hoppscotch 给你的朋友",
"share_request": "Share Request", "stop": "Stop",
"stop": "停止",
"title": "请求", "title": "请求",
"type": "请求类型", "type": "请求类型",
"url": "URL", "url": "URL",
"variables": "变量", "variables": "变量",
"view_my_links": "查看我的链接", "view_my_links": "查看我的链接"
"copy_link": "复制链接"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -591,7 +513,6 @@
"account_description": "自定义您的帐户设置。", "account_description": "自定义您的帐户设置。",
"account_email_description": "您的主要电子邮箱地址。", "account_email_description": "您的主要电子邮箱地址。",
"account_name_description": "这是您的显示名称。", "account_name_description": "这是您的显示名称。",
"additional": "Additional Settings",
"background": "背景", "background": "背景",
"black_mode": "黑色", "black_mode": "黑色",
"choose_language": "选择语言", "choose_language": "选择语言",
@@ -638,31 +559,14 @@
"verified_email": "已验证电子邮件地址", "verified_email": "已验证电子邮件地址",
"verify_email": "验证电子邮箱" "verify_email": "验证电子邮箱"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "操作",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "创建于",
"copy_html": "Copy HTML", "deleted": "已刪除快捷键",
"copy_link": "Copy Link", "method": "方法",
"copy_markdown": "Copy Markdown", "not_found": "找不到快捷键",
"creating_widget": "Creating widget", "short_code": "快捷键",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,28 +592,27 @@
"title": "导航" "title": "导航"
}, },
"others": { "others": {
"prettify": "美化内容", "prettify": "Prettify Editor's Content",
"title": "其他" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "复制请求链接",
"delete_method": "选择 DELETE 方法", "delete_method": "选择 DELETE 方法",
"get_method": "选择 GET 方法", "get_method": "选择 GET 方法",
"head_method": "选择 HEAD 方法", "head_method": "选择 HEAD 方法",
"import_curl": "导入cURL", "import_curl": "Import cURL",
"method": "方法", "method": "方法",
"next_method": "选择下一个方法", "next_method": "选择下一个方法",
"post_method": "选择 POST 方法", "post_method": "选择 POST 方法",
"previous_method": "选择上一个方法", "previous_method": "选择上一个方法",
"put_method": "选择 PUT 方法", "put_method": "选择 PUT 方法",
"rename": "重命名请求", "rename": "Rename Request",
"reset_request": "重置请求", "reset_request": "重置请求",
"save_request": "保存请求", "save_request": "Save Request",
"save_to_collections": "保存到集合", "save_to_collections": "保存到集合",
"send_request": "发送请求", "send_request": "发送请求",
"share_request": "Share Request", "show_code": "Generate code snippet",
"show_code": "生成代码片段", "title": "请求"
"title": "请求",
"copy_request_link": "复制请求链接"
}, },
"response": { "response": {
"copy": "复制响应至剪贴板", "copy": "复制响应至剪贴板",
@@ -739,82 +642,82 @@
"url": "URL" "url": "URL"
}, },
"spotlight": { "spotlight": {
"change_language": "更改语言", "change_language": "Change Language",
"environments": { "environments": {
"delete": "删除当前环境", "delete": "Delete current environment",
"duplicate": "复制当前环境", "duplicate": "Duplicate current environment",
"duplicate_global": "复制全局环境", "duplicate_global": "Duplicate global environment",
"edit": "编辑当前环境", "edit": "Edit current environment",
"edit_global": "编辑全局环境", "edit_global": "Edit global environment",
"new": "创建新环境", "new": "Create new environment",
"new_variable": "创建新的环境变量", "new_variable": "Create a new environment variable",
"title": "环境" "title": "Environments"
}, },
"general": { "general": {
"chat": "与支持人员聊天", "chat": "Chat with support",
"help_menu": "帮助和支持", "help_menu": "Help and support",
"open_docs": "阅读文档", "open_docs": "Read Documentation",
"open_github": "打开 GitHub 存储库", "open_github": "Open GitHub repository",
"open_keybindings": "键盘快捷键", "open_keybindings": "Keyboard shortcuts",
"social": "社交媒体", "social": "Social",
"title": "一般" "title": "General"
}, },
"graphql": { "graphql": {
"connect": "连接到服务器", "connect": "Connect to server",
"disconnect": "与服务器断开连接" "disconnect": "Disconnect from server"
}, },
"miscellaneous": { "miscellaneous": {
"invite": "邀请你的朋友来 Hoppscotch", "invite": "Invite your friends to Hoppscotch",
"title": "杂项" "title": "Miscellaneous"
}, },
"request": { "request": {
"save_as_new": "另存为新请求", "save_as_new": "Save as new request",
"select_method": "选择方法", "select_method": "Select method",
"switch_to": "切换到", "switch_to": "Switch to",
"tab_authorization": "授权标签页", "tab_authorization": "Authorization tab",
"tab_body": "请求体标签页", "tab_body": "Body tab",
"tab_headers": "请求头标签页", "tab_headers": "Headers tab",
"tab_parameters": "参数标签页", "tab_parameters": "Parameters tab",
"tab_pre_request_script": "预请求脚本标签页", "tab_pre_request_script": "Pre-request script tab",
"tab_query": "查询标签页", "tab_query": "Query tab",
"tab_tests": "测试标签页b", "tab_tests": "Tests tab",
"tab_variables": "变量标签页" "tab_variables": "Variables tab"
}, },
"response": { "response": {
"copy": "复制响应", "copy": "Copy response",
"download": "将响应下载为文件", "download": "Download response as file",
"title": "响应" "title": "Response"
}, },
"section": { "section": {
"interceptor": "拦截器", "interceptor": "Interceptor",
"interface": "界面", "interface": "Interface",
"theme": "主题", "theme": "Theme",
"user": "用户" "user": "User"
}, },
"settings": { "settings": {
"change_interceptor": "更改拦截器", "change_interceptor": "Change Interceptor",
"change_language": "更改语言", "change_language": "Change Language",
"theme": { "theme": {
"black": "黑色", "black": "Black",
"dark": "暗色", "dark": "Dark",
"light": "亮色", "light": "Light",
"system": "系统" "system": "System preference"
} }
}, },
"tab": { "tab": {
"close_current": "关闭当前标签页", "close_current": "Close current tab",
"close_others": "关闭所有其他标签页", "close_others": "Close all other tabs",
"duplicate": "复制当前标签页", "duplicate": "Duplicate current tab",
"new_tab": "打开新的标签页", "new_tab": "Open a new tab",
"title": "标签页" "title": "Tabs"
}, },
"workspace": { "workspace": {
"delete": "删除当前团队", "delete": "Delete current team",
"edit": "编辑当前团队", "edit": "Edit current team",
"invite": "邀请人员加入团队", "invite": "Invite people to team",
"new": "创建新团队", "new": "Create new team",
"switch_to_personal": "切换到您的个人工作空间", "switch_to_personal": "Switch to your personal workspace",
"title": "团队" "title": "Teams"
} }
}, },
"sse": { "sse": {
@@ -832,7 +735,6 @@
"connection_error": "连接错误", "connection_error": "连接错误",
"connection_failed": "连接失败", "connection_failed": "连接失败",
"connection_lost": "连接丢失", "connection_lost": "连接丢失",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "已复制到剪贴板", "copied_to_clipboard": "已复制到剪贴板",
"deleted": "已删除", "deleted": "已删除",
"deprecated": "已弃用", "deprecated": "已弃用",
@@ -840,12 +742,10 @@
"disconnected": "断开连接", "disconnected": "断开连接",
"disconnected_from": "与 {name} 断开连接", "disconnected_from": "与 {name} 断开连接",
"docs_generated": "已生成文档", "docs_generated": "已生成文档",
"download_failed": "Download failed",
"download_started": "开始下载", "download_started": "开始下载",
"enabled": "启用", "enabled": "启用",
"file_imported": "文件已导入", "file_imported": "文件已导入",
"finished_in": "在 {duration} 毫秒内完成", "finished_in": "在 {duration} 毫秒内完成",
"hide": "Hide",
"history_deleted": "历史记录已删除", "history_deleted": "历史记录已删除",
"linewrap": "换行", "linewrap": "换行",
"loading": "正在加载……", "loading": "正在加载……",
@@ -856,7 +756,6 @@
"published_error": "将信息:{topic}发布至主题:{message}时发生错误", "published_error": "将信息:{topic}发布至主题:{message}时发生错误",
"published_message": "已将此信息:{message} 发布至主题:{topic}", "published_message": "已将此信息:{message} 发布至主题:{topic}",
"reconnection_error": "重连失败", "reconnection_error": "重连失败",
"show": "Show",
"subscribed_failed": "无法订阅此主题:{topic}", "subscribed_failed": "无法订阅此主题:{topic}",
"subscribed_success": "成功订阅此主题:{topic}", "subscribed_success": "成功订阅此主题:{topic}",
"unsubscribed_failed": "无法取消订阅此主题:{topic}", "unsubscribed_failed": "无法取消订阅此主题:{topic}",
@@ -892,7 +791,6 @@
"queries": "查询", "queries": "查询",
"query": "查询", "query": "查询",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "测试", "tests": "测试",
@@ -909,7 +807,6 @@
"email_do_not_match": "邮箱无法与你的帐户信息匹配。请联系你的团队者。", "email_do_not_match": "邮箱无法与你的帐户信息匹配。请联系你的团队者。",
"exit": "退出团队", "exit": "退出团队",
"exit_disabled": "团队所有者无法退出团队", "exit_disabled": "团队所有者无法退出团队",
"failed_invites": "Failed invites",
"invalid_coll_id": "无效的集合 ID", "invalid_coll_id": "无效的集合 ID",
"invalid_email_format": "电子邮箱格式无效", "invalid_email_format": "电子邮箱格式无效",
"invalid_id": "无效的团队 ID请联系你的团队者。", "invalid_id": "无效的团队 ID请联系你的团队者。",
@@ -951,7 +848,6 @@
"same_target_destination": "目标相同", "same_target_destination": "目标相同",
"saved": "团队已保存", "saved": "团队已保存",
"select_a_team": "选择团队", "select_a_team": "选择团队",
"success_invites": "Success invites",
"title": "团队", "title": "团队",
"we_sent_invite_link": "我们向所有受邀者发送了邀请链接!", "we_sent_invite_link": "我们向所有受邀者发送了邀请链接!",
"we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。" "we_sent_invite_link_description": "请所有受邀者检查他们的收件箱,点击链接以加入团队。"
@@ -983,14 +879,5 @@
"personal": "我的工作空间", "personal": "我的工作空间",
"team": "团队工作空间", "team": "团队工作空间",
"title": "工作空间" "title": "工作空间"
},
"shortcodes": {
"actions": "操作",
"created_on": "创建于",
"deleted": "已刪除短链接",
"method": "方法",
"not_found": "找不到短链接",
"short_code": "短链接",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "zrušení", "cancel": "zrušení",
"choose_file": "Vyberte soubor", "choose_file": "Vyberte soubor",
@@ -11,7 +10,6 @@
"connect": "Připojit", "connect": "Připojit",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "kopírovat", "copy": "kopírovat",
"create": "Create",
"delete": "Vymazat", "delete": "Vymazat",
"disconnect": "Odpojit", "disconnect": "Odpojit",
"dismiss": "Zavrhnout", "dismiss": "Zavrhnout",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Odstranit", "remove": "Odstranit",
"rename": "Rename", "rename": "Rename",
"restore": "Obnovit", "restore": "Obnovit",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Vyhledávání", "search": "Vyhledávání",
"send": "Poslat", "send": "Poslat",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stop", "stop": "Stop",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "piš si s námi", "chat_with_us": "piš si s námi",
"contact_us": "Kontaktujte nás", "contact_us": "Kontaktujte nás",
"cookies": "Cookies",
"copy": "kopírovat", "copy": "kopírovat",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Klávesové zkratky", "keyboard_shortcuts": "Klávesové zkratky",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Nalezena nová verze. Aktualizujte aktualizací.", "new_version_found": "Nalezena nová verze. Aktualizujte aktualizací.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Zásady ochrany osobních údajů proxy", "proxy_privacy_policy": "Zásady ochrany osobních údajů proxy",
"reload": "Znovu načíst", "reload": "Znovu načíst",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generovat token", "generate_token": "Generovat token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Zahrnout do adresy URL", "include_in_url": "Zahrnout do adresy URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Zjistěte jak", "learn": "Zjistěte jak",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Heslo", "password": "Heslo",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Žeton", "token": "Žeton",
"type": "Typ autorizace", "type": "Typ autorizace",
"username": "Uživatelské jméno" "username": "Uživatelské jméno"
@@ -148,7 +124,6 @@
"created": "Kolekce vytvořena", "created": "Kolekce vytvořena",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Upravit sbírku", "edit": "Upravit sbírku",
"import_or_create": "Import or create a collection",
"invalid_name": "Uveďte prosím platný název kolekce", "invalid_name": "Uveďte prosím platný název kolekce",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Nová kolekce", "new": "Nová kolekce",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Sbírka přejmenována", "renamed": "Sbírka přejmenována",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Uložit jako", "save_as": "Uložit jako",
@@ -178,7 +151,6 @@
"remove_folder": "Opravdu chcete tuto složku trvale smazat?", "remove_folder": "Opravdu chcete tuto složku trvale smazat?",
"remove_history": "Opravdu chcete trvale smazat celou historii?", "remove_history": "Opravdu chcete trvale smazat celou historii?",
"remove_request": "Opravdu chcete tento požadavek trvale smazat?", "remove_request": "Opravdu chcete tento požadavek trvale smazat?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Opravdu chcete tento tým smazat?", "remove_team": "Opravdu chcete tento tým smazat?",
"remove_telemetry": "Opravdu se chcete odhlásit z telemetrie?", "remove_telemetry": "Opravdu se chcete odhlásit z telemetrie?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Záhlaví {count}", "header": "Záhlaví {count}",
"message": "Zpráva {count}", "message": "Zpráva {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokoly jsou prázdné", "protocols": "Protokoly jsou prázdné",
"schema": "Připojte se ke koncovému bodu GraphQL", "schema": "Připojte se ke koncovému bodu GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Název týmu prázdný", "team_name": "Název týmu prázdný",
"teams": "Týmy jsou prázdné", "teams": "Týmy jsou prázdné",
"tests": "Pro tento požadavek neexistují žádné testy", "tests": "Pro tento požadavek neexistují žádné testy"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Zadejte platný název prostředí", "invalid_name": "Zadejte platný název prostředí",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Seznam proměnných" "variable_list": "Seznam proměnných"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.", "browser_support_sse": "Zdá se, že tento prohlížeč nemá podporu událostí odeslaných serverem.",
"check_console_details": "Podrobnosti najdete v protokolu konzoly.", "check_console_details": "Podrobnosti najdete v protokolu konzoly.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL nemá správný formát", "curl_invalid_format": "cURL nemá správný formát",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Nelze předtifikovat neplatné tělo, vyřešit chyby syntaxe json a zkusit to znovu", "json_prettify_invalid_body": "Nelze předtifikovat neplatné tělo, vyřešit chyby syntaxe json a zkusit to znovu",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Žádost nelze odeslat", "network_fail": "Žádost nelze odeslat",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Žádné trvání", "no_duration": "Žádné trvání",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Skript předběžného požadavku nelze spustit", "script_fail": "Skript předběžného požadavku nelze spustit",
"something_went_wrong": "Něco se pokazilo", "something_went_wrong": "Něco se pokazilo",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exportovat jako JSON", "as_json": "Exportovat jako JSON",
"create_secret_gist": "Vytvořte tajnou podstatu", "create_secret_gist": "Vytvořte tajnou podstatu",
"failed": "Something went wrong while exporting",
"gist_created": "Podstata vytvořena", "gist_created": "Podstata vytvořena",
"require_github": "Přihlaste se pomocí GitHub a vytvořte tajný seznam", "require_github": "Přihlaste se pomocí GitHub a vytvořte tajný seznam",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Předplatné", "subscriptions": "Předplatné",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Autorizační hlavička se automaticky vygeneruje při odeslání požadavku.", "authorization": "Autorizační hlavička se automaticky vygeneruje při odeslání požadavku.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Nejprve vytvořte dokumentaci", "generate_documentation_first": "Nejprve vytvořte dokumentaci",
"network_fail": "Nelze dosáhnout koncového bodu API. Zkontrolujte připojení k síti a zkuste to znovu.", "network_fail": "Nelze dosáhnout koncového bodu API. Zkontrolujte připojení k síti a zkuste to znovu.",
"offline": "Zdá se, že jste offline. Data v tomto pracovním prostoru nemusí být aktuální.", "offline": "Zdá se, že jste offline. Data v tomto pracovním prostoru nemusí být aktuální.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Import sbírek", "collections": "Import sbírek",
"curl": "Importovat cURL", "curl": "Importovat cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Import se nezdařil", "failed": "Import se nezdařil",
"from_file": "Import from File",
"from_gist": "Import z Gist", "from_gist": "Import z Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Zadejte URL adresy", "gist_url": "Zadejte URL adresy",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Import" "title": "Import"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Sbírky", "collections": "Sbírky",
"confirm": "Potvrdit", "confirm": "Potvrdit",
"customize_request": "Customize Request",
"edit_request": "Upravit požadavek", "edit_request": "Upravit požadavek",
"import_export": "Import Export", "import_export": "Import Export"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopírovat odkaz",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Doba trvání", "duration": "Doba trvání",
"enter_curl": "Zadejte cURL", "enter_curl": "Zadejte cURL",
"generate_code": "Vygenerujte kód", "generate_code": "Vygenerujte kód",
"generated_code": "Generovaný kód", "generated_code": "Generovaný kód",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Seznam záhlaví", "header_list": "Seznam záhlaví",
"invalid_name": "Uveďte prosím název žádosti", "invalid_name": "Uveďte prosím název žádosti",
"method": "Metoda", "method": "Metoda",
@@ -552,14 +480,12 @@
"saved": "Žádost uložena", "saved": "Žádost uložena",
"share": "Podíl", "share": "Podíl",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Žádost", "title": "Žádost",
"type": "Typ požadavku", "type": "Typ požadavku",
"url": "URL", "url": "URL",
"variables": "Proměnné", "variables": "Proměnné",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopírovat odkaz"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Přizpůsobte si nastavení účtu.", "account_description": "Přizpůsobte si nastavení účtu.",
"account_email_description": "Vaše primární e -mailová adresa.", "account_email_description": "Vaše primární e -mailová adresa.",
"account_name_description": "Toto je vaše zobrazované jméno.", "account_name_description": "Toto je vaše zobrazované jméno.",
"additional": "Additional Settings",
"background": "Pozadí", "background": "Pozadí",
"black_mode": "Černá", "black_mode": "Černá",
"choose_language": "Vyber jazyk", "choose_language": "Vyber jazyk",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopírovat požadavek na odkaz",
"delete_method": "Vyberte metodu ODSTRANIT", "delete_method": "Vyberte metodu ODSTRANIT",
"get_method": "Vyberte metodu ZÍSKAT", "get_method": "Vyberte metodu ZÍSKAT",
"head_method": "Vyberte metodu HEAD", "head_method": "Vyberte metodu HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Uložit do sbírek", "save_to_collections": "Uložit do sbírek",
"send_request": "Poslat žádost", "send_request": "Poslat žádost",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Žádost", "title": "Žádost"
"copy_request_link": "Kopírovat požadavek na odkaz"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Zkopírováno do schránky", "copied_to_clipboard": "Zkopírováno do schránky",
"deleted": "Smazáno", "deleted": "Smazáno",
"deprecated": "ZASTARALÉ", "deprecated": "ZASTARALÉ",
@@ -836,12 +742,10 @@
"disconnected": "Odpojeno", "disconnected": "Odpojeno",
"disconnected_from": "Odpojeno od {name}", "disconnected_from": "Odpojeno od {name}",
"docs_generated": "Vygenerovaná dokumentace", "docs_generated": "Vygenerovaná dokumentace",
"download_failed": "Download failed",
"download_started": "Stahování zahájeno", "download_started": "Stahování zahájeno",
"enabled": "Povoleno", "enabled": "Povoleno",
"file_imported": "Soubor importován", "file_imported": "Soubor importován",
"finished_in": "Hotovo za {duration} ms", "finished_in": "Hotovo za {duration} ms",
"hide": "Hide",
"history_deleted": "Historie odstraněna", "history_deleted": "Historie odstraněna",
"linewrap": "Zabalit linky", "linewrap": "Zabalit linky",
"loading": "Načítání...", "loading": "Načítání...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Dotazy", "queries": "Dotazy",
"query": "Dotaz", "query": "Dotaz",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Testy", "tests": "Testy",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Ukončete tým", "exit": "Ukončete tým",
"exit_disabled": "Pouze vlastník nemůže opustit tým", "exit_disabled": "Pouze vlastník nemůže opustit tým",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Formát e -mailu je neplatný", "invalid_email_format": "Formát e -mailu je neplatný",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Tým uložen", "saved": "Tým uložen",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Týmy", "title": "Týmy",
"we_sent_invite_link": "We sent an invite link to all invitees!", "we_sent_invite_link": "We sent an invite link to all invitees!",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Afbestille", "cancel": "Afbestille",
"choose_file": "Vælg en fil", "choose_file": "Vælg en fil",
@@ -11,7 +10,6 @@
"connect": "Opret forbindelse", "connect": "Opret forbindelse",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopi", "copy": "Kopi",
"create": "Create",
"delete": "Slet", "delete": "Slet",
"disconnect": "Koble fra", "disconnect": "Koble fra",
"dismiss": "Afskedige", "dismiss": "Afskedige",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Fjerne", "remove": "Fjerne",
"rename": "Rename", "rename": "Rename",
"restore": "Gendan", "restore": "Gendan",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Søg", "search": "Søg",
"send": "Sende", "send": "Sende",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Hold op", "stop": "Hold op",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Chat med os", "chat_with_us": "Chat med os",
"contact_us": "Kontakt os", "contact_us": "Kontakt os",
"cookies": "Cookies",
"copy": "Kopi", "copy": "Kopi",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Tastaturgenveje", "keyboard_shortcuts": "Tastaturgenveje",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Ny version fundet. Opdater for at opdatere.", "new_version_found": "Ny version fundet. Opdater for at opdatere.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Politik til beskyttelse af personlige oplysninger i proxy", "proxy_privacy_policy": "Politik til beskyttelse af personlige oplysninger i proxy",
"reload": "Genindlæs", "reload": "Genindlæs",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generer Token", "generate_token": "Generer Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Inkluder i URL", "include_in_url": "Inkluder i URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Lær hvordan", "learn": "Lær hvordan",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Adgangskode", "password": "Adgangskode",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Polet", "token": "Polet",
"type": "Godkendelse Type", "type": "Godkendelse Type",
"username": "Brugernavn" "username": "Brugernavn"
@@ -148,7 +124,6 @@
"created": "Samlingen er oprettet", "created": "Samlingen er oprettet",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Rediger samling", "edit": "Rediger samling",
"import_or_create": "Import or create a collection",
"invalid_name": "Angiv et gyldigt navn til samlingen", "invalid_name": "Angiv et gyldigt navn til samlingen",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Ny kollektion", "new": "Ny kollektion",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Samling omdøbt", "renamed": "Samling omdøbt",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Gem som", "save_as": "Gem som",
@@ -178,7 +151,6 @@
"remove_folder": "Er du sikker på, at du vil slette denne mappe permanent?", "remove_folder": "Er du sikker på, at du vil slette denne mappe permanent?",
"remove_history": "Er du sikker på, at du vil slette hele historikken permanent?", "remove_history": "Er du sikker på, at du vil slette hele historikken permanent?",
"remove_request": "Er du sikker på, at du vil slette denne anmodning permanent?", "remove_request": "Er du sikker på, at du vil slette denne anmodning permanent?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Er du sikker på, at du vil slette dette hold?", "remove_team": "Er du sikker på, at du vil slette dette hold?",
"remove_telemetry": "Er du sikker på, at du vil fravælge telemetri?", "remove_telemetry": "Er du sikker på, at du vil fravælge telemetri?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Overskrift {count}", "header": "Overskrift {count}",
"message": "Besked {count}", "message": "Besked {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokoller er tomme", "protocols": "Protokoller er tomme",
"schema": "Opret forbindelse til et GraphQL -slutpunkt", "schema": "Opret forbindelse til et GraphQL -slutpunkt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Teamnavn er tomt", "team_name": "Teamnavn er tomt",
"teams": "Hold er tomme", "teams": "Hold er tomme",
"tests": "Der er ingen test for denne anmodning", "tests": "Der er ingen test for denne anmodning"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Angiv et gyldigt navn på miljøet", "invalid_name": "Angiv et gyldigt navn på miljøet",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Variabel liste" "variable_list": "Variabel liste"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.", "browser_support_sse": "Det ser ikke ud til, at denne browser understøtter Server Sent Events.",
"check_console_details": "Tjek konsollog for at få flere oplysninger.", "check_console_details": "Tjek konsollog for at få flere oplysninger.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL er ikke formateret korrekt", "curl_invalid_format": "cURL er ikke formateret korrekt",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Kunne ikke pryde et ugyldigt brødtekst, løse json -syntaksfejl og prøve igen", "json_prettify_invalid_body": "Kunne ikke pryde et ugyldigt brødtekst, løse json -syntaksfejl og prøve igen",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Anmodningen kunne ikke sendes", "network_fail": "Anmodningen kunne ikke sendes",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Ingen varighed", "no_duration": "Ingen varighed",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Kunne ikke udføre pre-request script", "script_fail": "Kunne ikke udføre pre-request script",
"something_went_wrong": "Noget gik galt", "something_went_wrong": "Noget gik galt",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Eksporter som JSON", "as_json": "Eksporter som JSON",
"create_secret_gist": "Opret hemmelig Gist", "create_secret_gist": "Opret hemmelig Gist",
"failed": "Something went wrong while exporting",
"gist_created": "Gist skabt", "gist_created": "Gist skabt",
"require_github": "Log ind med GitHub for at skabe hemmelig kerne", "require_github": "Log ind med GitHub for at skabe hemmelig kerne",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Abonnementer", "subscriptions": "Abonnementer",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Autorisationsoverskriften genereres automatisk, når du sender anmodningen.", "authorization": "Autorisationsoverskriften genereres automatisk, når du sender anmodningen.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Generer først dokumentation", "generate_documentation_first": "Generer først dokumentation",
"network_fail": "Kunne ikke nå API -slutpunktet. Kontroller din netværksforbindelse, og prøv igen.", "network_fail": "Kunne ikke nå API -slutpunktet. Kontroller din netværksforbindelse, og prøv igen.",
"offline": "Du ser ud til at være offline. Data i dette arbejdsområde er muligvis ikke opdaterede.", "offline": "Du ser ud til at være offline. Data i dette arbejdsområde er muligvis ikke opdaterede.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importer samlinger", "collections": "Importer samlinger",
"curl": "Importer cURL", "curl": "Importer cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Import mislykkedes", "failed": "Import mislykkedes",
"from_file": "Import from File",
"from_gist": "Import fra Gist", "from_gist": "Import fra Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Indtast Gist URL", "gist_url": "Indtast Gist URL",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importere" "title": "Importere"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Samlinger", "collections": "Samlinger",
"confirm": "Bekræfte", "confirm": "Bekræfte",
"customize_request": "Customize Request",
"edit_request": "Rediger anmodning", "edit_request": "Rediger anmodning",
"import_export": "Import Eksport", "import_export": "Import Eksport"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopier link",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Varighed", "duration": "Varighed",
"enter_curl": "Indtast cURL", "enter_curl": "Indtast cURL",
"generate_code": "Generer kode", "generate_code": "Generer kode",
"generated_code": "Genereret kode", "generated_code": "Genereret kode",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Overskriftsliste", "header_list": "Overskriftsliste",
"invalid_name": "Angiv et navn på anmodningen", "invalid_name": "Angiv et navn på anmodningen",
"method": "Metode", "method": "Metode",
@@ -552,14 +480,12 @@
"saved": "Anmodning gemt", "saved": "Anmodning gemt",
"share": "Del", "share": "Del",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Anmodning", "title": "Anmodning",
"type": "Anmodningstype", "type": "Anmodningstype",
"url": "URL", "url": "URL",
"variables": "Variabler", "variables": "Variabler",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopier link"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Tilpas dine kontoindstillinger.", "account_description": "Tilpas dine kontoindstillinger.",
"account_email_description": "Din primære e -mail -adresse.", "account_email_description": "Din primære e -mail -adresse.",
"account_name_description": "Dette er dit visningsnavn.", "account_name_description": "Dette er dit visningsnavn.",
"additional": "Additional Settings",
"background": "Baggrund", "background": "Baggrund",
"black_mode": "Sort", "black_mode": "Sort",
"choose_language": "Vælg sprog", "choose_language": "Vælg sprog",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopiér anmodningslink",
"delete_method": "Vælg SLET metode", "delete_method": "Vælg SLET metode",
"get_method": "Vælg GET -metode", "get_method": "Vælg GET -metode",
"head_method": "Vælg HEAD -metode", "head_method": "Vælg HEAD -metode",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Gem i samlinger", "save_to_collections": "Gem i samlinger",
"send_request": "Send anmodning", "send_request": "Send anmodning",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Anmodning", "title": "Anmodning"
"copy_request_link": "Kopiér anmodningslink"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Kopieret til udklipsholder", "copied_to_clipboard": "Kopieret til udklipsholder",
"deleted": "Slettet", "deleted": "Slettet",
"deprecated": "DEPRECATED", "deprecated": "DEPRECATED",
@@ -836,12 +742,10 @@
"disconnected": "Afbrudt", "disconnected": "Afbrudt",
"disconnected_from": "Koblet fra {name}", "disconnected_from": "Koblet fra {name}",
"docs_generated": "Dokumentation genereret", "docs_generated": "Dokumentation genereret",
"download_failed": "Download failed",
"download_started": "Downloaden er startet", "download_started": "Downloaden er startet",
"enabled": "Aktiveret", "enabled": "Aktiveret",
"file_imported": "Fil importeret", "file_imported": "Fil importeret",
"finished_in": "Færdig om {duration} ms", "finished_in": "Færdig om {duration} ms",
"hide": "Hide",
"history_deleted": "Historik slettet", "history_deleted": "Historik slettet",
"linewrap": "Wrap linjer", "linewrap": "Wrap linjer",
"loading": "Indlæser...", "loading": "Indlæser...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Forespørgsler", "queries": "Forespørgsler",
"query": "Forespørgsel", "query": "Forespørgsel",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Test", "tests": "Test",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Afslut Team", "exit": "Afslut Team",
"exit_disabled": "Kun ejeren kan ikke forlade teamet", "exit_disabled": "Kun ejeren kan ikke forlade teamet",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "E -mailformatet er ugyldigt", "invalid_email_format": "E -mailformatet er ugyldigt",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Hold reddet", "saved": "Hold reddet",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Hold", "title": "Hold",
"we_sent_invite_link": "We sent an invite link to all invitees!", "we_sent_invite_link": "We sent an invite link to all invitees!",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Abbrechen", "cancel": "Abbrechen",
"choose_file": "Datei auswählen", "choose_file": "Datei auswählen",
@@ -11,7 +10,6 @@
"connect": "Verbinden", "connect": "Verbinden",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopieren", "copy": "Kopieren",
"create": "Create",
"delete": "Löschen", "delete": "Löschen",
"disconnect": "Trennen", "disconnect": "Trennen",
"dismiss": "Verwerfen", "dismiss": "Verwerfen",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Einfügen", "paste": "Einfügen",
"prettify": "Verschönern", "prettify": "Verschönern",
"properties": "Properties",
"remove": "Entfernen", "remove": "Entfernen",
"rename": "Rename", "rename": "Rename",
"restore": "Wiederherstellen", "restore": "Wiederherstellen",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Suchen", "search": "Suchen",
"send": "Senden", "send": "Senden",
"share": "Share",
"start": "Start", "start": "Start",
"starting": "Starting", "starting": "Starting",
"stop": "Stopp", "stop": "Stopp",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Chatte mit uns", "chat_with_us": "Chatte mit uns",
"contact_us": "Kontaktiere uns", "contact_us": "Kontaktiere uns",
"cookies": "Cookies",
"copy": "Kopieren", "copy": "Kopieren",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Kopiere Nutzer-Authentisierungstoken", "copy_user_id": "Kopiere Nutzer-Authentisierungstoken",
"developer_option": "Entwickleroptionen", "developer_option": "Entwickleroptionen",
"developer_option_description": "Entwicklungswerkzeuge, die helfen Hoppscotch weiter zu entwickeln und zu warten.", "developer_option_description": "Entwicklungswerkzeuge, die helfen Hoppscotch weiter zu entwickeln und zu warten.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Tastaturkürzel", "keyboard_shortcuts": "Tastaturkürzel",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Neue Version gefunden. Zum Aktualisieren Seite neu laden.", "new_version_found": "Neue Version gefunden. Zum Aktualisieren Seite neu laden.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Proxy-Datenschutzrichtlinie", "proxy_privacy_policy": "Proxy-Datenschutzrichtlinie",
"reload": "Neu laden", "reload": "Neu laden",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Token generieren", "generate_token": "Token generieren",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "In URL einbinden", "include_in_url": "In URL einbinden",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Dokumentation", "learn": "Dokumentation",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Übertragungsart", "pass_key_by": "Übertragungsart",
"password": "Passwort", "password": "Passwort",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Autorisierungsverfahren", "type": "Autorisierungsverfahren",
"username": "Nutzername" "username": "Nutzername"
@@ -148,7 +124,6 @@
"created": "Sammlung erstellt", "created": "Sammlung erstellt",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Sammlung bearbeiten", "edit": "Sammlung bearbeiten",
"import_or_create": "Import or create a collection",
"invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an", "invalid_name": "Bitte gib einen gültigen Namen für die Sammlung an",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Sammlungsname soll mindestens 3 Zeichen lang sein", "name_length_insufficient": "Sammlungsname soll mindestens 3 Zeichen lang sein",
"new": "Neue Sammlung", "new": "Neue Sammlung",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Sammlung umbenannt", "renamed": "Sammlung umbenannt",
"request_in_use": "Anfrage wird ausgeführt", "request_in_use": "Anfrage wird ausgeführt",
"save_as": "Speichern als", "save_as": "Speichern als",
@@ -178,7 +151,6 @@
"remove_folder": "Möchtest Du diesen Ordner wirklich dauerhaft löschen?", "remove_folder": "Möchtest Du diesen Ordner wirklich dauerhaft löschen?",
"remove_history": "Möchtest Du wirklich den gesamten Verlauf dauerhaft löschen?", "remove_history": "Möchtest Du wirklich den gesamten Verlauf dauerhaft löschen?",
"remove_request": "Möchtest Du diese Anfrage wirklich dauerhaft löschen?", "remove_request": "Möchtest Du diese Anfrage wirklich dauerhaft löschen?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Möchtest Du dieses Team wirklich löschen?", "remove_team": "Möchtest Du dieses Team wirklich löschen?",
"remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?", "remove_telemetry": "Möchtest Du die Telemetrie wirklich deaktivieren?",
"request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.", "request_change": "Möchtest Du diese Anfrage verwerfen? Ungespeicherte Änderungen gehen verloren.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Header {count}", "header": "Header {count}",
"message": "Nachricht {count}", "message": "Nachricht {count}",
@@ -238,13 +192,11 @@
"profile": "Einloggen um das Profil anzusehen", "profile": "Einloggen um das Profil anzusehen",
"protocols": "Protokolle sind leer", "protocols": "Protokolle sind leer",
"schema": "Verbinden mit einem GraphQL-Endpunkt", "schema": "Verbinden mit einem GraphQL-Endpunkt",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Teamname leer", "team_name": "Teamname leer",
"teams": "Teams sind leer", "teams": "Teams sind leer",
"tests": "Es gibt keine Tests für diese Anfrage", "tests": "Es gibt keine Tests für diese Anfrage"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Zur Globalen Umgebung hinzufügen", "add_to_global": "Zur Globalen Umgebung hinzufügen",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Bitte gib einen gültigen Namen für die Umgebung an", "invalid_name": "Bitte gib einen gültigen Namen für die Umgebung an",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Variablenliste" "variable_list": "Variablenliste"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.", "browser_support_sse": "Dieser Browser scheint keine Unterstützung für die vom Server gesendete Ereignisse zu haben.",
"check_console_details": "Einzelheiten findest Du in der Browser-Konsole.", "check_console_details": "Einzelheiten findest Du in der Browser-Konsole.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL ist nicht richtig formatiert", "curl_invalid_format": "cURL ist nicht richtig formatiert",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Ein ungültiger Text konnte nicht verschönert werden, JSON-Syntaxfehler beheben und erneut versuchen", "json_prettify_invalid_body": "Ein ungültiger Text konnte nicht verschönert werden, JSON-Syntaxfehler beheben und erneut versuchen",
"network_error": "Netzwerkfehler. Bitte versuche es erneut.", "network_error": "Netzwerkfehler. Bitte versuche es erneut.",
"network_fail": "Anfrage konnte nicht gesendet werden", "network_fail": "Anfrage konnte nicht gesendet werden",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Keine Dauer", "no_duration": "Keine Dauer",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden", "script_fail": "Pre-Request-Skripte konnte nicht ausgeführt werden",
"something_went_wrong": "Etwas ist schief gelaufen", "something_went_wrong": "Etwas ist schief gelaufen",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Als JSON exportieren", "as_json": "Als JSON exportieren",
"create_secret_gist": "Geheimen Github Gist erstellen", "create_secret_gist": "Geheimen Github Gist erstellen",
"failed": "Something went wrong while exporting",
"gist_created": "Gist erstellt", "gist_created": "Gist erstellt",
"require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen", "require_github": "Melde Dich bei GitHub an, um einen geheimen Gist zu erstellen",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Abonnements", "subscriptions": "Abonnements",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Der Autorisierungsheader wird automatisch generiert, wenn Du die Anfrage sendest.", "authorization": "Der Autorisierungsheader wird automatisch generiert, wenn Du die Anfrage sendest.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Zuerst Dokumentation erstellen", "generate_documentation_first": "Zuerst Dokumentation erstellen",
"network_fail": "Der API-Endpunkt kann nicht erreicht werden. Überprüfe Deine Netzwerkverbindung und versuche es erneut.", "network_fail": "Der API-Endpunkt kann nicht erreicht werden. Überprüfe Deine Netzwerkverbindung und versuche es erneut.",
"offline": "Du scheinst offline zu sein. Die Daten in diesem Arbeitsbereich sind möglicherweise nicht aktuell.", "offline": "Du scheinst offline zu sein. Die Daten in diesem Arbeitsbereich sind möglicherweise nicht aktuell.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Sammlungen importieren", "collections": "Sammlungen importieren",
"curl": "cURL importieren", "curl": "cURL importieren",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Importieren fehlgeschlagen", "failed": "Importieren fehlgeschlagen",
"from_file": "Import from File",
"from_gist": "Von Github Gist importieren", "from_gist": "Von Github Gist importieren",
"from_gist_description": "Von Github Gist URL importieren", "from_gist_description": "Von Github Gist URL importieren",
"from_insomnia": "Von Insomnia importieren", "from_insomnia": "Von Insomnia importieren",
@@ -394,17 +331,11 @@
"from_postman_description": "Postman-Sammlung importieren", "from_postman_description": "Postman-Sammlung importieren",
"from_url": "Von URL importieren", "from_url": "Von URL importieren",
"gist_url": "Gist-URL eingeben", "gist_url": "Gist-URL eingeben",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Hoppscotch Sammlungsdatei (JSON) importieren", "json_description": "Hoppscotch Sammlungsdatei (JSON) importieren",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importieren" "title": "Importieren"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Sammlungen", "collections": "Sammlungen",
"confirm": "Aktion bestätigen", "confirm": "Aktion bestätigen",
"customize_request": "Customize Request",
"edit_request": "Anfrage bearbeiten", "edit_request": "Anfrage bearbeiten",
"import_export": "Import / Export", "import_export": "Import / Export"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Link kopieren",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Dauer", "duration": "Dauer",
"enter_curl": "cURL eingeben", "enter_curl": "cURL eingeben",
"generate_code": "Code generieren", "generate_code": "Code generieren",
"generated_code": "Generierter Code", "generated_code": "Generierter Code",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Header-Liste", "header_list": "Header-Liste",
"invalid_name": "Bitte gib einen Namen für die Anfrage an", "invalid_name": "Bitte gib einen Namen für die Anfrage an",
"method": "Methode", "method": "Methode",
@@ -552,14 +480,12 @@
"saved": "Anfrage gespeichert", "saved": "Anfrage gespeichert",
"share": "Teilen", "share": "Teilen",
"share_description": "Teile Hoppscotch mit Deinen Freunden", "share_description": "Teile Hoppscotch mit Deinen Freunden",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Anfrage", "title": "Anfrage",
"type": "Anfragetyp", "type": "Anfragetyp",
"url": "URL", "url": "URL",
"variables": "Variablen", "variables": "Variablen",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Link kopieren"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Passe Deine Kontoeinstellungen an.", "account_description": "Passe Deine Kontoeinstellungen an.",
"account_email_description": "Deine primäre E-Mail-Adresse.", "account_email_description": "Deine primäre E-Mail-Adresse.",
"account_name_description": "Dies ist Dein Anzeigename.", "account_name_description": "Dies ist Dein Anzeigename.",
"additional": "Additional Settings",
"background": "Hintergrund", "background": "Hintergrund",
"black_mode": "Schwarz", "black_mode": "Schwarz",
"choose_language": "Sprache wählen", "choose_language": "Sprache wählen",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "E-Mail-Adresse bestätigen" "verify_email": "E-Mail-Adresse bestätigen"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Anfragelink kopieren",
"delete_method": "DELETE-Methode auswählen", "delete_method": "DELETE-Methode auswählen",
"get_method": "GET-Methode auswählen", "get_method": "GET-Methode auswählen",
"head_method": "HEAD-Methode auswählen", "head_method": "HEAD-Methode auswählen",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "In Sammlungen speichern", "save_to_collections": "In Sammlungen speichern",
"send_request": "Anfrage senden", "send_request": "Anfrage senden",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Anfrage", "title": "Anfrage"
"copy_request_link": "Anfragelink kopieren"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "In die Zwischenablage kopiert", "copied_to_clipboard": "In die Zwischenablage kopiert",
"deleted": "Gelöscht", "deleted": "Gelöscht",
"deprecated": "VERALTET", "deprecated": "VERALTET",
@@ -836,12 +742,10 @@
"disconnected": "Getrennt", "disconnected": "Getrennt",
"disconnected_from": "Verbindung zu {name} getrennt", "disconnected_from": "Verbindung zu {name} getrennt",
"docs_generated": "Dokumentation erstellt", "docs_generated": "Dokumentation erstellt",
"download_failed": "Download failed",
"download_started": "Download gestartet", "download_started": "Download gestartet",
"enabled": "Aktiviert", "enabled": "Aktiviert",
"file_imported": "Datei importiert", "file_imported": "Datei importiert",
"finished_in": "Fertig in {duration} ms", "finished_in": "Fertig in {duration} ms",
"hide": "Hide",
"history_deleted": "Verlauf gelöscht", "history_deleted": "Verlauf gelöscht",
"linewrap": "Zeilen umbrechen", "linewrap": "Zeilen umbrechen",
"loading": "Wird geladen...", "loading": "Wird geladen...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Anfragen", "queries": "Anfragen",
"query": "Anfrage", "query": "Anfrage",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Tests", "tests": "Tests",
@@ -905,7 +807,6 @@
"email_do_not_match": "E-Mail-Adresse stimmt nicht mit Deinen Kontodaten überein, bitte kontaktiere den Teameigentümer.", "email_do_not_match": "E-Mail-Adresse stimmt nicht mit Deinen Kontodaten überein, bitte kontaktiere den Teameigentümer.",
"exit": "Team verlassen", "exit": "Team verlassen",
"exit_disabled": "Eigentümer können das Team nicht verlassen", "exit_disabled": "Eigentümer können das Team nicht verlassen",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "E-Mail-Format ist ungültig", "invalid_email_format": "E-Mail-Format ist ungültig",
"invalid_id": "Ungültige Team-ID, bitte kontaktiere den Teameigentümer.", "invalid_id": "Ungültige Team-ID, bitte kontaktiere den Teameigentümer.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Team gespeichert", "saved": "Team gespeichert",
"select_a_team": "Team auswählen", "select_a_team": "Team auswählen",
"success_invites": "Success invites",
"title": "Team", "title": "Team",
"we_sent_invite_link": "Einladungen wurden an alle E-Mails verschickt!", "we_sent_invite_link": "Einladungen wurden an alle E-Mails verschickt!",
"we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen. Klicke auf den Link, um dem Team beizutreten." "we_sent_invite_link_description": "Bitte alle eingeladenen Personen, ihren Posteingang zu überprüfen. Klicke auf den Link, um dem Team beizutreten."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Ματαίωση", "cancel": "Ματαίωση",
"choose_file": "Επιλέξτε ένα αρχείο", "choose_file": "Επιλέξτε ένα αρχείο",
@@ -11,7 +10,6 @@
"connect": "Συνδέω-συωδεομαι", "connect": "Συνδέω-συωδεομαι",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "αντίγραφο", "copy": "αντίγραφο",
"create": "Create",
"delete": "Διαγράφω", "delete": "Διαγράφω",
"disconnect": "Αποσυνδέω", "disconnect": "Αποσυνδέω",
"dismiss": "Απολύω", "dismiss": "Απολύω",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Ωραιοποιώ", "prettify": "Ωραιοποιώ",
"properties": "Properties",
"remove": "Αφαιρώ", "remove": "Αφαιρώ",
"rename": "Rename", "rename": "Rename",
"restore": "Επαναφέρω", "restore": "Επαναφέρω",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Αναζήτηση", "search": "Αναζήτηση",
"send": "Στείλετε", "send": "Στείλετε",
"share": "Share",
"start": "Αρχή", "start": "Αρχή",
"starting": "Starting", "starting": "Starting",
"stop": "Να σταματήσει", "stop": "Να σταματήσει",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "μίλα μαζί μας", "chat_with_us": "μίλα μαζί μας",
"contact_us": "Επικοινωνήστε μαζί μας", "contact_us": "Επικοινωνήστε μαζί μας",
"cookies": "Cookies",
"copy": "αντίγραφο", "copy": "αντίγραφο",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Εργαλεία για προγραμματιστές που βοηθάνε στην ανάπτυξη και συντήρηση του Hoppscotch.", "developer_option_description": "Εργαλεία για προγραμματιστές που βοηθάνε στην ανάπτυξη και συντήρηση του Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου", "keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Βρέθηκε νέα έκδοση. Ανανέωση για ενημέρωση.", "new_version_found": "Βρέθηκε νέα έκδοση. Ανανέωση για ενημέρωση.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Πολιτική απορρήτου μεσολάβησης", "proxy_privacy_policy": "Πολιτική απορρήτου μεσολάβησης",
"reload": "Φορτώνω πάλι", "reload": "Φορτώνω πάλι",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Δημιουργήστε το διακριτικό", "generate_token": "Δημιουργήστε το διακριτικό",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Συμπερίληψη στη διεύθυνση URL", "include_in_url": "Συμπερίληψη στη διεύθυνση URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Μάθε πως", "learn": "Μάθε πως",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Κωδικός πρόσβασης", "password": "Κωδικός πρόσβασης",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Ενδειξη", "token": "Ενδειξη",
"type": "Τύπος εξουσιοδότησης", "type": "Τύπος εξουσιοδότησης",
"username": "Όνομα χρήστη" "username": "Όνομα χρήστη"
@@ -148,7 +124,6 @@
"created": "Η συλλογή δημιουργήθηκε", "created": "Η συλλογή δημιουργήθηκε",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Επεξεργασία Συλλογής", "edit": "Επεξεργασία Συλλογής",
"import_or_create": "Import or create a collection",
"invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για τη συλλογή", "invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για τη συλλογή",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Νέα συλλογή", "new": "Νέα συλλογή",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Η συλλογή μετονομάστηκε", "renamed": "Η συλλογή μετονομάστηκε",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Αποθήκευση ως", "save_as": "Αποθήκευση ως",
@@ -178,7 +151,6 @@
"remove_folder": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτόν τον φάκελο;", "remove_folder": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτόν τον φάκελο;",
"remove_history": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά όλο το ιστορικό;", "remove_history": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά όλο το ιστορικό;",
"remove_request": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το αίτημα;", "remove_request": "Είστε βέβαιοι ότι θέλετε να διαγράψετε οριστικά αυτό το αίτημα;",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την ομάδα;", "remove_team": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτήν την ομάδα;",
"remove_telemetry": "Είστε βέβαιοι ότι θέλετε να εξαιρεθείτε από την τηλεμετρία;", "remove_telemetry": "Είστε βέβαιοι ότι θέλετε να εξαιρεθείτε από την τηλεμετρία;",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Κεφαλίδα {count}", "header": "Κεφαλίδα {count}",
"message": "Μήνυμα {count}", "message": "Μήνυμα {count}",
@@ -238,13 +192,11 @@
"profile": "Συνδεθείτε για προβολή του προφίλ σας", "profile": "Συνδεθείτε για προβολή του προφίλ σας",
"protocols": "Τα πρωτόκολλα είναι κενά", "protocols": "Τα πρωτόκολλα είναι κενά",
"schema": "Συνδεθείτε σε ένα τελικό σημείο GraphQL", "schema": "Συνδεθείτε σε ένα τελικό σημείο GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Τα Shortcodes είναι κενά",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Το όνομα της ομάδας είναι κενό", "team_name": "Το όνομα της ομάδας είναι κενό",
"teams": "Οι ομάδες είναι άδειες", "teams": "Οι ομάδες είναι άδειες",
"tests": "Δεν υπάρχουν δοκιμές για αυτό το αίτημα", "tests": "Δεν υπάρχουν δοκιμές για αυτό το αίτημα"
"shortcodes": "Τα Shortcodes είναι κενά"
}, },
"environment": { "environment": {
"add_to_global": "Προσθήκη στο Global", "add_to_global": "Προσθήκη στο Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για το περιβάλλον", "invalid_name": "Καταχωρίστε ένα έγκυρο όνομα για το περιβάλλον",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "Τα Περιβάλλοντα μου", "my_environments": "Τα Περιβάλλοντα μου",
@@ -281,10 +232,8 @@
"variable_list": "Λίστα μεταβλητών" "variable_list": "Λίστα μεταβλητών"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.", "browser_support_sse": "Αυτό το πρόγραμμα περιήγησης δεν φαίνεται να υποστηρίζει διακομιστές που έχουν σταλεί συμβάντα.",
"check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.", "check_console_details": "Ελέγξτε το αρχείο καταγραφής της κονσόλας για λεπτομέρειες.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά", "curl_invalid_format": "Το cURL δεν έχει μορφοποιηθεί σωστά",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Δεν ήταν δυνατή η ομορφιά ενός μη έγκυρου σώματος, η επίλυση σφαλμάτων σύνταξης json και η προσπάθεια ξανά", "json_prettify_invalid_body": "Δεν ήταν δυνατή η ομορφιά ενός μη έγκυρου σώματος, η επίλυση σφαλμάτων σύνταξης json και η προσπάθεια ξανά",
"network_error": "Από ότι φαίνεται υπάρχει ένα σφάλμα δικτύου. Παρακαλούμε προσπαθήστε ξανά.", "network_error": "Από ότι φαίνεται υπάρχει ένα σφάλμα δικτύου. Παρακαλούμε προσπαθήστε ξανά.",
"network_fail": "Δεν ήταν δυνατή η αποστολή του αιτήματος", "network_fail": "Δεν ήταν δυνατή η αποστολή του αιτήματος",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Χωρίς διάρκεια", "no_duration": "Χωρίς διάρκεια",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "Δεν βρέθηκαν αντιστοιχίες", "no_results_found": "Δεν βρέθηκαν αντιστοιχίες",
"page_not_found": "Αυτή η σελίδα δεν βρέθηκε", "page_not_found": "Αυτή η σελίδα δεν βρέθηκε",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Δεν ήταν δυνατή η εκτέλεση του σεναρίου πριν από το αίτημα", "script_fail": "Δεν ήταν δυνατή η εκτέλεση του σεναρίου πριν από το αίτημα",
"something_went_wrong": "Κάτι πήγε στραβά", "something_went_wrong": "Κάτι πήγε στραβά",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Εξαγωγή ως JSON", "as_json": "Εξαγωγή ως JSON",
"create_secret_gist": "Δημιουργήστε μυστική ουσία", "create_secret_gist": "Δημιουργήστε μυστική ουσία",
"failed": "Something went wrong while exporting",
"gist_created": "Η ουσία δημιουργήθηκε", "gist_created": "Η ουσία δημιουργήθηκε",
"require_github": "Συνδεθείτε με το GitHub για να δημιουργήσετε μυστική ουσία", "require_github": "Συνδεθείτε με το GitHub για να δημιουργήσετε μυστική ουσία",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Συνδρομές", "subscriptions": "Συνδρομές",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Η κεφαλίδα εξουσιοδότησης θα δημιουργηθεί αυτόματα κατά την αποστολή του αιτήματος.", "authorization": "Η κεφαλίδα εξουσιοδότησης θα δημιουργηθεί αυτόματα κατά την αποστολή του αιτήματος.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Δημιουργήστε πρώτα έγγραφα", "generate_documentation_first": "Δημιουργήστε πρώτα έγγραφα",
"network_fail": "Δεν είναι δυνατή η πρόσβαση στο τελικό σημείο API. Ελέγξτε τη σύνδεση δικτύου και δοκιμάστε ξανά.", "network_fail": "Δεν είναι δυνατή η πρόσβαση στο τελικό σημείο API. Ελέγξτε τη σύνδεση δικτύου και δοκιμάστε ξανά.",
"offline": "Φαίνεται ότι είστε εκτός σύνδεσης. Τα δεδομένα σε αυτόν τον χώρο εργασίας ενδέχεται να μην είναι ενημερωμένα.", "offline": "Φαίνεται ότι είστε εκτός σύνδεσης. Τα δεδομένα σε αυτόν τον χώρο εργασίας ενδέχεται να μην είναι ενημερωμένα.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Εισαγωγή συλλογών", "collections": "Εισαγωγή συλλογών",
"curl": "Εισαγωγή cURL", "curl": "Εισαγωγή cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Η εισαγωγή απέτυχε", "failed": "Η εισαγωγή απέτυχε",
"from_file": "Import from File",
"from_gist": "Εισαγωγή από το Gist", "from_gist": "Εισαγωγή από το Gist",
"from_gist_description": "Εισαγωγή από Gist URL", "from_gist_description": "Εισαγωγή από Gist URL",
"from_insomnia": "Εισαγωγή από Insomnia", "from_insomnia": "Εισαγωγή από Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Εισαγωγή Συλλογής από Postman", "from_postman_description": "Εισαγωγή Συλλογής από Postman",
"from_url": "Εισαγωγή από URL", "from_url": "Εισαγωγή από URL",
"gist_url": "Εισαγάγετε Gist URL", "gist_url": "Εισαγάγετε Gist URL",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Δεν μπορέσαμε να πάρουμε δεδομένα από το url", "import_from_url_invalid_fetch": "Δεν μπορέσαμε να πάρουμε δεδομένα από το url",
"import_from_url_invalid_file_format": "Σφάλμα κατά την εισαγωγή των Συλλογών", "import_from_url_invalid_file_format": "Σφάλμα κατά την εισαγωγή των Συλλογών",
"import_from_url_invalid_type": "Μη υποστηριζόμενος τύπος. Αποδεκτές τιμές είναι 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Μη υποστηριζόμενος τύπος. Αποδεκτές τιμές είναι 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Η εισαγωγή των Συλλογών ήταν επιτυχής", "import_from_url_success": "Η εισαγωγή των Συλλογών ήταν επιτυχής",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Εισαγωγή συλλογών αρχείο JSON Hoppscotch Collections", "json_description": "Εισαγωγή συλλογών αρχείο JSON Hoppscotch Collections",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Εισαγωγή" "title": "Εισαγωγή"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Συλλογές", "collections": "Συλλογές",
"confirm": "Επιβεβαιώνω", "confirm": "Επιβεβαιώνω",
"customize_request": "Customize Request",
"edit_request": "Αίτημα Επεξεργασίας", "edit_request": "Αίτημα Επεξεργασίας",
"import_export": "Εισαγωγή εξαγωγή", "import_export": "Εισαγωγή εξαγωγή"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Αντιγραφή συνδέσμου",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Διάρκεια", "duration": "Διάρκεια",
"enter_curl": "Εισαγάγετε cURL", "enter_curl": "Εισαγάγετε cURL",
"generate_code": "Δημιουργία κώδικα", "generate_code": "Δημιουργία κώδικα",
"generated_code": "Παραγόμενος κώδικας", "generated_code": "Παραγόμενος κώδικας",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Λίστα κεφαλίδων", "header_list": "Λίστα κεφαλίδων",
"invalid_name": "Καταχωρίστε ένα όνομα για το αίτημα", "invalid_name": "Καταχωρίστε ένα όνομα για το αίτημα",
"method": "Μέθοδος", "method": "Μέθοδος",
@@ -552,14 +480,12 @@
"saved": "Το αίτημα αποθηκεύτηκε", "saved": "Το αίτημα αποθηκεύτηκε",
"share": "Μερίδιο", "share": "Μερίδιο",
"share_description": "Κοινοποίηση Hoppscotch στους φίλους σου", "share_description": "Κοινοποίηση Hoppscotch στους φίλους σου",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Αίτηση", "title": "Αίτηση",
"type": "Τύπος αιτήματος", "type": "Τύπος αιτήματος",
"url": "URL", "url": "URL",
"variables": "Μεταβλητές", "variables": "Μεταβλητές",
"view_my_links": "Προβολή των links μου", "view_my_links": "Προβολή των links μου"
"copy_link": "Αντιγραφή συνδέσμου"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.", "account_description": "Προσαρμόστε τις ρυθμίσεις του λογαριασμού σας.",
"account_email_description": "Η κύρια διεύθυνση email σας.", "account_email_description": "Η κύρια διεύθυνση email σας.",
"account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.", "account_name_description": "Αυτό είναι το εμφανιζόμενο όνομά σας.",
"additional": "Additional Settings",
"background": "Ιστορικό", "background": "Ιστορικό",
"black_mode": "Μαύρος", "black_mode": "Μαύρος",
"choose_language": "Διάλεξε γλώσσα", "choose_language": "Διάλεξε γλώσσα",
@@ -634,31 +559,14 @@
"verified_email": "Επαληθευμένο email", "verified_email": "Επαληθευμένο email",
"verify_email": "Επαλήθευση email" "verify_email": "Επαλήθευση email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Δράσεις",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Το Shortcode διαγράφηκε",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Το Shortcode δεν βρέθηκε",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Αντιγραφή συνδέσμου αιτήματος",
"delete_method": "Επιλέξτε ΔΙΑΓΡΑΦΗ μεθόδου", "delete_method": "Επιλέξτε ΔΙΑΓΡΑΦΗ μεθόδου",
"get_method": "Επιλέξτε μέθοδο GET", "get_method": "Επιλέξτε μέθοδο GET",
"head_method": "Επιλέξτε μέθοδο HEAD", "head_method": "Επιλέξτε μέθοδο HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Αποθήκευση στις Συλλογές", "save_to_collections": "Αποθήκευση στις Συλλογές",
"send_request": "Στείλε αίτημα", "send_request": "Στείλε αίτημα",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Αίτηση", "title": "Αίτηση"
"copy_request_link": "Αντιγραφή συνδέσμου αιτήματος"
}, },
"response": { "response": {
"copy": "Αντιγραφή response στο πρόχειρο", "copy": "Αντιγραφή response στο πρόχειρο",
@@ -828,7 +735,6 @@
"connection_error": "Η Σύνδεση απέτυχε", "connection_error": "Η Σύνδεση απέτυχε",
"connection_failed": "Αποτυχής Σύνδεση", "connection_failed": "Αποτυχής Σύνδεση",
"connection_lost": "Η Σύνδεση χάθηκε", "connection_lost": "Η Σύνδεση χάθηκε",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Αντιγράφηκε στο πρόχειρο", "copied_to_clipboard": "Αντιγράφηκε στο πρόχειρο",
"deleted": "Διαγράφηκε", "deleted": "Διαγράφηκε",
"deprecated": "ΚΑΤΑΡΓΗΘΗΚΕ", "deprecated": "ΚΑΤΑΡΓΗΘΗΚΕ",
@@ -836,12 +742,10 @@
"disconnected": "Ασύνδετος", "disconnected": "Ασύνδετος",
"disconnected_from": "Αποσυνδέθηκε από το {name}", "disconnected_from": "Αποσυνδέθηκε από το {name}",
"docs_generated": "Δημιουργήθηκε τεκμηρίωση", "docs_generated": "Δημιουργήθηκε τεκμηρίωση",
"download_failed": "Download failed",
"download_started": "Η λήψη ξεκίνησε", "download_started": "Η λήψη ξεκίνησε",
"enabled": "Ενεργοποιημένο", "enabled": "Ενεργοποιημένο",
"file_imported": "Το αρχείο εισήχθη", "file_imported": "Το αρχείο εισήχθη",
"finished_in": "Ολοκληρώθηκε σε {duration} ms", "finished_in": "Ολοκληρώθηκε σε {duration} ms",
"hide": "Hide",
"history_deleted": "Το ιστορικό διαγράφηκε", "history_deleted": "Το ιστορικό διαγράφηκε",
"linewrap": "Τυλίξτε γραμμές", "linewrap": "Τυλίξτε γραμμές",
"loading": "Φόρτωση...", "loading": "Φόρτωση...",
@@ -852,7 +756,6 @@
"published_error": "Κάτι πήγε στραβά κατα την αποστολή του μηνύματος: {topic} με θέμα: {message}", "published_error": "Κάτι πήγε στραβά κατα την αποστολή του μηνύματος: {topic} με θέμα: {message}",
"published_message": "Δημοσιευμένο Μηνυμα: {message} με θέμα: {topic}", "published_message": "Δημοσιευμένο Μηνυμα: {message} με θέμα: {topic}",
"reconnection_error": "Αποτυχία επανασύνδεσης", "reconnection_error": "Αποτυχία επανασύνδεσης",
"show": "Show",
"subscribed_failed": "Αποτυχία εγγραφής στο Θέμα: {topic}", "subscribed_failed": "Αποτυχία εγγραφής στο Θέμα: {topic}",
"subscribed_success": "Επιτυχία εγγραφής στο Θέμα: {topic}", "subscribed_success": "Επιτυχία εγγραφής στο Θέμα: {topic}",
"unsubscribed_failed": "Αποτυχία απεγγραφής στο Θέμα: {topic}", "unsubscribed_failed": "Αποτυχία απεγγραφής στο Θέμα: {topic}",
@@ -888,7 +791,6 @@
"queries": "Ερωτήματα", "queries": "Ερωτήματα",
"query": "Ερώτηση", "query": "Ερώτηση",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Υποδοχή.IO", "socketio": "Υποδοχή.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Δοκιμές", "tests": "Δοκιμές",
@@ -905,7 +807,6 @@
"email_do_not_match": "Το Email δεν ταιριάζει με τις λεπτομέριες του προφιλ σας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.", "email_do_not_match": "Το Email δεν ταιριάζει με τις λεπτομέριες του προφιλ σας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.",
"exit": "Έξοδος από την ομάδα", "exit": "Έξοδος από την ομάδα",
"exit_disabled": "Μόνο ο ιδιοκτήτης δεν μπορεί να αποχωρήσει από την ομάδα", "exit_disabled": "Μόνο ο ιδιοκτήτης δεν μπορεί να αποχωρήσει από την ομάδα",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Η μορφή ηλεκτρονικού ταχυδρομείου δεν είναι έγκυρη", "invalid_email_format": "Η μορφή ηλεκτρονικού ταχυδρομείου δεν είναι έγκυρη",
"invalid_id": "Μή εγκυρο αναγνωριστικό ομάδας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.", "invalid_id": "Μή εγκυρο αναγνωριστικό ομάδας. Επικοινωνήστε με τον Ιδιοκτήτη της Ομάδας.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Η ομάδα σώθηκε", "saved": "Η ομάδα σώθηκε",
"select_a_team": "Επιλογή ομάδας", "select_a_team": "Επιλογή ομάδας",
"success_invites": "Success invites",
"title": "Της ομάδας", "title": "Της ομάδας",
"we_sent_invite_link": "Στείλαμε έναν σύνδεσμο πρόσκλησης σε όλους!", "we_sent_invite_link": "Στείλαμε έναν σύνδεσμο πρόσκλησης σε όλους!",
"we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα." "we_sent_invite_link_description": "Ζητήστε από όλους όσους στείλατε πρόσκληση να ελέγξουν τα email τους. Click στον σύνδεσμο για εισαγωγή στην ομάδα."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Δράσεις",
"created_on": "Created on",
"deleted": "Το Shortcode διαγράφηκε",
"method": "Method",
"not_found": "Το Shortcode δεν βρέθηκε",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -33,7 +33,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Prettify", "prettify": "Prettify",
"properties": "Properties",
"remove": "Remove", "remove": "Remove",
"rename": "Rename", "rename": "Rename",
"restore": "Restore", "restore": "Restore",
@@ -58,6 +57,24 @@
"new": "Add new", "new": "Add new",
"star": "Add star" "star": "Add star"
}, },
"cookies": {
"modal": {
"new_domain_name": "New domain name",
"set": "Set a cookie",
"cookie_string": "Cookie string",
"enter_cookie_string": "Enter cookie string",
"cookie_name": "Name",
"cookie_value": "Value",
"cookie_path": "Path",
"cookie_expires": "Expires",
"managed_tab": "Managed",
"raw_tab": "Raw",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"empty_domains": "Domain list is empty",
"empty_domain": "Domain is empty",
"no_cookies_in_domain": "No cookies set for this domain"
}
},
"app": { "app": {
"chat_with_us": "Chat with us", "chat_with_us": "Chat with us",
"contact_us": "Contact us", "contact_us": "Contact us",
@@ -121,28 +138,26 @@
"generate_token": "Generate Token", "generate_token": "Generate Token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init", "graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Include in URL", "include_in_url": "Include in URL",
"inherited_from": "Inherited {auth} from parent collection {collection} ",
"learn": "Learn how", "learn": "Learn how",
"pass_key_by": "Pass by",
"password": "Password",
"token": "Token",
"type": "Authorization Type",
"username": "Username",
"oauth": { "oauth": {
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"redirect_auth_server_returned_error": "Auth Server returned an error state", "redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect", "redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"redirect_no_client_id": "No Client ID defined", "redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined", "redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined", "redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined", "redirect_auth_token_request_failed": "Request to get the auth token failed",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect", "redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"something_went_wrong_on_token_generation": "Something went wrong on token generation", "something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect"
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed" }
},
"pass_key_by": "Pass by",
"password": "Password",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token",
"type": "Authorization Type",
"username": "Username"
}, },
"collection": { "collection": {
"created": "Collection created", "created": "Collection created",
@@ -157,8 +172,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "New Collection", "new": "New Collection",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Collection renamed", "renamed": "Collection renamed",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Save as", "save_as": "Save as",
@@ -190,24 +203,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Header {count}", "header": "Header {count}",
"message": "Message {count}", "message": "Message {count}",
@@ -238,8 +233,8 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protocols are empty", "protocols": "Protocols are empty",
"schema": "Connect to a GraphQL endpoint to view schema", "schema": "Connect to a GraphQL endpoint to view schema",
"shared_requests": "Shared requests are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one", "shared_requests_logout": "Login to view your shared requests or create a new one",
"shared_requests": "Shared requests are empty",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Team name empty", "team_name": "Team name empty",
"teams": "You don't belong to any teams", "teams": "You don't belong to any teams",
@@ -279,8 +274,10 @@
"variable": "Variable", "variable": "Variable",
"variable_list": "Variable List" "variable_list": "Variable List"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "This browser doesn't seems to have Server Sent Events support.", "browser_support_sse": "This browser doesn't seems to have Server Sent Events support.",
"check_console_details": "Check console log for details.", "check_console_details": "Check console log for details.",
"check_how_to_add_origin": "Check how you can add an origin", "check_how_to_add_origin": "Check how you can add an origin",
@@ -295,7 +292,6 @@
"incorrect_email": "Incorrect email", "incorrect_email": "Incorrect email",
"invalid_link": "Invalid link", "invalid_link": "Invalid link",
"invalid_link_description": "The link you clicked is invalid or expired.", "invalid_link_description": "The link you clicked is invalid or expired.",
"invalid_embed_link": "The embed does not exist or is invalid.",
"json_parsing_failed": "Invalid JSON", "json_parsing_failed": "Invalid JSON",
"json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again", "json_prettify_invalid_body": "Couldn't prettify an invalid body, solve json syntax errors and try again",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
@@ -314,12 +310,10 @@
"export": { "export": {
"as_json": "Export as JSON", "as_json": "Export as JSON",
"create_secret_gist": "Create secret Gist", "create_secret_gist": "Create secret Gist",
"create_secret_gist_tooltip_text": "Export as secret Gist", "gist_created": "Gist created",
"failed": "Something went wrong while exporting",
"secret_gist_success": "Successfully exported as secret Gist",
"require_github": "Login with GitHub to create secret gist", "require_github": "Login with GitHub to create secret gist",
"title": "Export", "title": "Export",
"success": "Successfully exported" "failed": "Something went wrong while exporting"
}, },
"filter": { "filter": {
"all": "All", "all": "All",
@@ -343,9 +337,6 @@
"subscriptions": "Subscriptions", "subscriptions": "Subscriptions",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -357,8 +348,6 @@
}, },
"helpers": { "helpers": {
"authorization": "The authorization header will be automatically generated when you send the request.", "authorization": "The authorization header will be automatically generated when you send the request.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Generate documentation first", "generate_documentation_first": "Generate documentation first",
"network_fail": "Unable to reach the API endpoint. Check your network connection or select a different Interceptor and try again.", "network_fail": "Unable to reach the API endpoint. Check your network connection or select a different Interceptor and try again.",
"offline": "You're using Hoppscotch offline. Updates will sync when you're online, based on workspace settings.", "offline": "You're using Hoppscotch offline. Updates will sync when you're online, based on workspace settings.",
@@ -378,10 +367,7 @@
"import": { "import": {
"collections": "Import collections", "collections": "Import collections",
"curl": "Import cURL", "curl": "Import cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Error while importing: format not recognized", "failed": "Error while importing: format not recognized",
"from_file": "Import from File",
"from_gist": "Import from Gist", "from_gist": "Import from Gist",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,20 +380,22 @@
"from_openapi_description": "Import from OpenAPI specification file (YML/JSON)", "from_openapi_description": "Import from OpenAPI specification file (YML/JSON)",
"from_postman": "Import from Postman", "from_postman": "Import from Postman",
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_file": "Import from File",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Enter Gist URL", "gist_url": "Enter Gist URL",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"title": "Import",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"postman_environment": "Postman Environment", "postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file", "postman_environment_description": "Import Postman Environment JSON file",
"title": "Import" "environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist"
}, },
"inspections": { "inspections": {
"description": "Inspect possible errors", "description": "Inspect possible errors",
@@ -446,8 +434,8 @@
"confirm": "Confirm", "confirm": "Confirm",
"customize_request": "Customize Request", "customize_request": "Customize Request",
"edit_request": "Edit Request", "edit_request": "Edit Request",
"import_export": "Import / Export", "share_request": "Share Request",
"share_request": "Share Request" "import_export": "Import / Export"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -588,7 +576,6 @@
"account_description": "Customize your account settings.", "account_description": "Customize your account settings.",
"account_email_description": "Your primary email address.", "account_email_description": "Your primary email address.",
"account_name_description": "This is your display name.", "account_name_description": "This is your display name.",
"additional": "Additional Settings",
"background": "Background", "background": "Background",
"black_mode": "Black", "black_mode": "Black",
"choose_language": "Choose language", "choose_language": "Choose language",
@@ -633,16 +620,17 @@
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting", "use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
"user": "User", "user": "User",
"verified_email": "Verified email", "verified_email": "Verified email",
"additional": "Additional Settings",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shared_requests": {
"button": "Button", "button": "Button",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.",
"customize": "Customize",
"creating_widget": "Creating widget",
"copy_html": "Copy HTML", "copy_html": "Copy HTML",
"copy_link": "Copy Link", "copy_link": "Copy Link",
"copy_markdown": "Copy Markdown", "copy_markdown": "Copy Markdown",
"creating_widget": "Creating widget",
"customize": "Customize",
"deleted": "Shared request deleted", "deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later", "description": "Select a widget, you can change and customize this later",
"embed": "Embed", "embed": "Embed",
@@ -703,8 +691,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Save to Collections", "save_to_collections": "Save to Collections",
"send_request": "Send Request", "send_request": "Send Request",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"share_request": "Share Request",
"title": "Request" "title": "Request"
}, },
"response": { "response": {
@@ -828,16 +816,16 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copied to clipboard", "copied_to_clipboard": "Copied to clipboard",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"deleted": "Deleted", "deleted": "Deleted",
"deprecated": "DEPRECATED", "deprecated": "DEPRECATED",
"disabled": "Disabled", "disabled": "Disabled",
"disconnected": "Disconnected", "disconnected": "Disconnected",
"disconnected_from": "Disconnected from {name}", "disconnected_from": "Disconnected from {name}",
"docs_generated": "Documentation generated", "docs_generated": "Documentation generated",
"download_failed": "Download failed",
"download_started": "Download started", "download_started": "Download started",
"download_failed": "Download failed",
"enabled": "Enabled", "enabled": "Enabled",
"file_imported": "File imported", "file_imported": "File imported",
"finished_in": "Finished in {duration} ms", "finished_in": "Finished in {duration} ms",
@@ -905,7 +893,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Exit Team", "exit": "Exit Team",
"exit_disabled": "Only owner cannot exit the team", "exit_disabled": "Only owner cannot exit the team",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Email format is invalid", "invalid_email_format": "Email format is invalid",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -942,12 +929,13 @@
"not_found": "Team not found. Contact your team owner.", "not_found": "Team not found. Contact your team owner.",
"not_valid_viewer": "You are not a valid viewer. Contact your team owner.", "not_valid_viewer": "You are not a valid viewer. Contact your team owner.",
"parent_coll_move": "Cannot move collection to a child collection", "parent_coll_move": "Cannot move collection to a child collection",
"success_invites": "Success invites",
"failed_invites": "Failed invites",
"pending_invites": "Pending invites", "pending_invites": "Pending invites",
"permissions": "Permissions", "permissions": "Permissions",
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Team saved", "saved": "Team saved",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Teams", "title": "Teams",
"we_sent_invite_link": "We sent an invite link to all invitees!", "we_sent_invite_link": "We sent an invite link to all invitees!",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Desplazamiento automático", "autoscroll": "Desplazamiento automático",
"cancel": "Cancelar", "cancel": "Cancelar",
"choose_file": "Seleccionar archivo", "choose_file": "Seleccionar archivo",
@@ -11,7 +10,6 @@
"connect": "Conectar", "connect": "Conectar",
"connecting": "Conectando", "connecting": "Conectando",
"copy": "Copiar", "copy": "Copiar",
"create": "Create",
"delete": "Borrar", "delete": "Borrar",
"disconnect": "Desconectar", "disconnect": "Desconectar",
"dismiss": "Descartar", "dismiss": "Descartar",
@@ -33,7 +31,6 @@
"open_workspace": "Abrir espacio de trabajo", "open_workspace": "Abrir espacio de trabajo",
"paste": "Pegar", "paste": "Pegar",
"prettify": "Embellecer", "prettify": "Embellecer",
"properties": "Properties",
"remove": "Eliminar", "remove": "Eliminar",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurar", "restore": "Restaurar",
@@ -42,7 +39,6 @@
"scroll_to_top": "Desplazar hacia arriba", "scroll_to_top": "Desplazar hacia arriba",
"search": "Buscar", "search": "Buscar",
"send": "Enviar", "send": "Enviar",
"share": "Share",
"start": "Comenzar", "start": "Comenzar",
"starting": "Iniciando", "starting": "Iniciando",
"stop": "Detener", "stop": "Detener",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Habla con nosotros", "chat_with_us": "Habla con nosotros",
"contact_us": "Contáctanos", "contact_us": "Contáctanos",
"cookies": "Cookies",
"copy": "Copiar", "copy": "Copiar",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copiar token de autenticación de usuario", "copy_user_id": "Copiar token de autenticación de usuario",
"developer_option": "Opciones para desarrolladores", "developer_option": "Opciones para desarrolladores",
"developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.", "developer_option_description": "Herramientas para desarrolladores que ayudan en el desarrollo y mantenimiento de Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Atajos de teclado", "keyboard_shortcuts": "Atajos de teclado",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.", "new_version_found": "Se ha encontrado una nueva versión. Recarga la página para usarla.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Política de privacidad de proxy", "proxy_privacy_policy": "Política de privacidad de proxy",
"reload": "Recargar", "reload": "Recargar",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Generar token", "generate_token": "Generar token",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Incluir en la URL", "include_in_url": "Incluir en la URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Aprender", "learn": "Aprender",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pasar por", "pass_key_by": "Pasar por",
"password": "Contraseña", "password": "Contraseña",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Tipo de autorización", "type": "Tipo de autorización",
"username": "Nombre de usuario" "username": "Nombre de usuario"
@@ -148,7 +124,6 @@
"created": "Colección creada", "created": "Colección creada",
"different_parent": "No se puede reordenar la colección con un padre diferente", "different_parent": "No se puede reordenar la colección con un padre diferente",
"edit": "Editar colección", "edit": "Editar colección",
"import_or_create": "Import or create a collection",
"invalid_name": "Proporciona un nombre válido para la colección.", "invalid_name": "Proporciona un nombre válido para la colección.",
"invalid_root_move": "La colección ya está en la raíz", "invalid_root_move": "La colección ya está en la raíz",
"moved": "Movido con éxito", "moved": "Movido con éxito",
@@ -157,8 +132,6 @@
"name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres", "name_length_insufficient": "El nombre de la colección debe tener al menos 3 caracteres",
"new": "Nueva colección", "new": "Nueva colección",
"order_changed": "Orden de colección actualizada", "order_changed": "Orden de colección actualizada",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Colección renombrada", "renamed": "Colección renombrada",
"request_in_use": "Solicitud en uso", "request_in_use": "Solicitud en uso",
"save_as": "Guardar como", "save_as": "Guardar como",
@@ -178,7 +151,6 @@
"remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?", "remove_folder": "¿Estás seguro de que deseas eliminar esta carpeta de forma permanente?",
"remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?", "remove_history": "¿Estás seguro de que deseas eliminar todo el historial de forma permanente?",
"remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?", "remove_request": "¿Estás seguro de que deseas eliminar esta solicitud de forma permanente?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "¿Estás seguro de que deseas eliminar este equipo?", "remove_team": "¿Estás seguro de que deseas eliminar este equipo?",
"remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?", "remove_telemetry": "¿Estás seguro de que deseas darse de baja de la telemetría?",
"request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.", "request_change": "¿Estás seguro de que deseas descartar la solicitud actual, los cambios no guardados se perderán.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Encabezado {count}", "header": "Encabezado {count}",
"message": "Mensaje {count}", "message": "Mensaje {count}",
@@ -238,13 +192,11 @@
"profile": "Iniciar sesión para ver tu perfil", "profile": "Iniciar sesión para ver tu perfil",
"protocols": "Los protocolos están vacíos", "protocols": "Los protocolos están vacíos",
"schema": "Conectarse a un punto final de GraphQL", "schema": "Conectarse a un punto final de GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Aún no se han creado Shortcodes",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Nombre del equipo vacío", "team_name": "Nombre del equipo vacío",
"teams": "Los equipos están vacíos", "teams": "Los equipos están vacíos",
"tests": "No hay pruebas para esta solicitud", "tests": "No hay pruebas para esta solicitud"
"shortcodes": "Aún no se han creado Shortcodes"
}, },
"environment": { "environment": {
"add_to_global": "Añadir a Global", "add_to_global": "Añadir a Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Proporciona un nombre válido para el entorno.", "invalid_name": "Proporciona un nombre válido para el entorno.",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "Mis entornos", "my_environments": "Mis entornos",
@@ -281,10 +232,8 @@
"variable_list": "Lista de variables" "variable_list": "Lista de variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.", "browser_support_sse": "Este navegador no parece ser compatible con los eventos enviados por el servidor.",
"check_console_details": "Consulta el registro de la consola para obtener más detalles.", "check_console_details": "Consulta el registro de la consola para obtener más detalles.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL no está formateado correctamente", "curl_invalid_format": "cURL no está formateado correctamente",
"danger_zone": "Zona de peligro", "danger_zone": "Zona de peligro",
"delete_account": "Tu cuenta es actualmente propietaria en estos equipos:", "delete_account": "Tu cuenta es actualmente propietaria en estos equipos:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo", "json_prettify_invalid_body": "No se puede aplicar embellecedor a un cuerpo inválido, resuelve errores de sintaxis json y vuelve a intentarlo",
"network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.", "network_error": "Parece que hay un error de red. Por favor, inténtalo de nuevo.",
"network_fail": "No se pudo enviar la solicitud", "network_fail": "No se pudo enviar la solicitud",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Sin duración", "no_duration": "Sin duración",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No se han encontrado coincidencias", "no_results_found": "No se han encontrado coincidencias",
"page_not_found": "No se ha podido encontrar esta página", "page_not_found": "No se ha podido encontrar esta página",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "No se pudo ejecutar el script de solicitud previa", "script_fail": "No se pudo ejecutar el script de solicitud previa",
"something_went_wrong": "Algo salió mal", "something_went_wrong": "Algo salió mal",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exportar como JSON", "as_json": "Exportar como JSON",
"create_secret_gist": "Crear un Gist secreto", "create_secret_gist": "Crear un Gist secreto",
"failed": "Something went wrong while exporting",
"gist_created": "Gist creado", "gist_created": "Gist creado",
"require_github": "Iniciar sesión con GitHub para crear un Gist secreto", "require_github": "Iniciar sesión con GitHub para crear un Gist secreto",
"title": "Exportar" "title": "Exportar"
@@ -341,9 +286,6 @@
"subscriptions": "Suscripciones", "subscriptions": "Suscripciones",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Tiempo", "time": "Tiempo",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.", "authorization": "El encabezado de autorización se generará automáticamente cuando se envía la solicitud.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Generar la documentación primero", "generate_documentation_first": "Generar la documentación primero",
"network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.", "network_fail": "No se puede acceder a la API. Comprueba tu conexión de red y vuelve a intentarlo.",
"offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.", "offline": "Parece estar desconectado. Es posible que los datos de este espacio de trabajo no estén actualizados.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importar colecciones", "collections": "Importar colecciones",
"curl": "Importar cURL", "curl": "Importar cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Importación fallida", "failed": "Importación fallida",
"from_file": "Import from File",
"from_gist": "Importar desde Gist", "from_gist": "Importar desde Gist",
"from_gist_description": "Importar desde URL de Gist", "from_gist_description": "Importar desde URL de Gist",
"from_insomnia": "Importar desde Insomnia", "from_insomnia": "Importar desde Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Importar desde una colección de Postman", "from_postman_description": "Importar desde una colección de Postman",
"from_url": "Importar desde una URL", "from_url": "Importar desde una URL",
"gist_url": "Introduce la URL de Gist", "gist_url": "Introduce la URL de Gist",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch", "json_description": "Importar colecciones desde un archivo JSON de colecciones de Hoppscotch",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importar" "title": "Importar"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "Tienes cambios sin guardar", "close_unsaved_tab": "Tienes cambios sin guardar",
"collections": "Colecciones", "collections": "Colecciones",
"confirm": "Confirmar", "confirm": "Confirmar",
"customize_request": "Customize Request",
"edit_request": "Editar solicitud", "edit_request": "Editar solicitud",
"import_export": "Importación y exportación", "import_export": "Importación y exportación"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "Ya estás suscrito a este tema.", "already_subscribed": "Ya estás suscrito a este tema.",
@@ -520,14 +449,13 @@
"structured": "Estructurado", "structured": "Estructurado",
"text": "Texto" "text": "Texto"
}, },
"copy_link": "Copiar enlace",
"different_collection": "No se pueden reordenar solicitudes de diferentes colecciones", "different_collection": "No se pueden reordenar solicitudes de diferentes colecciones",
"duplicated": "Solicitud duplicada", "duplicated": "Solicitud duplicada",
"duration": "Duración", "duration": "Duración",
"enter_curl": "Ingrese cURL", "enter_curl": "Ingrese cURL",
"generate_code": "Generar código", "generate_code": "Generar código",
"generated_code": "Código generado", "generated_code": "Código generado",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Lista de encabezados", "header_list": "Lista de encabezados",
"invalid_name": "Proporciona un nombre para la solicitud.", "invalid_name": "Proporciona un nombre para la solicitud.",
"method": "Método", "method": "Método",
@@ -552,14 +480,12 @@
"saved": "Solicitud guardada", "saved": "Solicitud guardada",
"share": "Compartir", "share": "Compartir",
"share_description": "Comparte Hoppscotch con tus amigos", "share_description": "Comparte Hoppscotch con tus amigos",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Solicitud", "title": "Solicitud",
"type": "Tipo de solicitud", "type": "Tipo de solicitud",
"url": "URL", "url": "URL",
"variables": "Variables", "variables": "Variables",
"view_my_links": "Ver mis enlaces", "view_my_links": "Ver mis enlaces"
"copy_link": "Copiar enlace"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Personaliza la configuración de tu cuenta.", "account_description": "Personaliza la configuración de tu cuenta.",
"account_email_description": "Tu dirección de correo electrónico principal.", "account_email_description": "Tu dirección de correo electrónico principal.",
"account_name_description": "Este es tu nombre para mostrar.", "account_name_description": "Este es tu nombre para mostrar.",
"additional": "Additional Settings",
"background": "Fondo", "background": "Fondo",
"black_mode": "Negro", "black_mode": "Negro",
"choose_language": "Elegir idioma", "choose_language": "Elegir idioma",
@@ -634,31 +559,14 @@
"verified_email": "Correo electrónico verificado", "verified_email": "Correo electrónico verificado",
"verify_email": "Verificar correo electrónico" "verify_email": "Verificar correo electrónico"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Acciones",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Creado el",
"copy_html": "Copy HTML", "deleted": "Código corto eliminado",
"copy_link": "Copy Link", "method": "Método",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode no encontrado",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Copiar enlace de solicitud",
"delete_method": "Seleccionar método DELETE", "delete_method": "Seleccionar método DELETE",
"get_method": "Seleccionar método GET", "get_method": "Seleccionar método GET",
"head_method": "Seleccionar método HEAD", "head_method": "Seleccionar método HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Guardar en colecciones", "save_to_collections": "Guardar en colecciones",
"send_request": "Enviar solicitud", "send_request": "Enviar solicitud",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Solicitud", "title": "Solicitud"
"copy_request_link": "Copiar enlace de solicitud"
}, },
"response": { "response": {
"copy": "Copiar la respuesta al portapapeles", "copy": "Copiar la respuesta al portapapeles",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Error de conexión", "connection_failed": "Error de conexión",
"connection_lost": "Conexión perdida", "connection_lost": "Conexión perdida",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copiado al portapapeles", "copied_to_clipboard": "Copiado al portapapeles",
"deleted": "Eliminado", "deleted": "Eliminado",
"deprecated": "OBSOLETO", "deprecated": "OBSOLETO",
@@ -836,12 +742,10 @@
"disconnected": "Desconectado", "disconnected": "Desconectado",
"disconnected_from": "Desconectado de {name}", "disconnected_from": "Desconectado de {name}",
"docs_generated": "Documentación generada", "docs_generated": "Documentación generada",
"download_failed": "Download failed",
"download_started": "Descarga iniciada", "download_started": "Descarga iniciada",
"enabled": "Activado", "enabled": "Activado",
"file_imported": "Archivo importado", "file_imported": "Archivo importado",
"finished_in": "Terminado en {duration} ms", "finished_in": "Terminado en {duration} ms",
"hide": "Hide",
"history_deleted": "Historial eliminado", "history_deleted": "Historial eliminado",
"linewrap": "Envolver líneas", "linewrap": "Envolver líneas",
"loading": "Cargando...", "loading": "Cargando...",
@@ -852,7 +756,6 @@
"published_error": "Algo ha ido mal al publicar el mensaje: {topic} al tema: {message}", "published_error": "Algo ha ido mal al publicar el mensaje: {topic} al tema: {message}",
"published_message": "Mensaje publicado: {mensaje} al tema: {topic}", "published_message": "Mensaje publicado: {mensaje} al tema: {topic}",
"reconnection_error": "Fallo en la reconexión", "reconnection_error": "Fallo en la reconexión",
"show": "Show",
"subscribed_failed": "Error al suscribirse al tema: {topic}", "subscribed_failed": "Error al suscribirse al tema: {topic}",
"subscribed_success": "Suscrito con éxito al tema: {topic}", "subscribed_success": "Suscrito con éxito al tema: {topic}",
"unsubscribed_failed": "Error al darse de baja del tema: {topic}", "unsubscribed_failed": "Error al darse de baja del tema: {topic}",
@@ -888,7 +791,6 @@
"queries": "Consultas", "queries": "Consultas",
"query": "Consulta", "query": "Consulta",
"schema": "Esquema", "schema": "Esquema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Pruebas", "tests": "Pruebas",
@@ -905,7 +807,6 @@
"email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.", "email_do_not_match": "El correo electrónico no coincide con los datos de tu cuenta. Ponte en contacto con el propietario de tu equipo.",
"exit": "Salir del equipo", "exit": "Salir del equipo",
"exit_disabled": "Solo el propietario puede salir del equipo", "exit_disabled": "Solo el propietario puede salir del equipo",
"failed_invites": "Failed invites",
"invalid_coll_id": "Identificador de colección no válido", "invalid_coll_id": "Identificador de colección no válido",
"invalid_email_format": "El formato de correo electrónico no es válido", "invalid_email_format": "El formato de correo electrónico no es válido",
"invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.", "invalid_id": "Identificador de equipo inválido. Ponte en contacto con el propietario de tu equipo.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Equipo guardado", "saved": "Equipo guardado",
"select_a_team": "Seleccionar un equipo", "select_a_team": "Seleccionar un equipo",
"success_invites": "Success invites",
"title": "Equipos", "title": "Equipos",
"we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!", "we_sent_invite_link": "¡Hemos enviado un enlace de invitación a todos los invitados!",
"we_sent_invite_link_description": "Pide a todos los invitados que revisen tu bandeja de entrada. Haz clic en el enlace para unirse al equipo." "we_sent_invite_link_description": "Pide a todos los invitados que revisen tu bandeja de entrada. Haz clic en el enlace para unirse al equipo."
@@ -979,14 +879,5 @@
"personal": "Mi espacio de trabajo", "personal": "Mi espacio de trabajo",
"team": "Espacio de trabajo en equipo", "team": "Espacio de trabajo en equipo",
"title": "Espacios de trabajo" "title": "Espacios de trabajo"
},
"shortcodes": {
"actions": "Acciones",
"created_on": "Creado el",
"deleted": "Código corto eliminado",
"method": "Método",
"not_found": "Shortcode no encontrado",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Peruuttaa", "cancel": "Peruuttaa",
"choose_file": "Valitse tiedosto", "choose_file": "Valitse tiedosto",
@@ -11,7 +10,6 @@
"connect": "Kytkeä", "connect": "Kytkeä",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Kopio", "copy": "Kopio",
"create": "Create",
"delete": "Poistaa", "delete": "Poistaa",
"disconnect": "Katkaista", "disconnect": "Katkaista",
"dismiss": "Hylkää", "dismiss": "Hylkää",
@@ -33,7 +31,6 @@
"open_workspace": "Open workspace", "open_workspace": "Open workspace",
"paste": "Paste", "paste": "Paste",
"prettify": "Koristella", "prettify": "Koristella",
"properties": "Properties",
"remove": "Poista", "remove": "Poista",
"rename": "Rename", "rename": "Rename",
"restore": "Palauttaa", "restore": "Palauttaa",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Hae", "search": "Hae",
"send": "Lähettää", "send": "Lähettää",
"share": "Share",
"start": "alkaa", "start": "alkaa",
"starting": "Starting", "starting": "Starting",
"stop": "Lopettaa", "stop": "Lopettaa",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Juttele kanssamme", "chat_with_us": "Juttele kanssamme",
"contact_us": "Ota meihin yhteyttä", "contact_us": "Ota meihin yhteyttä",
"cookies": "Cookies",
"copy": "Kopio", "copy": "Kopio",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copy User Auth Token", "copy_user_id": "Copy User Auth Token",
"developer_option": "Developer options", "developer_option": "Developer options",
"developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.", "developer_option_description": "Developer tools which helps in development and maintenance of Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Pikanäppäimet", "keyboard_shortcuts": "Pikanäppäimet",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Uusi versio löytyi. Päivitä päivittääksesi.", "new_version_found": "Uusi versio löytyi. Päivitä päivittääksesi.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Välityspalvelimen tietosuojakäytäntö", "proxy_privacy_policy": "Välityspalvelimen tietosuojakäytäntö",
"reload": "Lataa uudelleen", "reload": "Lataa uudelleen",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Luo tunnus", "generate_token": "Luo tunnus",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Sisällytä URL -osoitteeseen", "include_in_url": "Sisällytä URL -osoitteeseen",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Opi kuinka", "learn": "Opi kuinka",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Salasana", "password": "Salasana",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Token", "token": "Token",
"type": "Valtuustyyppi", "type": "Valtuustyyppi",
"username": "Käyttäjätunnus" "username": "Käyttäjätunnus"
@@ -148,7 +124,6 @@
"created": "Kokoelma luotu", "created": "Kokoelma luotu",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Muokkaa kokoelmaa", "edit": "Muokkaa kokoelmaa",
"import_or_create": "Import or create a collection",
"invalid_name": "Anna kokoelmalle kelvollinen nimi", "invalid_name": "Anna kokoelmalle kelvollinen nimi",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Collection name should be at least 3 characters long", "name_length_insufficient": "Collection name should be at least 3 characters long",
"new": "Uusi kokoelma", "new": "Uusi kokoelma",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Kokoelma nimetty uudelleen", "renamed": "Kokoelma nimetty uudelleen",
"request_in_use": "Request in use", "request_in_use": "Request in use",
"save_as": "Tallenna nimellä", "save_as": "Tallenna nimellä",
@@ -178,7 +151,6 @@
"remove_folder": "Haluatko varmasti poistaa tämän kansion pysyvästi?", "remove_folder": "Haluatko varmasti poistaa tämän kansion pysyvästi?",
"remove_history": "Haluatko varmasti poistaa koko historian pysyvästi?", "remove_history": "Haluatko varmasti poistaa koko historian pysyvästi?",
"remove_request": "Haluatko varmasti poistaa tämän pyynnön pysyvästi?", "remove_request": "Haluatko varmasti poistaa tämän pyynnön pysyvästi?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Haluatko varmasti poistaa tämän ryhmän?", "remove_team": "Haluatko varmasti poistaa tämän ryhmän?",
"remove_telemetry": "Haluatko varmasti poistaa telemetrian käytöstä?", "remove_telemetry": "Haluatko varmasti poistaa telemetrian käytöstä?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "Otsikko {count}", "header": "Otsikko {count}",
"message": "Viesti {count}", "message": "Viesti {count}",
@@ -238,13 +192,11 @@
"profile": "Login to view your profile", "profile": "Login to view your profile",
"protocols": "Protokollat ovat tyhjiä", "protocols": "Protokollat ovat tyhjiä",
"schema": "Muodosta yhteys GraphQL -päätepisteeseen", "schema": "Muodosta yhteys GraphQL -päätepisteeseen",
"shared_requests": "Shared requests are empty", "shortcodes": "Shortcodes are empty",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Joukkueen nimi tyhjä", "team_name": "Joukkueen nimi tyhjä",
"teams": "Joukkueet ovat tyhjiä", "teams": "Joukkueet ovat tyhjiä",
"tests": "Tätä pyyntöä ei ole testattu", "tests": "Tätä pyyntöä ei ole testattu"
"shortcodes": "Shortcodes are empty"
}, },
"environment": { "environment": {
"add_to_global": "Add to Global", "add_to_global": "Add to Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Anna ympäristölle kelvollinen nimi", "invalid_name": "Anna ympäristölle kelvollinen nimi",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Muuttujien luettelo" "variable_list": "Muuttujien luettelo"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Tämä selain ei näytä tukevan palvelimen lähettämiä tapahtumia.", "browser_support_sse": "Tämä selain ei näytä tukevan palvelimen lähettämiä tapahtumia.",
"check_console_details": "Katso lisätietoja konsolilokista.", "check_console_details": "Katso lisätietoja konsolilokista.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL ei ole alustettu oikein", "curl_invalid_format": "cURL ei ole alustettu oikein",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Virheellistä runkoa ei voitu määrittää, ratkaista json -syntaksivirheitä ja yrittää uudelleen", "json_prettify_invalid_body": "Virheellistä runkoa ei voitu määrittää, ratkaista json -syntaksivirheitä ja yrittää uudelleen",
"network_error": "There seems to be a network error. Please try again.", "network_error": "There seems to be a network error. Please try again.",
"network_fail": "Pyyntöä ei voitu lähettää", "network_fail": "Pyyntöä ei voitu lähettää",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Ei kestoa", "no_duration": "Ei kestoa",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "No matches found", "no_results_found": "No matches found",
"page_not_found": "This page could not be found", "page_not_found": "This page could not be found",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Ennakkopyyntöskriptiä ei voitu suorittaa", "script_fail": "Ennakkopyyntöskriptiä ei voitu suorittaa",
"something_went_wrong": "Jotain meni pieleen", "something_went_wrong": "Jotain meni pieleen",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Vie JSON -muodossa", "as_json": "Vie JSON -muodossa",
"create_secret_gist": "Luo salainen ydin", "create_secret_gist": "Luo salainen ydin",
"failed": "Something went wrong while exporting",
"gist_created": "Gist luotu", "gist_created": "Gist luotu",
"require_github": "Kirjaudu sisään GitHubilla luodaksesi salaisen sisällön", "require_github": "Kirjaudu sisään GitHubilla luodaksesi salaisen sisällön",
"title": "Export" "title": "Export"
@@ -341,9 +286,6 @@
"subscriptions": "Tilaukset", "subscriptions": "Tilaukset",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "Valtuutusotsikko luodaan automaattisesti, kun lähetät pyynnön.", "authorization": "Valtuutusotsikko luodaan automaattisesti, kun lähetät pyynnön.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Luo asiakirjat ensin", "generate_documentation_first": "Luo asiakirjat ensin",
"network_fail": "Sovellusliittymän päätepistettä ei voi saavuttaa. Tarkista verkkoyhteys ja yritä uudelleen.", "network_fail": "Sovellusliittymän päätepistettä ei voi saavuttaa. Tarkista verkkoyhteys ja yritä uudelleen.",
"offline": "Näytät olevan offline -tilassa. Tämän työtilan tiedot eivät ehkä ole ajan tasalla.", "offline": "Näytät olevan offline -tilassa. Tämän työtilan tiedot eivät ehkä ole ajan tasalla.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Tuo kokoelmia", "collections": "Tuo kokoelmia",
"curl": "Tuo cURL", "curl": "Tuo cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Tuonti epäonnistui", "failed": "Tuonti epäonnistui",
"from_file": "Import from File",
"from_gist": "Tuo Gististä", "from_gist": "Tuo Gististä",
"from_gist_description": "Import from Gist URL", "from_gist_description": "Import from Gist URL",
"from_insomnia": "Import from Insomnia", "from_insomnia": "Import from Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import from Postman collection", "from_postman_description": "Import from Postman collection",
"from_url": "Import from URL", "from_url": "Import from URL",
"gist_url": "Anna Gist URL", "gist_url": "Anna Gist URL",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Couldn't get data from the url", "import_from_url_invalid_fetch": "Couldn't get data from the url",
"import_from_url_invalid_file_format": "Error while importing collections", "import_from_url_invalid_file_format": "Error while importing collections",
"import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'", "import_from_url_invalid_type": "Unsupported type. accepted values are 'hoppscotch', 'openapi', 'postman', 'insomnia'",
"import_from_url_success": "Collections Imported", "import_from_url_success": "Collections Imported",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Import collections from a Hoppscotch Collections JSON file", "json_description": "Import collections from a Hoppscotch Collections JSON file",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Tuonti" "title": "Tuonti"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Kokoelmat", "collections": "Kokoelmat",
"confirm": "Vahvistaa", "confirm": "Vahvistaa",
"customize_request": "Customize Request",
"edit_request": "Muokkaa pyyntöä", "edit_request": "Muokkaa pyyntöä",
"import_export": "Tuonti ja vienti", "import_export": "Tuonti ja vienti"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structured", "structured": "Structured",
"text": "Text" "text": "Text"
}, },
"copy_link": "Kopioi linkki",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Kesto", "duration": "Kesto",
"enter_curl": "Kirjoita cURL", "enter_curl": "Kirjoita cURL",
"generate_code": "Luo koodi", "generate_code": "Luo koodi",
"generated_code": "Luotu koodi", "generated_code": "Luotu koodi",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Otsikkoluettelo", "header_list": "Otsikkoluettelo",
"invalid_name": "Anna pyynnölle nimi", "invalid_name": "Anna pyynnölle nimi",
"method": "Menetelmä", "method": "Menetelmä",
@@ -552,14 +480,12 @@
"saved": "Pyyntö tallennettu", "saved": "Pyyntö tallennettu",
"share": "Jaa", "share": "Jaa",
"share_description": "Share Hoppscotch with your friends", "share_description": "Share Hoppscotch with your friends",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Pyyntö", "title": "Pyyntö",
"type": "Pyynnön tyyppi", "type": "Pyynnön tyyppi",
"url": "URL -osoite", "url": "URL -osoite",
"variables": "Muuttujat", "variables": "Muuttujat",
"view_my_links": "View my links", "view_my_links": "View my links"
"copy_link": "Kopioi linkki"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Muokkaa tilisi asetuksia.", "account_description": "Muokkaa tilisi asetuksia.",
"account_email_description": "Ensisijainen sähköpostiosoitteesi.", "account_email_description": "Ensisijainen sähköpostiosoitteesi.",
"account_name_description": "Tämä on näyttönimesi.", "account_name_description": "Tämä on näyttönimesi.",
"additional": "Additional Settings",
"background": "Tausta", "background": "Tausta",
"black_mode": "Musta", "black_mode": "Musta",
"choose_language": "Valitse kieli", "choose_language": "Valitse kieli",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Verify email" "verify_email": "Verify email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Created on",
"copy_html": "Copy HTML", "deleted": "Shortcode deleted",
"copy_link": "Copy Link", "method": "Method",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode not found",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Kopioi pyyntölinkki",
"delete_method": "Valitse POISTA menetelmä", "delete_method": "Valitse POISTA menetelmä",
"get_method": "Valitse GET -menetelmä", "get_method": "Valitse GET -menetelmä",
"head_method": "Valitse HEAD -menetelmä", "head_method": "Valitse HEAD -menetelmä",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Tallenna kokoelmiin", "save_to_collections": "Tallenna kokoelmiin",
"send_request": "Lähetä pyyntö", "send_request": "Lähetä pyyntö",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Pyyntö", "title": "Pyyntö"
"copy_request_link": "Kopioi pyyntölinkki"
}, },
"response": { "response": {
"copy": "Copy response to clipboard", "copy": "Copy response to clipboard",
@@ -828,7 +735,6 @@
"connection_error": "Failed to connect", "connection_error": "Failed to connect",
"connection_failed": "Connection failed", "connection_failed": "Connection failed",
"connection_lost": "Connection lost", "connection_lost": "Connection lost",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Kopioitu leikepöydälle", "copied_to_clipboard": "Kopioitu leikepöydälle",
"deleted": "Poistettu", "deleted": "Poistettu",
"deprecated": "POISTETTU", "deprecated": "POISTETTU",
@@ -836,12 +742,10 @@
"disconnected": "Yhteys katkaistu", "disconnected": "Yhteys katkaistu",
"disconnected_from": "Yhteys katkaistu: {name}", "disconnected_from": "Yhteys katkaistu: {name}",
"docs_generated": "Dokumentaatio luotu", "docs_generated": "Dokumentaatio luotu",
"download_failed": "Download failed",
"download_started": "Lataus aloitettu", "download_started": "Lataus aloitettu",
"enabled": "Käytössä", "enabled": "Käytössä",
"file_imported": "Tiedosto tuotu", "file_imported": "Tiedosto tuotu",
"finished_in": "Valmis {duration} ms", "finished_in": "Valmis {duration} ms",
"hide": "Hide",
"history_deleted": "Historia poistettu", "history_deleted": "Historia poistettu",
"linewrap": "Kääri linjat", "linewrap": "Kääri linjat",
"loading": "Ladataan...", "loading": "Ladataan...",
@@ -852,7 +756,6 @@
"published_error": "Something went wrong while publishing msg: {topic} to topic: {message}", "published_error": "Something went wrong while publishing msg: {topic} to topic: {message}",
"published_message": "Published message: {message} to topic: {topic}", "published_message": "Published message: {message} to topic: {topic}",
"reconnection_error": "Failed to reconnect", "reconnection_error": "Failed to reconnect",
"show": "Show",
"subscribed_failed": "Failed to subscribe to topic: {topic}", "subscribed_failed": "Failed to subscribe to topic: {topic}",
"subscribed_success": "Successfully subscribed to topic: {topic}", "subscribed_success": "Successfully subscribed to topic: {topic}",
"unsubscribed_failed": "Failed to unsubscribe from topic: {topic}", "unsubscribed_failed": "Failed to unsubscribe from topic: {topic}",
@@ -888,7 +791,6 @@
"queries": "Kyselyt", "queries": "Kyselyt",
"query": "Kysely", "query": "Kysely",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Pistorasia.IO", "socketio": "Pistorasia.IO",
"sse": "SSE", "sse": "SSE",
"tests": "Testit", "tests": "Testit",
@@ -905,7 +807,6 @@
"email_do_not_match": "Email doesn't match with your account details. Contact your team owner.", "email_do_not_match": "Email doesn't match with your account details. Contact your team owner.",
"exit": "Poistu tiimistä", "exit": "Poistu tiimistä",
"exit_disabled": "Vain omistaja ei voi poistua tiimistä", "exit_disabled": "Vain omistaja ei voi poistua tiimistä",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Sähköpostin muoto on virheellinen", "invalid_email_format": "Sähköpostin muoto on virheellinen",
"invalid_id": "Invalid team ID. Contact your team owner.", "invalid_id": "Invalid team ID. Contact your team owner.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Joukkue tallennettu", "saved": "Joukkue tallennettu",
"select_a_team": "Select a team", "select_a_team": "Select a team",
"success_invites": "Success invites",
"title": "Joukkueet", "title": "Joukkueet",
"we_sent_invite_link": "We sent an invite link to all invitees!", "we_sent_invite_link": "We sent an invite link to all invitees!",
"we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team." "we_sent_invite_link_description": "Ask all invitees to check their inbox. Click on the link to join the team."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Created on",
"deleted": "Shortcode deleted",
"method": "Method",
"not_found": "Shortcode not found",
"short_code": "Short code",
"url": "URL"
} }
} }

View File

@@ -1,6 +1,5 @@
{ {
"action": { "action": {
"add": "Add",
"autoscroll": "Autoscroll", "autoscroll": "Autoscroll",
"cancel": "Annuler", "cancel": "Annuler",
"choose_file": "Choisir un fichier", "choose_file": "Choisir un fichier",
@@ -11,7 +10,6 @@
"connect": "Connecter", "connect": "Connecter",
"connecting": "Connecting", "connecting": "Connecting",
"copy": "Copier", "copy": "Copier",
"create": "Create",
"delete": "Supprimer", "delete": "Supprimer",
"disconnect": "Déconnecter", "disconnect": "Déconnecter",
"dismiss": "Rejeter", "dismiss": "Rejeter",
@@ -33,7 +31,6 @@
"open_workspace": "Ouvrir un espace de travail", "open_workspace": "Ouvrir un espace de travail",
"paste": "Coller", "paste": "Coller",
"prettify": "Formater", "prettify": "Formater",
"properties": "Properties",
"remove": "Supprimer", "remove": "Supprimer",
"rename": "Rename", "rename": "Rename",
"restore": "Restaurer", "restore": "Restaurer",
@@ -42,7 +39,6 @@
"scroll_to_top": "Scroll to top", "scroll_to_top": "Scroll to top",
"search": "Chercher", "search": "Chercher",
"send": "Envoyer", "send": "Envoyer",
"share": "Share",
"start": "Démarrer", "start": "Démarrer",
"starting": "Starting", "starting": "Starting",
"stop": "Arrêter", "stop": "Arrêter",
@@ -61,9 +57,7 @@
"app": { "app": {
"chat_with_us": "Discuter avec nous", "chat_with_us": "Discuter avec nous",
"contact_us": "Nous contacter", "contact_us": "Nous contacter",
"cookies": "Cookies",
"copy": "Copier", "copy": "Copier",
"copy_interface_type": "Copy interface type",
"copy_user_id": "Copier le jeton d'authentification de l'utilisateur", "copy_user_id": "Copier le jeton d'authentification de l'utilisateur",
"developer_option": "Options pour les développeurs", "developer_option": "Options pour les développeurs",
"developer_option_description": "Des outils pour les développeurs qui aident au développement et à la maintenance de Hoppscotch.", "developer_option_description": "Des outils pour les développeurs qui aident au développement et à la maintenance de Hoppscotch.",
@@ -79,7 +73,6 @@
"keyboard_shortcuts": "Raccourcis clavier", "keyboard_shortcuts": "Raccourcis clavier",
"name": "Hoppscotch", "name": "Hoppscotch",
"new_version_found": "Nouvelle version trouvée. Actualiser pour mettre à jour.", "new_version_found": "Nouvelle version trouvée. Actualiser pour mettre à jour.",
"open_in_hoppscotch": "Open in Hoppscotch",
"options": "Options", "options": "Options",
"proxy_privacy_policy": "Politique de confidentialité du proxy", "proxy_privacy_policy": "Politique de confidentialité du proxy",
"reload": "Recharger", "reload": "Recharger",
@@ -119,27 +112,10 @@
}, },
"authorization": { "authorization": {
"generate_token": "Générer un jeton", "generate_token": "Générer un jeton",
"graphql_headers": "Authorization Headers are sent as part of the payload to connection_init",
"include_in_url": "Inclure dans l'URL", "include_in_url": "Inclure dans l'URL",
"inherited_from": "Inherited from {auth} from Parent Collection {collection} ",
"learn": "Apprendre comment", "learn": "Apprendre comment",
"oauth": {
"redirect_auth_server_returned_error": "Auth Server returned an error state",
"redirect_auth_token_request_failed": "Request to get the auth token failed",
"redirect_auth_token_request_invalid_response": "Invalid Response from the Token Endpoint when requesting for an auth token",
"redirect_invalid_state": "Invalid State value present in the redirect",
"redirect_no_auth_code": "No Authorization Code present in the redirect",
"redirect_no_client_id": "No Client ID defined",
"redirect_no_client_secret": "No Client Secret Defined",
"redirect_no_code_verifier": "No Code Verifier Defined",
"redirect_no_token_endpoint": "No Token Endpoint Defined",
"something_went_wrong_on_oauth_redirect": "Something went wrong during OAuth Redirect",
"something_went_wrong_on_token_generation": "Something went wrong on token generation",
"token_generation_oidc_discovery_failed": "Failure on token generation: OpenID Connect Discovery Failed"
},
"pass_key_by": "Pass by", "pass_key_by": "Pass by",
"password": "Mot de passe", "password": "Mot de passe",
"save_to_inherit": "Please save this request in any collection to inherit the authorization",
"token": "Jeton", "token": "Jeton",
"type": "Type d'autorisation", "type": "Type d'autorisation",
"username": "Nom d'utilisateur" "username": "Nom d'utilisateur"
@@ -148,7 +124,6 @@
"created": "Collection créée", "created": "Collection créée",
"different_parent": "Cannot reorder collection with different parent", "different_parent": "Cannot reorder collection with different parent",
"edit": "Modifier la collection", "edit": "Modifier la collection",
"import_or_create": "Import or create a collection",
"invalid_name": "Veuillez fournir un nom valide pour la collection", "invalid_name": "Veuillez fournir un nom valide pour la collection",
"invalid_root_move": "Collection already in the root", "invalid_root_move": "Collection already in the root",
"moved": "Moved Successfully", "moved": "Moved Successfully",
@@ -157,8 +132,6 @@
"name_length_insufficient": "Le nom de la collection doit comporter au moins 3 caractères", "name_length_insufficient": "Le nom de la collection doit comporter au moins 3 caractères",
"new": "Nouvelle collection", "new": "Nouvelle collection",
"order_changed": "Collection Order Updated", "order_changed": "Collection Order Updated",
"properties": "Collection Properties",
"properties_updated": "Collection Properties Updated",
"renamed": "Collection renommée", "renamed": "Collection renommée",
"request_in_use": "Demande en cours d'utilisation", "request_in_use": "Demande en cours d'utilisation",
"save_as": "Enregistrer sous", "save_as": "Enregistrer sous",
@@ -178,7 +151,6 @@
"remove_folder": "Voulez-vous vraiment supprimer définitivement ce dossier ?", "remove_folder": "Voulez-vous vraiment supprimer définitivement ce dossier ?",
"remove_history": "Voulez-vous vraiment supprimer définitivement tout l'historique ?", "remove_history": "Voulez-vous vraiment supprimer définitivement tout l'historique ?",
"remove_request": "Voulez-vous vraiment supprimer définitivement cette requête ?", "remove_request": "Voulez-vous vraiment supprimer définitivement cette requête ?",
"remove_shared_request": "Are you sure you want to permanently delete this shared request?",
"remove_team": "Voulez-vous vraiment supprimer cette équipe ?", "remove_team": "Voulez-vous vraiment supprimer cette équipe ?",
"remove_telemetry": "Êtes-vous sûr de vouloir désactiver la télémétrie ?", "remove_telemetry": "Êtes-vous sûr de vouloir désactiver la télémétrie ?",
"request_change": "Are you sure you want to discard current request, unsaved changes will be lost.", "request_change": "Are you sure you want to discard current request, unsaved changes will be lost.",
@@ -190,24 +162,6 @@
"open_request_in_new_tab": "Open request in new tab", "open_request_in_new_tab": "Open request in new tab",
"set_environment_variable": "Set as variable" "set_environment_variable": "Set as variable"
}, },
"cookies": {
"modal": {
"cookie_expires": "Expires",
"cookie_name": "Name",
"cookie_path": "Path",
"cookie_string": "Cookie string",
"cookie_value": "Value",
"empty_domain": "Domain is empty",
"empty_domains": "Domain list is empty",
"enter_cookie_string": "Enter cookie string",
"interceptor_no_support": "Your currently selected interceptor does not support cookies. Select a different Interceptor and try again.",
"managed_tab": "Managed",
"new_domain_name": "New domain name",
"no_cookies_in_domain": "No cookies set for this domain",
"raw_tab": "Raw",
"set": "Set a cookie"
}
},
"count": { "count": {
"header": "En-tête {count}", "header": "En-tête {count}",
"message": "Message {compte}", "message": "Message {compte}",
@@ -238,13 +192,11 @@
"profile": "Connectez-vous pour voir votre profil", "profile": "Connectez-vous pour voir votre profil",
"protocols": "Les protocoles sont vides", "protocols": "Les protocoles sont vides",
"schema": "Se connecter à un point de terminaison GraphQL", "schema": "Se connecter à un point de terminaison GraphQL",
"shared_requests": "Shared requests are empty", "shortcodes": "Les shortcodes sont vides",
"shared_requests_logout": "Login to view your shared requests or create a new one",
"subscription": "Subscriptions are empty", "subscription": "Subscriptions are empty",
"team_name": "Nom de l'équipe vide", "team_name": "Nom de l'équipe vide",
"teams": "Les équipes sont vides", "teams": "Les équipes sont vides",
"tests": "Il n'y a pas de tests pour cette requête", "tests": "Il n'y a pas de tests pour cette requête"
"shortcodes": "Les shortcodes sont vides"
}, },
"environment": { "environment": {
"add_to_global": "Ajouter dans Global", "add_to_global": "Ajouter dans Global",
@@ -257,7 +209,6 @@
"empty_variables": "No variables", "empty_variables": "No variables",
"global": "Global", "global": "Global",
"global_variables": "Global variables", "global_variables": "Global variables",
"import_or_create": "Import or create a environment",
"invalid_name": "Veuillez fournir un nom valide pour l'environnement", "invalid_name": "Veuillez fournir un nom valide pour l'environnement",
"list": "Environment variables", "list": "Environment variables",
"my_environments": "My Environments", "my_environments": "My Environments",
@@ -281,10 +232,8 @@
"variable_list": "Liste des variables" "variable_list": "Liste des variables"
}, },
"error": { "error": {
"authproviders_load_error": "Unable to load auth providers",
"browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.", "browser_support_sse": "Ce navigateur ne semble pas prendre en charge les événements envoyés par le serveur.",
"check_console_details": "Consultez le journal de la console pour plus de détails.", "check_console_details": "Consultez le journal de la console pour plus de détails.",
"check_how_to_add_origin": "Check how you can add an origin",
"curl_invalid_format": "cURL n'est pas formaté correctement", "curl_invalid_format": "cURL n'est pas formaté correctement",
"danger_zone": "Danger zone", "danger_zone": "Danger zone",
"delete_account": "Your account is currently an owner in these teams:", "delete_account": "Your account is currently an owner in these teams:",
@@ -300,12 +249,9 @@
"json_prettify_invalid_body": "Impossible de formater un corps non valide, résolvez les erreurs de syntaxe json et réessayez", "json_prettify_invalid_body": "Impossible de formater un corps non valide, résolvez les erreurs de syntaxe json et réessayez",
"network_error": "Il semble y avoir une erreur de réseau. Veuillez réessayer.", "network_error": "Il semble y avoir une erreur de réseau. Veuillez réessayer.",
"network_fail": "Impossible d'envoyer la requête", "network_fail": "Impossible d'envoyer la requête",
"no_collections_to_export": "No collections to export. Please create a collection to get started.",
"no_duration": "Pas de durée", "no_duration": "Pas de durée",
"no_environments_to_export": "No environments to export. Please create an environment to get started.",
"no_results_found": "Aucune correspondance trouvée", "no_results_found": "Aucune correspondance trouvée",
"page_not_found": "Cette page n'a pas pu être trouvée", "page_not_found": "Cette page n'a pas pu être trouvée",
"please_install_extension": "Please install the extension and add origin to the extension.",
"proxy_error": "Proxy error", "proxy_error": "Proxy error",
"script_fail": "Impossible d'exécuter le script de pré-requête", "script_fail": "Impossible d'exécuter le script de pré-requête",
"something_went_wrong": "Quelque chose s'est mal passé", "something_went_wrong": "Quelque chose s'est mal passé",
@@ -314,7 +260,6 @@
"export": { "export": {
"as_json": "Exporter au format JSON", "as_json": "Exporter au format JSON",
"create_secret_gist": "Créer un Gist secret", "create_secret_gist": "Créer un Gist secret",
"failed": "Something went wrong while exporting",
"gist_created": "Gist créé", "gist_created": "Gist créé",
"require_github": "Connectez-vous avec GitHub pour créer un Gist secret", "require_github": "Connectez-vous avec GitHub pour créer un Gist secret",
"title": "Exportation" "title": "Exportation"
@@ -341,9 +286,6 @@
"subscriptions": "Abonnements", "subscriptions": "Abonnements",
"switch_connection": "Switch connection" "switch_connection": "Switch connection"
}, },
"graphql_collections": {
"title": "GraphQL Collections"
},
"group": { "group": {
"time": "Time", "time": "Time",
"url": "URL" "url": "URL"
@@ -355,8 +297,6 @@
}, },
"helpers": { "helpers": {
"authorization": "L'en-tête d'autorisation sera généré automatiquement lors de l'envoi de la requête.", "authorization": "L'en-tête d'autorisation sera généré automatiquement lors de l'envoi de la requête.",
"collection_properties_authorization": " This authorization will be set for every request in this collection.",
"collection_properties_header": "This header will be set for every request in this collection.",
"generate_documentation_first": "Générez d'abord la documentation", "generate_documentation_first": "Générez d'abord la documentation",
"network_fail": "Impossible d'atteindre le point de terminaison de l'API. Vérifiez votre connexion réseau et réessayez.", "network_fail": "Impossible d'atteindre le point de terminaison de l'API. Vérifiez votre connexion réseau et réessayez.",
"offline": "Vous semblez être hors ligne. Les données de cet espace de travail peuvent ne pas être à jour.", "offline": "Vous semblez être hors ligne. Les données de cet espace de travail peuvent ne pas être à jour.",
@@ -376,10 +316,7 @@
"import": { "import": {
"collections": "Importer des collections", "collections": "Importer des collections",
"curl": "Importer en cURL", "curl": "Importer en cURL",
"environments_from_gist": "Import From Gist",
"environments_from_gist_description": "Import Hoppscotch Environments From Gist",
"failed": "Échec de l'importation", "failed": "Échec de l'importation",
"from_file": "Import from File",
"from_gist": "Importer depuis Gist", "from_gist": "Importer depuis Gist",
"from_gist_description": "Importer depuis un lien Gist", "from_gist_description": "Importer depuis un lien Gist",
"from_insomnia": "Importer depuis Insomnia", "from_insomnia": "Importer depuis Insomnia",
@@ -394,17 +331,11 @@
"from_postman_description": "Import depuis une collection Postman", "from_postman_description": "Import depuis une collection Postman",
"from_url": "Importer depuis une URL", "from_url": "Importer depuis une URL",
"gist_url": "Entrez l'URL principale", "gist_url": "Entrez l'URL principale",
"gql_collections_from_gist_description": "Import GraphQL Collections From Gist",
"hoppscotch_environment": "Hoppscotch Environment",
"hoppscotch_environment_description": "Import Hoppscotch Environment JSON file",
"import_from_url_invalid_fetch": "Impossible d'obtenir des données à partir de l'url.", "import_from_url_invalid_fetch": "Impossible d'obtenir des données à partir de l'url.",
"import_from_url_invalid_file_format": "Erreur lors de l'importation de collections", "import_from_url_invalid_file_format": "Erreur lors de l'importation de collections",
"import_from_url_invalid_type": "Type non supporté. Les valeurs acceptées sont 'hoppscotch', 'openapi', 'postman', 'insomnia'.", "import_from_url_invalid_type": "Type non supporté. Les valeurs acceptées sont 'hoppscotch', 'openapi', 'postman', 'insomnia'.",
"import_from_url_success": "Collections Importées", "import_from_url_success": "Collections Importées",
"insomnia_environment_description": "Import Insomnia Environment from a JSON/YAML file",
"json_description": "Importer des collections depuis un fichier JSON Hoppscotch", "json_description": "Importer des collections depuis un fichier JSON Hoppscotch",
"postman_environment": "Postman Environment",
"postman_environment_description": "Import Postman Environment from a JSON file",
"title": "Importer" "title": "Importer"
}, },
"inspections": { "inspections": {
@@ -442,10 +373,8 @@
"close_unsaved_tab": "You have unsaved changes", "close_unsaved_tab": "You have unsaved changes",
"collections": "Collections", "collections": "Collections",
"confirm": "Confirmer", "confirm": "Confirmer",
"customize_request": "Customize Request",
"edit_request": "Modifier la requête", "edit_request": "Modifier la requête",
"import_export": "Importer / Exporter", "import_export": "Importer / Exporter"
"share_request": "Share Request"
}, },
"mqtt": { "mqtt": {
"already_subscribed": "You are already subscribed to this topic.", "already_subscribed": "You are already subscribed to this topic.",
@@ -520,14 +449,13 @@
"structured": "Structuré", "structured": "Structuré",
"text": "Texte" "text": "Texte"
}, },
"copy_link": "Copier le lien",
"different_collection": "Cannot reorder requests from different collections", "different_collection": "Cannot reorder requests from different collections",
"duplicated": "Request duplicated", "duplicated": "Request duplicated",
"duration": "Durée", "duration": "Durée",
"enter_curl": "Entrer cURL", "enter_curl": "Entrer cURL",
"generate_code": "Générer le code", "generate_code": "Générer le code",
"generated_code": "Code généré", "generated_code": "Code généré",
"go_to_authorization_tab": "Go to Authorization tab",
"go_to_body_tab": "Go to Body tab",
"header_list": "Liste des en-têtes", "header_list": "Liste des en-têtes",
"invalid_name": "Veuillez fournir un nom pour la requête", "invalid_name": "Veuillez fournir un nom pour la requête",
"method": "Méthode", "method": "Méthode",
@@ -552,14 +480,12 @@
"saved": "Requête enregistrée", "saved": "Requête enregistrée",
"share": "Partager", "share": "Partager",
"share_description": "Partagez Hoppscotch avec vos amis", "share_description": "Partagez Hoppscotch avec vos amis",
"share_request": "Share Request",
"stop": "Stop", "stop": "Stop",
"title": "Requête", "title": "Requête",
"type": "Type de requête", "type": "Type de requête",
"url": "URL", "url": "URL",
"variables": "Variables", "variables": "Variables",
"view_my_links": "Voir mes liens", "view_my_links": "Voir mes liens"
"copy_link": "Copier le lien"
}, },
"response": { "response": {
"audio": "Audio", "audio": "Audio",
@@ -587,7 +513,6 @@
"account_description": "Personnalisez les paramètres de votre compte.", "account_description": "Personnalisez les paramètres de votre compte.",
"account_email_description": "Votre adresse e-mail principale.", "account_email_description": "Votre adresse e-mail principale.",
"account_name_description": "Ceci est votre nom d'affichage.", "account_name_description": "Ceci est votre nom d'affichage.",
"additional": "Additional Settings",
"background": "Fond", "background": "Fond",
"black_mode": "Noir", "black_mode": "Noir",
"choose_language": "Choisissez la langue", "choose_language": "Choisissez la langue",
@@ -634,31 +559,14 @@
"verified_email": "Verified email", "verified_email": "Verified email",
"verify_email": "Vérifier l'email" "verify_email": "Vérifier l'email"
}, },
"shared_requests": { "shortcodes": {
"button": "Button", "actions": "Actions",
"button_info": "Create a 'Run in Hoppscotch' button for your website, blog or a README.", "created_on": "Créé le",
"copy_html": "Copy HTML", "deleted": "Shortcode supprimé",
"copy_link": "Copy Link", "method": "Méthode",
"copy_markdown": "Copy Markdown", "not_found": "Shortcode non trouvé",
"creating_widget": "Creating widget", "short_code": "Short code",
"customize": "Customize", "url": "URL"
"deleted": "Shared request deleted",
"description": "Select a widget, you can change and customize this later",
"embed": "Embed",
"embed_info": "Add a mini 'Hoppscotch API Playground' to your website, blog or documentation.",
"link": "Link",
"link_info": "Create a shareable link to share with anyone on the internet with view access.",
"modified": "Shared request modified",
"not_found": "Shared request not found",
"open_new_tab": "Open in new tab",
"preview": "Preview",
"run_in_hoppscotch": "Run in Hoppscotch",
"theme": {
"dark": "Dark",
"light": "Light",
"system": "System",
"title": "Theme"
}
}, },
"shortcut": { "shortcut": {
"general": { "general": {
@@ -688,6 +596,7 @@
"title": "Others" "title": "Others"
}, },
"request": { "request": {
"copy_request_link": "Copier le lien de requête",
"delete_method": "Sélectionnez la méthode DELETE", "delete_method": "Sélectionnez la méthode DELETE",
"get_method": "Sélectionnez la méthode GET", "get_method": "Sélectionnez la méthode GET",
"head_method": "Sélectionnez la méthode HEAD", "head_method": "Sélectionnez la méthode HEAD",
@@ -702,10 +611,8 @@
"save_request": "Save Request", "save_request": "Save Request",
"save_to_collections": "Enregistrer dans les collections", "save_to_collections": "Enregistrer dans les collections",
"send_request": "Envoyer la requête", "send_request": "Envoyer la requête",
"share_request": "Share Request",
"show_code": "Generate code snippet", "show_code": "Generate code snippet",
"title": "Requête", "title": "Requête"
"copy_request_link": "Copier le lien de requête"
}, },
"response": { "response": {
"copy": "Copier la réponse dans le presse-papiers", "copy": "Copier la réponse dans le presse-papiers",
@@ -828,7 +735,6 @@
"connection_error": "Échec de la connexion", "connection_error": "Échec de la connexion",
"connection_failed": "La connexion a échoué", "connection_failed": "La connexion a échoué",
"connection_lost": "Connexion perdue", "connection_lost": "Connexion perdue",
"copied_interface_to_clipboard": "Copied {language} interface type to clipboard",
"copied_to_clipboard": "Copié dans le presse-papier", "copied_to_clipboard": "Copié dans le presse-papier",
"deleted": "Supprimé", "deleted": "Supprimé",
"deprecated": "DÉCONSEILLÉ", "deprecated": "DÉCONSEILLÉ",
@@ -836,12 +742,10 @@
"disconnected": "Déconnecté", "disconnected": "Déconnecté",
"disconnected_from": "Déconnecté de {name}", "disconnected_from": "Déconnecté de {name}",
"docs_generated": "Documentation générée", "docs_generated": "Documentation générée",
"download_failed": "Download failed",
"download_started": "Téléchargement commencé", "download_started": "Téléchargement commencé",
"enabled": "Active", "enabled": "Active",
"file_imported": "Fichier importé", "file_imported": "Fichier importé",
"finished_in": "Terminé en {duration} ms", "finished_in": "Terminé en {duration} ms",
"hide": "Hide",
"history_deleted": "Historique supprimé", "history_deleted": "Historique supprimé",
"linewrap": "Retour à la ligne", "linewrap": "Retour à la ligne",
"loading": "Chargement...", "loading": "Chargement...",
@@ -852,7 +756,6 @@
"published_error": "Quelque chose s'est mal passé lors de la publication du message : {topic} dans le sujet : {message}", "published_error": "Quelque chose s'est mal passé lors de la publication du message : {topic} dans le sujet : {message}",
"published_message": "Message publié : {message} au sujet : {topic}", "published_message": "Message publié : {message} au sujet : {topic}",
"reconnection_error": "Échec de la reconnexion", "reconnection_error": "Échec de la reconnexion",
"show": "Show",
"subscribed_failed": "Échec de l'inscription au sujet : {topic}", "subscribed_failed": "Échec de l'inscription au sujet : {topic}",
"subscribed_success": "Inscription réussie au sujet : {topic}", "subscribed_success": "Inscription réussie au sujet : {topic}",
"unsubscribed_failed": "Échec de la désinscription du sujet : {topic}", "unsubscribed_failed": "Échec de la désinscription du sujet : {topic}",
@@ -888,7 +791,6 @@
"queries": "Requêtes", "queries": "Requêtes",
"query": "Requête", "query": "Requête",
"schema": "Schema", "schema": "Schema",
"shared_requests": "Shared Requests",
"socketio": "Socket.IO", "socketio": "Socket.IO",
"sse": "ESS", "sse": "ESS",
"tests": "Tests", "tests": "Tests",
@@ -905,7 +807,6 @@
"email_do_not_match": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", "email_do_not_match": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.",
"exit": "Quitter l'équipe", "exit": "Quitter l'équipe",
"exit_disabled": "Seul le propriétaire ne peut pas quitter l'équipe", "exit_disabled": "Seul le propriétaire ne peut pas quitter l'équipe",
"failed_invites": "Failed invites",
"invalid_coll_id": "Invalid collection ID", "invalid_coll_id": "Invalid collection ID",
"invalid_email_format": "Le format de l'e-mail n'est pas valide", "invalid_email_format": "Le format de l'e-mail n'est pas valide",
"invalid_id": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.", "invalid_id": "L'email ne correspond pas aux détails de votre compte. Contactez le propriétaire de votre équipe.",
@@ -947,7 +848,6 @@
"same_target_destination": "Same target and destination", "same_target_destination": "Same target and destination",
"saved": "Équipe enregistrée", "saved": "Équipe enregistrée",
"select_a_team": "Choisir une équipe", "select_a_team": "Choisir une équipe",
"success_invites": "Success invites",
"title": "Équipes", "title": "Équipes",
"we_sent_invite_link": "Nous avons envoyé un lien d'invitation à tous les invités !", "we_sent_invite_link": "Nous avons envoyé un lien d'invitation à tous les invités !",
"we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe." "we_sent_invite_link_description": "Demandez à tous les invités de vérifier leur boîte de réception. Cliquez sur le lien pour rejoindre l'équipe."
@@ -979,14 +879,5 @@
"personal": "My Workspace", "personal": "My Workspace",
"team": "Team Workspace", "team": "Team Workspace",
"title": "Workspaces" "title": "Workspaces"
},
"shortcodes": {
"actions": "Actions",
"created_on": "Créé le",
"deleted": "Shortcode supprimé",
"method": "Méthode",
"not_found": "Shortcode non trouvé",
"short_code": "Short code",
"url": "URL"
} }
} }

Some files were not shown because too many files have changed in this diff Show More