diff --git a/.drone.yml b/.drone.yml index d9b03b43..121a3d83 100644 --- a/.drone.yml +++ b/.drone.yml @@ -58,7 +58,7 @@ steps: - yarn build - yarn test - yarn workspace @snort/app eslint - - yarn workspace @snort/app prettier --check . + - yarn prettier --check . volumes: - name: cache claim: @@ -93,7 +93,7 @@ steps: - yarn install - npx @crowdin/cli upload sources -b main -T $CTOKEN - npx @crowdin/cli pull -b main -T $CTOKEN - - yarn workspace @snort/app format + - yarn prettier --write . - git add . - 'git commit -a -m "chore: Update translations"' - git push -u origin main diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 661d1a92..3de15c6a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,7 +44,77 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tagName: ${{ github.ref_name }} - releaseName: "Snort v__VERSION__" - releaseBody: "See the assets to download and install this version." - releaseDraft: true - prerelease: false + app: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v1 + with: + java-version: 17 + + - name: Cache gradle + uses: actions/cache@v1 + with: + path: ~/.gradle/caches + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Install frontend dependencies + run: yarn install + + - name: Build Site + run: yarn build + + - name: Copy files + run: |- + git clone https://git.v0l.io/Kieran/snort_android.git + mkdir -p snort_android/app/src/main/assets/ + cp packages/app/build/* snort_android/app/src/main/assets/ + + - name: Build AAB + working-directory: snort_android + run: ./gradlew clean bundleRelease --stacktrace + - name: Build APK + working-directory: snort_android + run: ./gradlew assembleRelease --stacktrace + + - name: Sign AAB + uses: r0adkll/sign-android-release@v1 + with: + releaseDirectory: snort_android/app/build/outputs/bundle/release + signingKeyBase64: ${{ secrets.SIGNING_KEY }} + alias: ${{ secrets.KEY_ALIAS }} + keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} + keyPassword: ${{ secrets.KEY_PASSWORD }} + + - name: Sign APK + uses: r0adkll/sign-android-release@v1 + with: + releaseDirectory: snort_android/app/build/outputs/apk/release + signingKeyBase64: ${{ secrets.SIGNING_KEY }} + alias: ${{ secrets.KEY_ALIAS }} + keyStorePassword: ${{ secrets.KEY_STORE_PASSWORD }} + keyPassword: ${{ secrets.KEY_PASSWORD }} + - name: Rename files + run: |- + mkdir -p snort_android/app/release + mv snort_android/app/build/outputs/bundle/release/app-release.aab snort_android/app/release/snort-${{ github.ref_name }}.aab + mv snort_android/app/build/outputs/apk/release/app-universal-release-unsigned-signed.apk snort_android/app/release/snort-universal-${{ github.ref_name }}.apk + mv snort_android/app/build/outputs/apk/release/app-arm64-v8a-release-unsigned-signed.apk snort_android/app/release/snort-arm64-v8a-${{ github.ref_name }}.apk + mv snort_android/app/build/outputs/apk/release/app-x86_64-release-unsigned-signed.apk snort_android/app/release/snort-x86_64-${{ github.ref_name }}.apk + mv snort_android/app/build/outputs/apk/release/app-armeabi-v7a-release-unsigned-signed.apk snort_android/app/release/snort-armeabi-v7a-${{ github.ref_name }}.apk + - name: Upload assets + uses: softprops/action-gh-release@v1 + with: + files: | + snort_android/app/release/snort-${{ github.ref_name }}.aab + snort_android/app/release/snort-universal-${{ github.ref_name }}.apk + snort_android/app/release/snort-arm64-v8a-${{ github.ref_name }}.apk + snort_android/app/release/snort-x86_64-${{ github.ref_name }}.apk + snort_android/app/release/snort-armeabi-v7a-${{ github.ref_name }}.apk \ No newline at end of file diff --git a/.prettierignore b/.prettierignore index bf970ce0..9b4632da 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,5 @@ build/ .vscode/ .github/ transifex.yml -dist/ \ No newline at end of file +dist/ +src-tauri/ \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8e8adf9e..daaa5ee2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "arcanis.vscode-zipfs", - "dbaeumer.vscode-eslint" + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode" ] } diff --git a/.yarn/sdks/eslint/package.json b/.yarn/sdks/eslint/package.json index 06e74e37..ff446fd6 100644 --- a/.yarn/sdks/eslint/package.json +++ b/.yarn/sdks/eslint/package.json @@ -1,6 +1,6 @@ { "name": "eslint", - "version": "8.44.0-sdk", + "version": "8.48.0-sdk", "main": "./lib/api.js", "type": "commonjs" } diff --git a/.yarn/sdks/prettier/index.js b/.yarn/sdks/prettier/index.js new file mode 100755 index 00000000..8758e367 --- /dev/null +++ b/.yarn/sdks/prettier/index.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +const {existsSync} = require(`fs`); +const {createRequire} = require(`module`); +const {resolve} = require(`path`); + +const relPnpApiPath = "../../../.pnp.cjs"; + +const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absRequire = createRequire(absPnpApiPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require prettier + require(absPnpApiPath).setup(); + } +} + +// Defer to the real prettier your application uses +module.exports = absRequire(`prettier`); diff --git a/.yarn/sdks/prettier/package.json b/.yarn/sdks/prettier/package.json new file mode 100644 index 00000000..c102fa29 --- /dev/null +++ b/.yarn/sdks/prettier/package.json @@ -0,0 +1,6 @@ +{ + "name": "prettier", + "version": "3.0.3-sdk", + "main": "./index.js", + "type": "commonjs" +} diff --git a/.yarn/sdks/typescript/package.json b/.yarn/sdks/typescript/package.json index 7503a11c..0bfa4eb2 100644 --- a/.yarn/sdks/typescript/package.json +++ b/.yarn/sdks/typescript/package.json @@ -1,6 +1,6 @@ { "name": "typescript", - "version": "5.1.6-sdk", + "version": "5.2.2-sdk", "main": "./lib/typescript.js", "type": "commonjs" } diff --git a/LICENSE b/LICENSE index f288702d..aa14a6bb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,674 +1,21 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. +MIT License + +Copyright (c) 2023 Kieran (v0l) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 662364dc..99315420 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,12 @@ Snort supports the following NIP's: - [x] NIP-09: Event Deletion - [x] NIP-10: Conventions for clients' use of `e` and `p` tags in text events - [x] NIP-11: Relay Information Document -- [x] NIP-12: Generic Tag Queries - [x] NIP-13: Proof of Work - [ ] NIP-14: Subject tag in text events -- [x] NIP-15: End of Stored Events Notice - [x] NIP-18: Reposts - [x] NIP-19: bech32-encoded entities -- [x] NIP-20: Command Results - [x] NIP-21: `nostr:` Protocol handler (`web+nostr`) +- [x] NIP-23: Long form content - [x] NIP-25: Reactions - [x] NIP-26: Delegated Event Signing (Display delegated signings only) - [x] NIP-27: Text note references @@ -30,6 +28,8 @@ Snort supports the following NIP's: - [x] NIP-30: Custom Emoji - [x] NIP-31: Alt tag for unknown events - [x] NIP-36: Sensitive Content +- [x] NIP-38: User Statuses +- [ ] NIP-39: External Identities - [ ] NIP-40: Expiration Timestamp - [x] NIP-42: Authentication of clients to relays - [x] NIP-44: Versioned encryption @@ -38,11 +38,14 @@ Snort supports the following NIP's: - [x] NIP-50: Search - [x] NIP-51: Lists - [x] NIP-53: Live Events +- [x] NIP-57: Zaps - [x] NIP-58: Badges - [x] NIP-59: Gift Wrap - [x] NIP-65: Relay List Metadata +- [x] NIP-75: Zap Goals - [ ] NIP-78: App specific data -- [x] NIP-94: File header +- [ ] NIP-89: App handlers +- [x] NIP-94: File Metadata - [x] NIP-98: HTTP Auth ### Running diff --git a/package.json b/package.json index ec436ed4..0d26662c 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,8 @@ "scripts": { "build": "yarn workspace @snort/shared build && yarn workspace @snort/system build && yarn workspace @snort/system-react build && yarn workspace @snort/app build", "start": "yarn workspace @snort/shared build && yarn workspace @snort/system build && yarn workspace @snort/system-react build && yarn workspace @snort/app start", - "test": "yarn workspace @snort/shared build && yarn workspace @snort/system build && yarn workspace @snort/app test && yarn workspace @snort/system test" - }, - "devDependencies": { - "@cloudflare/workers-types": "^4.20230307.0", - "@tauri-apps/cli": "^1.2.3", - "prettier": "^3.0.0" + "test": "yarn workspace @snort/shared build && yarn workspace @snort/system build && yarn workspace @snort/app test && yarn workspace @snort/system test", + "pre:commit": "yarn workspace @snort/app intl-extract && yarn workspace @snort/app intl-compile && yarn prettier --write ." }, "prettier": { "printWidth": 120, @@ -20,7 +16,10 @@ }, "packageManager": "yarn@3.6.3", "dependencies": { + "@cloudflare/workers-types": "^4.20230307.0", + "@tauri-apps/cli": "^1.2.3", "eslint": "^8.48.0", + "prettier": "^3.0.3", "typescript": "^5.2.2" } } diff --git a/packages/app/_headers b/packages/app/_headers index d7ff4911..5921f994 100644 --- a/packages/app/_headers +++ b/packages/app/_headers @@ -1,2 +1,2 @@ /* - Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src youtube.com www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://nostrnests.com https://embed.wavlake.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://static.cloudflareinsights.com https://platform.twitter.com https://embed.tidal.com; \ No newline at end of file + Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src youtube.com www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://nostrnests.com https://embed.wavlake.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://analytics.v0l.io https://platform.twitter.com https://embed.tidal.com; \ No newline at end of file diff --git a/packages/app/config/README.md b/packages/app/config/README.md new file mode 100644 index 00000000..b50a8b6d --- /dev/null +++ b/packages/app/config/README.md @@ -0,0 +1 @@ +Choose config with NODE_CONFIG_ENV: `NODE_CONFIG_ENV=iris yarn start` diff --git a/packages/app/config/default.json b/packages/app/config/default.json new file mode 100644 index 00000000..ea2f8f6b --- /dev/null +++ b/packages/app/config/default.json @@ -0,0 +1,7 @@ +{ + "appName": "Snort", + "appNameCapitalized": "Snort", + "appTitle": "Snort - Nostr", + "nip05Domain": "snort.social", + "favicon": "public/favicon.ico" +} diff --git a/packages/app/config/iris.json b/packages/app/config/iris.json new file mode 100644 index 00000000..f1c6ab75 --- /dev/null +++ b/packages/app/config/iris.json @@ -0,0 +1,7 @@ +{ + "appName": "iris", + "appNameCapitalized": "Iris", + "appTitle": "iris", + "nip05Domain": "iris.to", + "favicon": "public/iris.ico" +} diff --git a/packages/app/package.json b/packages/app/package.json index 060d4b19..32872e4d 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -6,14 +6,15 @@ "@lightninglabs/lnc-web": "^0.2.3-alpha", "@noble/curves": "^1.0.0", "@noble/hashes": "^1.2.0", - "@reduxjs/toolkit": "^1.9.1", "@scure/base": "^1.1.1", "@scure/bip32": "^1.3.0", "@scure/bip39": "^1.1.1", "@snort/shared": "workspace:*", "@snort/system": "workspace:*", "@snort/system-react": "workspace:*", + "@snort/system-wasm": "workspace:*", "@szhsin/react-menu": "^3.3.1", + "@types/use-sync-external-store": "^0.0.4", "@void-cat/api": "^1.0.4", "debug": "^4.3.4", "dexie": "^3.2.4", @@ -26,11 +27,11 @@ "react-dom": "^18.2.0", "react-intersection-observer": "^9.4.1", "react-intl": "^6.4.4", - "react-redux": "^8.0.5", "react-router-dom": "^6.5.0", "react-textarea-autosize": "^8.4.0", "react-twitter-embed": "^4.0.4", "use-long-press": "^2.0.3", + "use-sync-external-store": "^1.2.0", "uuid": "^9.0.0", "workbox-core": "^6.4.2", "workbox-precaching": "^7.0.0", @@ -43,9 +44,7 @@ "test": "jest --runInBand", "intl-extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang.json --flatten true", "intl-compile": "formatjs compile src/lang.json --out-file src/translations/en.json", - "format": "prettier --write .", - "eslint": "eslint .", - "prepare": "cd ../.. && husky install" + "eslint": "eslint ." }, "eslintConfig": { "extends": [ @@ -89,6 +88,7 @@ "@webpack-cli/generators": "^3.0.4", "@webscopeio/react-textarea-autocomplete": "^4.9.2", "babel-loader": "^9.1.3", + "config": "^3.3.9", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^5.0.0", @@ -102,6 +102,7 @@ "prop-types": "^15.8.1", "source-map-loader": "^4.0.1", "terser-webpack-plugin": "^5.3.9", + "tinybench": "^2.5.0", "ts-jest": "^29.1.0", "ts-loader": "^9.4.4", "typescript": "^5.2.2", @@ -110,8 +111,5 @@ "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1", "workbox-webpack-plugin": "^6.5.4" - }, - "lint-staged": { - "*.{js,jsx,ts,tsx,css,md}": "prettier --write" } } diff --git a/packages/app/public/bench.html b/packages/app/public/bench.html new file mode 100644 index 00000000..95159cd9 --- /dev/null +++ b/packages/app/public/bench.html @@ -0,0 +1,10 @@ + + + + + Snort Benchmarks + + + Check console + + diff --git a/packages/app/public/icons.svg b/packages/app/public/icons.svg index 15371630..f7e27883 100644 --- a/packages/app/public/icons.svg +++ b/packages/app/public/icons.svg @@ -176,7 +176,7 @@ - + @@ -296,6 +296,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/app/public/index.html b/packages/app/public/index.html index ee84c3b7..6f644eb7 100644 --- a/packages/app/public/index.html +++ b/packages/app/public/index.html @@ -1,4 +1,4 @@ - + @@ -11,7 +11,7 @@ - Snort - Nostr + <%= htmlWebpackPlugin.options.templateParameters.appTitle %>
diff --git a/packages/app/public/iris.ico b/packages/app/public/iris.ico new file mode 100644 index 00000000..a71cea29 Binary files /dev/null and b/packages/app/public/iris.ico differ diff --git a/packages/app/src/Cache/FollowsFeed.ts b/packages/app/src/Cache/FollowsFeed.ts new file mode 100644 index 00000000..46e44737 --- /dev/null +++ b/packages/app/src/Cache/FollowsFeed.ts @@ -0,0 +1,126 @@ +import debug from "debug"; +import { EventKind, RequestBuilder, SystemInterface, TaggedNostrEvent } from "@snort/system"; +import { unixNow, unixNowMs } from "@snort/shared"; + +import { db } from "Db"; +import { RefreshFeedCache, TWithCreated } from "./RefreshFeedCache"; +import { LoginSession } from "Login"; +import { Day, Hour } from "Const"; + +const WindowSize = Hour * 6; +const MaxCacheWindow = Day * 7; + +export class FollowsFeedCache extends RefreshFeedCache { + #kinds = [EventKind.TextNote, EventKind.Repost, EventKind.Polls]; + #oldest?: number; + + constructor() { + super("FollowsFeedCache", db.followsFeed); + } + + key(of: TWithCreated): string { + return of.id; + } + + takeSnapshot(): TWithCreated[] { + return [...this.cache.values()]; + } + + buildSub(session: LoginSession, rb: RequestBuilder): void { + const since = this.newest(); + rb.withFilter() + .kinds(this.#kinds) + .authors(session.follows.item) + .since(since === 0 ? unixNow() - WindowSize : since); + } + + async onEvent(evs: readonly TaggedNostrEvent[]): Promise { + const filtered = evs.filter(a => this.#kinds.includes(a.kind)); + if (filtered.length > 0) { + await this.bulkSet(filtered); + this.notifyChange(filtered.map(a => this.key(a))); + } + } + + override async preload() { + const start = unixNowMs(); + const keys = (await this.table?.toCollection().primaryKeys()) ?? []; + this.onTable = new Set(keys.map(a => a as string)); + + // load only latest 10 posts, rest can be loaded on-demand + const latest = await this.table?.orderBy("created_at").reverse().limit(50).toArray(); + latest?.forEach(v => this.cache.set(this.key(v), v)); + + // cleanup older than 7 days + await this.table + ?.where("created_at") + .below(unixNow() - MaxCacheWindow) + .delete(); + + const oldest = await this.table?.orderBy("created_at").first(); + this.#oldest = oldest?.created_at; + this.notifyChange(latest?.map(a => this.key(a)) ?? []); + + debug(this.name)(`Loaded %d/%d in %d ms`, latest?.length ?? 0, keys.length, (unixNowMs() - start).toLocaleString()); + } + + async loadMore(system: SystemInterface, session: LoginSession, before: number) { + if (this.#oldest && before <= this.#oldest) { + const rb = new RequestBuilder(`${this.name}-loadmore`); + rb.withFilter() + .kinds(this.#kinds) + .authors(session.follows.item) + .until(before) + .since(before - WindowSize); + await system.Fetch(rb, async evs => { + await this.bulkSet(evs); + }); + } else { + const latest = await this.table + ?.where("created_at") + .between(before - WindowSize, before) + .reverse() + .sortBy("created_at"); + latest?.forEach(v => { + const k = this.key(v); + this.cache.set(k, v); + this.onTable.add(k); + }); + + this.notifyChange(latest?.map(a => this.key(a)) ?? []); + } + } + + /** + * Backfill cache with new follows + */ + async backFill(system: SystemInterface, keys: Array) { + if (keys.length === 0) return; + + const rb = new RequestBuilder(`${this.name}-backfill`); + rb.withFilter() + .kinds(this.#kinds) + .authors(keys) + .until(unixNow()) + .since(this.#oldest ?? unixNow() - MaxCacheWindow); + await system.Fetch(rb, async evs => { + await this.bulkSet(evs); + }); + } + + /** + * Backfill cache based on follows list + */ + async backFillIfMissing(system: SystemInterface, keys: Array) { + if (!this.#oldest) return; + + const start = unixNowMs(); + const everything = await this.table?.toArray(); + if ((everything?.length ?? 0) > 0) { + const allKeys = new Set(everything?.map(a => a.pubkey)); + const missingKeys = keys.filter(a => !allKeys.has(a)); + await this.backFill(system, missingKeys); + debug(this.name)(`Backfilled %d keys in %d ms`, missingKeys.length, (unixNowMs() - start).toLocaleString()); + } + } +} diff --git a/packages/app/src/Cache/GiftWrapCache.ts b/packages/app/src/Cache/GiftWrapCache.ts index 184ad3ad..2c999ac9 100644 --- a/packages/app/src/Cache/GiftWrapCache.ts +++ b/packages/app/src/Cache/GiftWrapCache.ts @@ -1,9 +1,10 @@ -import { FeedCache } from "@snort/shared"; -import { EventKind, EventPublisher, TaggedNostrEvent } from "@snort/system"; +import { EventKind, EventPublisher, RequestBuilder, TaggedNostrEvent } from "@snort/system"; import { UnwrappedGift, db } from "Db"; import { findTag, unwrap } from "SnortUtils"; +import { RefreshFeedCache } from "./RefreshFeedCache"; +import { LoginSession, LoginSessionType } from "Login"; -export class GiftWrapCache extends FeedCache { +export class GiftWrapCache extends RefreshFeedCache { constructor() { super("GiftWrapCache", db.gifts); } @@ -12,22 +13,20 @@ export class GiftWrapCache extends FeedCache { return of.id; } - override async preload(): Promise { - await super.preload(); - await this.buffer([...this.onTable]); - } - - newest(): number { - let ret = 0; - this.cache.forEach(v => (ret = v.created_at > ret ? v.created_at : ret)); - return ret; + buildSub(session: LoginSession, rb: RequestBuilder): void { + const pubkey = session.publicKey; + if (pubkey && session.type === LoginSessionType.PrivateKey) { + rb.withFilter().kinds([EventKind.GiftWrap]).tag("p", [pubkey]).since(this.newest()); + } } takeSnapshot(): Array { return [...this.cache.values()]; } - async onEvent(evs: Array, pub: EventPublisher) { + override async onEvent(evs: Array, pub?: EventPublisher) { + if (!pub) return; + const unwrapped = ( await Promise.all( evs.map(async v => { @@ -41,7 +40,7 @@ export class GiftWrapCache extends FeedCache { } catch (e) { console.debug(e, v); } - }) + }), ) ) .filter(a => a !== undefined) diff --git a/packages/app/src/Cache/Notifications.ts b/packages/app/src/Cache/Notifications.ts index fc94bd32..6f606f19 100644 --- a/packages/app/src/Cache/Notifications.ts +++ b/packages/app/src/Cache/Notifications.ts @@ -1,8 +1,9 @@ import { EventKind, NostrEvent, RequestBuilder, TaggedNostrEvent } from "@snort/system"; import { RefreshFeedCache, TWithCreated } from "./RefreshFeedCache"; import { LoginSession } from "Login"; -import { unixNow } from "SnortUtils"; import { db } from "Db"; +import { Day } from "Const"; +import { unixNow } from "@snort/shared"; export class NotificationsCache extends RefreshFeedCache { #kinds = [EventKind.TextNote, EventKind.Reaction, EventKind.Repost, EventKind.ZapReceipt]; @@ -17,7 +18,7 @@ export class NotificationsCache extends RefreshFeedCache { rb.withFilter() .kinds(this.#kinds) .tag("p", [session.publicKey]) - .since(newest === 0 ? unixNow() - 60 * 60 * 24 * 30 : newest); + .since(newest === 0 ? unixNow() - Day * 30 : newest); } } diff --git a/packages/app/src/Cache/RefreshFeedCache.ts b/packages/app/src/Cache/RefreshFeedCache.ts index debb299d..29372ffb 100644 --- a/packages/app/src/Cache/RefreshFeedCache.ts +++ b/packages/app/src/Cache/RefreshFeedCache.ts @@ -1,12 +1,12 @@ import { FeedCache } from "@snort/shared"; -import { RequestBuilder, TaggedNostrEvent } from "@snort/system"; +import { EventPublisher, RequestBuilder, TaggedNostrEvent } from "@snort/system"; import { LoginSession } from "Login"; -export type TWithCreated = T & { created_at: number }; +export type TWithCreated = (T | Readonly) & { created_at: number }; export abstract class RefreshFeedCache extends FeedCache> { abstract buildSub(session: LoginSession, rb: RequestBuilder): void; - abstract onEvent(evs: Readonly>): void; + abstract onEvent(evs: Readonly>, pub?: EventPublisher): void; /** * Get latest event diff --git a/packages/app/src/Cache/index.ts b/packages/app/src/Cache/index.ts index 81921532..63c1a0f7 100644 --- a/packages/app/src/Cache/index.ts +++ b/packages/app/src/Cache/index.ts @@ -4,6 +4,7 @@ import { ChatCache } from "./ChatCache"; import { Payments } from "./PaymentsCache"; import { GiftWrapCache } from "./GiftWrapCache"; import { NotificationsCache } from "./Notifications"; +import { FollowsFeedCache } from "./FollowsFeed"; export const UserCache = new UserProfileCache(); export const UserRelays = new UserRelaysCache(); @@ -13,6 +14,7 @@ export const PaymentsCache = new Payments(); export const InteractionCache = new EventInteractionCache(); export const GiftsCache = new GiftWrapCache(); export const Notifications = new NotificationsCache(); +export const FollowsFeed = new FollowsFeedCache(); export async function preload(follows?: Array) { const preloads = [ @@ -23,6 +25,7 @@ export async function preload(follows?: Array) { RelayMetrics.preload(), GiftsCache.preload(), Notifications.preload(), + FollowsFeed.preload(), ]; await Promise.all(preloads); } diff --git a/packages/app/src/Const.ts b/packages/app/src/Const.ts index a412edcc..2eefd906 100644 --- a/packages/app/src/Const.ts +++ b/packages/app/src/Const.ts @@ -1,5 +1,15 @@ import { RelaySettings } from "@snort/system"; +/** + * 1 Hour in seconds + */ +export const Hour = 60 * 60; + +/** + * 1 Day in seconds + */ +export const Day = Hour * 24; + /** * Add-on api for snort features */ diff --git a/packages/app/src/Db/index.ts b/packages/app/src/Db/index.ts index 963382ff..ec85977a 100644 --- a/packages/app/src/Db/index.ts +++ b/packages/app/src/Db/index.ts @@ -1,8 +1,8 @@ import Dexie, { Table } from "dexie"; -import { HexKey, NostrEvent, u256 } from "@snort/system"; +import { HexKey, NostrEvent, TaggedNostrEvent, u256 } from "@snort/system"; export const NAME = "snortDB"; -export const VERSION = 13; +export const VERSION = 14; export interface SubCache { id: string; @@ -41,6 +41,7 @@ const STORES = { payments: "++url", gifts: "++id", notifications: "++id", + followsFeed: "++id, created_at, kind", }; export class SnortDB extends Dexie { @@ -50,6 +51,7 @@ export class SnortDB extends Dexie { payments!: Table; gifts!: Table; notifications!: Table; + followsFeed!: Table; constructor() { super(NAME); diff --git a/packages/app/src/Element/AsyncButton.tsx b/packages/app/src/Element/AsyncButton.tsx index c4118afa..93a91239 100644 --- a/packages/app/src/Element/AsyncButton.tsx +++ b/packages/app/src/Element/AsyncButton.tsx @@ -1,5 +1,5 @@ import "./AsyncButton.css"; -import { useState } from "react"; +import React, { useState, ForwardedRef } from "react"; import Spinner from "../Icons/Spinner"; interface AsyncButtonProps extends React.ButtonHTMLAttributes { @@ -8,7 +8,7 @@ interface AsyncButtonProps extends React.ButtonHTMLAttributes children?: React.ReactNode; } -export default function AsyncButton(props: AsyncButtonProps) { +const AsyncButton = React.forwardRef((props, ref) => { const [loading, setLoading] = useState(false); async function handle(e: React.MouseEvent) { @@ -28,7 +28,13 @@ export default function AsyncButton(props: AsyncButtonProps) { } return ( - ); -} +}); + +export default AsyncButton; diff --git a/packages/app/src/Element/Avatar.css b/packages/app/src/Element/Avatar.css deleted file mode 100644 index 178f52d4..00000000 --- a/packages/app/src/Element/Avatar.css +++ /dev/null @@ -1,20 +0,0 @@ -.avatar { - border-radius: 50%; - height: 210px; - width: 210px; - background-image: var(--img-url); - border: 1px solid transparent; - background-origin: border-box; - background-clip: content-box, border-box; - background-size: cover; - box-sizing: border-box; - background-color: var(--gray); -} - -.avatar[data-domain="snort.social"] { - background-image: var(--img-url), var(--snort-gradient); -} - -.avatar[data-domain="strike.army"] { - background-image: var(--img-url), var(--strike-army-gradient); -} diff --git a/packages/app/src/Element/BackButton.css b/packages/app/src/Element/BackButton.css index 99b93224..0b631f71 100644 --- a/packages/app/src/Element/BackButton.css +++ b/packages/app/src/Element/BackButton.css @@ -6,18 +6,17 @@ font-size: var(--font-size); display: flex; align-items: center; + border: none !important; + box-shadow: none !important; } .back-button svg { margin-right: 0.5em; } -.back-button:hover { - text-decoration: underline; -} - -.back-button:hover { - background: none; - color: var(--font-color); +.back-button:hover:hover, +.light .back-button:hover { text-decoration: underline; + box-shadow: none !important; + background: none !important; } diff --git a/packages/app/src/Element/Bookmarks.tsx b/packages/app/src/Element/Bookmarks.tsx index f41511ca..e900f3ab 100644 --- a/packages/app/src/Element/Bookmarks.tsx +++ b/packages/app/src/Element/Bookmarks.tsx @@ -1,8 +1,8 @@ import { useState, useMemo, ChangeEvent } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { HexKey, TaggedNostrEvent } from "@snort/system"; -import Note from "Element/Note"; +import Note from "Element/Event/Note"; import useLogin from "Hooks/useLogin"; import { UserCache } from "Cache"; @@ -16,7 +16,7 @@ interface BookmarksProps { const Bookmarks = ({ pubkey, bookmarks, related }: BookmarksProps) => { const [onlyPubkey, setOnlyPubkey] = useState("all"); - const loginPubKey = useLogin().publicKey; + const { publicKey } = useLogin(s => ({ publicKey: s.publicKey })); const ps = useMemo(() => { return [...new Set(bookmarks.map(ev => ev.pubkey))]; }, [bookmarks]); @@ -28,7 +28,7 @@ const Bookmarks = ({ pubkey, bookmarks, related }: BookmarksProps) => { return (
-
+
changePollOption(i, e.target.value)} /> + {i > 1 && ( + + )} +
+
+ ))} + + + ); + } + } + + function changePollOption(i: number, v: string) { + if (note.pollOptions) { + const copy = [...note.pollOptions]; + copy[i] = v; + note.update(v => (v.pollOptions = copy)); + } + } + + function removePollOption(i: number) { + if (note.pollOptions) { + const copy = [...note.pollOptions]; + copy.splice(i, 1); + note.update(v => (v.pollOptions = copy)); + } + } + + function renderRelayCustomisation() { + return ( +
+ {Object.keys(relays.item || {}) + .filter(el => relays.item[el].write) + .map((r, i, a) => ( +
+
{r}
+
+ { + note.update( + v => + (v.selectedCustomRelays = + // set false if all relays selected + e.target.checked && + note.selectedCustomRelays && + note.selectedCustomRelays.length == a.length - 1 + ? undefined + : // otherwise return selectedCustomRelays with target relay added / removed + a.filter(el => + el === r + ? e.target.checked + : !note.selectedCustomRelays || note.selectedCustomRelays.includes(el), + )), + ); + }} + /> +
+
+ ))} +
+ ); + } + + /*function listAccounts() { + return LoginStore.getSessions().map(a => ( + { + ev.stopPropagation = true; + LoginStore.switchAccount(a); + }}> + + + )); + }*/ + + const handlePaste: ClipboardEventHandler = evt => { + if (evt.clipboardData) { + const clipboardItems = evt.clipboardData.items; + const items: DataTransferItem[] = Array.from(clipboardItems).filter(function (item: DataTransferItem) { + // Filter the image items only + return /^image\//.test(item.type); + }); + if (items.length === 0) { + return; + } + + const item = items[0]; + const blob = item.getAsFile(); + if (blob) { + uploadFile(blob); + } + } + }; + + if (!note.show) return null; + return ( + note.update(v => (v.show = false))}> + {note.replyTo && ( + + )} + {note.preview && getPreviewNote()} + {!note.preview && ( +
+ +

- setWebsite(e.target.value)} /> + setWebsite(e.target.value)} + disabled={readonly} + />

- setNip05(e.target.value)} /> + setNip05(e.target.value)} + disabled={readonly} + /> @@ -150,9 +172,15 @@ export default function ProfileSettings(props: ProfileSettingsProps) {

- setLud16(e.target.value)} /> + setLud16(e.target.value)} + disabled={readonly} + />
- saveProfile()}> + saveProfile()} disabled={readonly}>
@@ -170,7 +198,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) { background: (banner?.length ?? 0) > 0 ? `no-repeat center/cover url("${banner}")` : undefined, }} className="banner"> - setNewBanner()}> + setNewBanner()} disabled={readonly}>
@@ -178,7 +206,7 @@ export default function ProfileSettings(props: ProfileSettingsProps) { {(props.avatar ?? true) && (
- setNewAvatar()}> + setNewAvatar()} disabled={readonly}>
diff --git a/packages/app/src/Pages/settings/RelayInfo.tsx b/packages/app/src/Pages/settings/RelayInfo.tsx index 6c5bca22..196dc47f 100644 --- a/packages/app/src/Pages/settings/RelayInfo.tsx +++ b/packages/app/src/Pages/settings/RelayInfo.tsx @@ -1,5 +1,5 @@ -import { FormattedMessage } from "react-intl"; -import ProfilePreview from "Element/ProfilePreview"; +import FormattedMessage from "Element/FormattedMessage"; +import ProfilePreview from "Element/User/ProfilePreview"; import useRelayState from "Feed/RelayState"; import { useNavigate, useParams } from "react-router-dom"; import { parseId, unwrap } from "SnortUtils"; diff --git a/packages/app/src/Pages/settings/Relays.tsx b/packages/app/src/Pages/settings/Relays.tsx index 9470ed22..035d9f39 100644 --- a/packages/app/src/Pages/settings/Relays.tsx +++ b/packages/app/src/Pages/settings/Relays.tsx @@ -1,14 +1,17 @@ import { useMemo, useState } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; +import { unixNowMs } from "@snort/shared"; -import { randomSample, unixNowMs } from "SnortUtils"; -import Relay from "Element/Relay"; -import useEventPublisher from "Feed/EventPublisher"; +import { randomSample } from "SnortUtils"; +import Relay from "Element/Relay/Relay"; +import useEventPublisher from "Hooks/useEventPublisher"; import { System } from "index"; import useLogin from "Hooks/useLogin"; import { setRelays } from "Login"; +import AsyncButton from "Element/AsyncButton"; import messages from "./messages"; + const RelaySettingsPage = () => { const publisher = useEventPublisher(); const login = useLogin(); @@ -89,9 +92,9 @@ const RelaySettingsPage = () => {
- +
{addRelay()}

diff --git a/packages/app/src/Pages/settings/Root.css b/packages/app/src/Pages/settings/Root.css index 5afcbcc6..434ebed1 100644 --- a/packages/app/src/Pages/settings/Root.css +++ b/packages/app/src/Pages/settings/Root.css @@ -10,7 +10,7 @@ } .settings-nav > div { - border: 1px solid var(--gray-superdark); + border: 1px solid var(--border-color); } .settings-nav > div.content { @@ -26,7 +26,7 @@ grid-template-columns: 24px 1fr 24px; align-items: center; cursor: pointer; - padding: 12px 16px; + padding: 12px 0px 12px 16px; gap: 8px; font-size: 16px; font-weight: 600; diff --git a/packages/app/src/Pages/settings/Root.tsx b/packages/app/src/Pages/settings/Root.tsx index 2ac92d6f..8edab2b8 100644 --- a/packages/app/src/Pages/settings/Root.tsx +++ b/packages/app/src/Pages/settings/Root.tsx @@ -1,11 +1,10 @@ import "./Root.css"; import { useEffect, useMemo } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; import Icon from "Icons/Icon"; import { LoginStore, logout } from "Login"; import useLogin from "Hooks/useLogin"; -import { unwrap } from "SnortUtils"; import { getCurrentSubscription } from "Subscription"; import messages from "./messages"; @@ -19,7 +18,7 @@ const SettingsIndex = () => { const sub = getCurrentSubscription(LoginStore.allSubscriptions()); function handleLogout() { - logout(unwrap(login.publicKey)); + logout(login.id); navigate("/"); } @@ -52,6 +51,11 @@ const SettingsIndex = () => { +
navigate("moderation")}> + + + +
navigate("handle")}> diff --git a/packages/app/src/Pages/settings/WalletSettings.tsx b/packages/app/src/Pages/settings/WalletSettings.tsx index 26540973..ca1f107e 100644 --- a/packages/app/src/Pages/settings/WalletSettings.tsx +++ b/packages/app/src/Pages/settings/WalletSettings.tsx @@ -1,6 +1,6 @@ import "./WalletSettings.css"; import LndLogo from "lnd-logo.png"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Link, RouteObject, useNavigate } from "react-router-dom"; import BlueWallet from "Icons/BlueWallet"; diff --git a/packages/app/src/Pages/settings/handle/LNAddress.tsx b/packages/app/src/Pages/settings/handle/LNAddress.tsx index 562dc571..1f4d5fc0 100644 --- a/packages/app/src/Pages/settings/handle/LNAddress.tsx +++ b/packages/app/src/Pages/settings/handle/LNAddress.tsx @@ -4,7 +4,7 @@ import { LNURL } from "@snort/shared"; import { ApiHost } from "Const"; import AsyncButton from "Element/AsyncButton"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider"; export default function LNForwardAddress({ handle }: { handle: ManageHandle }) { @@ -29,7 +29,7 @@ export default function LNForwardAddress({ handle }: { handle: ManageHandle }) { setError( formatMessage({ defaultMessage: "Invalid LNURL", - }) + }), ); return; } diff --git a/packages/app/src/Pages/settings/handle/ListHandles.tsx b/packages/app/src/Pages/settings/handle/ListHandles.tsx index b209710a..7677f72f 100644 --- a/packages/app/src/Pages/settings/handle/ListHandles.tsx +++ b/packages/app/src/Pages/settings/handle/ListHandles.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Link, useNavigate } from "react-router-dom"; import { ApiHost } from "Const"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider"; export default function ListHandles() { diff --git a/packages/app/src/Pages/settings/handle/TransferHandle.tsx b/packages/app/src/Pages/settings/handle/TransferHandle.tsx index ba5b0907..d21249f2 100644 --- a/packages/app/src/Pages/settings/handle/TransferHandle.tsx +++ b/packages/app/src/Pages/settings/handle/TransferHandle.tsx @@ -1,6 +1,6 @@ import { ApiHost } from "Const"; import AsyncButton from "Element/AsyncButton"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import { ServiceError } from "Nip05/ServiceProvider"; import SnortServiceProvider, { ManageHandle } from "Nip05/SnortServiceProvider"; import { useState } from "react"; diff --git a/packages/app/src/Pages/settings/handle/index.tsx b/packages/app/src/Pages/settings/handle/index.tsx index 5a60b380..c8531da1 100644 --- a/packages/app/src/Pages/settings/handle/index.tsx +++ b/packages/app/src/Pages/settings/handle/index.tsx @@ -1,4 +1,4 @@ -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Outlet, RouteObject, useNavigate } from "react-router-dom"; import ListHandles from "./ListHandles"; diff --git a/packages/app/src/Pages/settings/wallet/Cashu.tsx b/packages/app/src/Pages/settings/wallet/Cashu.tsx index 5a2f4efa..e1a6d411 100644 --- a/packages/app/src/Pages/settings/wallet/Cashu.tsx +++ b/packages/app/src/Pages/settings/wallet/Cashu.tsx @@ -39,7 +39,7 @@ const ConnectCashu = () => { setError( formatMessage({ defaultMessage: "Unknown error", - }) + }), ); } } diff --git a/packages/app/src/Pages/settings/wallet/LNC.tsx b/packages/app/src/Pages/settings/wallet/LNC.tsx index d33fa9fe..64c66d21 100644 --- a/packages/app/src/Pages/settings/wallet/LNC.tsx +++ b/packages/app/src/Pages/settings/wallet/LNC.tsx @@ -32,7 +32,7 @@ const ConnectLNC = () => { setError( formatMessage({ defaultMessage: "Unknown error", - }) + }), ); } } diff --git a/packages/app/src/Pages/settings/wallet/LNDHub.tsx b/packages/app/src/Pages/settings/wallet/LNDHub.tsx index b33462ec..d2c20f44 100644 --- a/packages/app/src/Pages/settings/wallet/LNDHub.tsx +++ b/packages/app/src/Pages/settings/wallet/LNDHub.tsx @@ -37,7 +37,7 @@ const ConnectLNDHub = () => { setError( formatMessage({ defaultMessage: "Unknown error", - }) + }), ); } } diff --git a/packages/app/src/Pages/settings/wallet/NWC.tsx b/packages/app/src/Pages/settings/wallet/NWC.tsx index 921d6c59..41419615 100644 --- a/packages/app/src/Pages/settings/wallet/NWC.tsx +++ b/packages/app/src/Pages/settings/wallet/NWC.tsx @@ -37,7 +37,7 @@ const ConnectNostrWallet = () => { setError( formatMessage({ defaultMessage: "Unknown error", - }) + }), ); } } diff --git a/packages/app/src/Pages/subscribe/ManageSubscription.tsx b/packages/app/src/Pages/subscribe/ManageSubscription.tsx index 120d138d..b4b194c4 100644 --- a/packages/app/src/Pages/subscribe/ManageSubscription.tsx +++ b/packages/app/src/Pages/subscribe/ManageSubscription.tsx @@ -1,9 +1,9 @@ import { useEffect, useState } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Link, useNavigate } from "react-router-dom"; import PageSpinner from "Element/PageSpinner"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import SnortApi, { Subscription, SubscriptionError } from "SnortApi"; import { mapSubscriptionErrorCode } from "."; import SubscriptionCard from "./SubscriptionCard"; @@ -33,7 +33,7 @@ export default function ManageSubscriptionPage() { return ; } return ( -
+

@@ -41,7 +41,7 @@ export default function ManageSubscriptionPage() { ))} {subs.length !== 0 && ( - )} diff --git a/packages/app/src/Pages/subscribe/SubscriptionCard.tsx b/packages/app/src/Pages/subscribe/SubscriptionCard.tsx index 87c872ab..610cd39f 100644 --- a/packages/app/src/Pages/subscribe/SubscriptionCard.tsx +++ b/packages/app/src/Pages/subscribe/SubscriptionCard.tsx @@ -5,11 +5,11 @@ import SnortApi, { Subscription, SubscriptionError } from "SnortApi"; import { mapPlanName, mapSubscriptionErrorCode } from "."; import AsyncButton from "Element/AsyncButton"; import Icon from "Icons/Icon"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import SendSats from "Element/SendSats"; import Nip5Service from "Element/Nip5Service"; import { SnortNostrAddressService } from "Pages/NostrAddressPage"; -import Nip05 from "Element/Nip05"; +import Nip05 from "Element/User/Nip05"; export default function SubscriptionCard({ sub }: { sub: Subscription }) { const publisher = useEventPublisher(); @@ -62,7 +62,7 @@ export default function SubscriptionCard({ sub }: { sub: Subscription }) { return ( <> -
+
{mapPlanName(sub.type)} diff --git a/packages/app/src/Pages/subscribe/index.tsx b/packages/app/src/Pages/subscribe/index.tsx index 48acf99a..9167956f 100644 --- a/packages/app/src/Pages/subscribe/index.tsx +++ b/packages/app/src/Pages/subscribe/index.tsx @@ -1,14 +1,14 @@ import "./index.css"; import { useState } from "react"; -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { RouteObject } from "react-router-dom"; import { formatShort } from "Number"; import { LockedFeatures, Plans, SubscriptionType } from "Subscription"; import ManageSubscriptionPage from "Pages/subscribe/ManageSubscription"; import AsyncButton from "Element/AsyncButton"; -import useEventPublisher from "Feed/EventPublisher"; +import useEventPublisher from "Hooks/useEventPublisher"; import SnortApi, { SubscriptionError, SubscriptionErrorCode } from "SnortApi"; import SendSats from "Element/SendSats"; diff --git a/packages/app/src/SnortApi.ts b/packages/app/src/SnortApi.ts index a69f71e6..a94ad9c7 100644 --- a/packages/app/src/SnortApi.ts +++ b/packages/app/src/SnortApi.ts @@ -84,11 +84,15 @@ export default class SnortApi { return this.#getJson(`api/v1/preview?url=${encodeURIComponent(url)}`); } + onChainDonation() { + return this.#getJson<{ address: string }>("p/on-chain"); + } + async #getJsonAuthd( path: string, method?: "GET" | string, body?: { [key: string]: string }, - headers?: { [key: string]: string } + headers?: { [key: string]: string }, ): Promise { if (!this.#publisher) { throw new Error("Publisher not set"); @@ -110,7 +114,7 @@ export default class SnortApi { path: string, method?: "GET" | string, body?: { [key: string]: string }, - headers?: { [key: string]: string } + headers?: { [key: string]: string }, ): Promise { const rsp = await fetch(`${this.#url}${path}`, { method: method, diff --git a/packages/app/src/SnortUtils/index.ts b/packages/app/src/SnortUtils/index.ts index 33ca7af6..65062b2a 100644 --- a/packages/app/src/SnortUtils/index.ts +++ b/packages/app/src/SnortUtils/index.ts @@ -50,7 +50,7 @@ export async function openFile(): Promise { } }, 300); }, - { once: true } + { once: true }, ); }); } @@ -173,14 +173,6 @@ export function getAllReactions(notes: readonly TaggedNostrEvent[] | undefined, return notes?.filter(a => a.kind === (kind ?? a.kind) && a.tags.some(a => a[0] === "e" && ids.includes(a[1]))) || []; } -export function unixNow() { - return Math.floor(unixNowMs() / 1000); -} - -export function unixNowMs() { - return new Date().getTime(); -} - export function deepClone(obj: T) { if ("structuredClone" in window) { return structuredClone(obj); @@ -217,7 +209,7 @@ export function dedupeByPubkey(events: TaggedNostrEvent[]) { list: [...list, ev], }; }, - { list: [], seen: new Set([]) } + { list: [], seen: new Set([]) }, ); return deduped.list as TaggedNostrEvent[]; } @@ -234,7 +226,7 @@ export function dedupeById(events: Array) { list: [...list, ev], }; }, - { list: [], seen: new Set([]) } + { list: [], seen: new Set([]) }, ); return deduped.list as Array; } @@ -495,7 +487,7 @@ export function kvToObject(o: string, sep?: string) { return [match[1], match[2]]; } return []; - }) + }), ) as T; } diff --git a/packages/app/src/State/NoteCreator.ts b/packages/app/src/State/NoteCreator.ts deleted file mode 100644 index ccde3617..00000000 --- a/packages/app/src/State/NoteCreator.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { NostrEvent, TaggedNostrEvent } from "@snort/system"; - -interface NoteCreatorStore { - show: boolean; - note: string; - error: string; - active: boolean; - preview?: NostrEvent; - replyTo?: TaggedNostrEvent; - showAdvanced: boolean; - selectedCustomRelays: false | Array; - zapForward: string; - sensitive: string; - pollOptions?: Array; - otherEvents: Array; -} - -const InitState: NoteCreatorStore = { - show: false, - note: "", - error: "", - active: false, - showAdvanced: false, - selectedCustomRelays: false, - zapForward: "", - sensitive: "", - otherEvents: [], -}; - -const NoteCreatorSlice = createSlice({ - name: "NoteCreator", - initialState: InitState, - reducers: { - setShow: (state, action: PayloadAction) => { - state.show = action.payload; - }, - setNote: (state, action: PayloadAction) => { - state.note = action.payload; - }, - setError: (state, action: PayloadAction) => { - state.error = action.payload; - }, - setActive: (state, action: PayloadAction) => { - state.active = action.payload; - }, - setPreview: (state, action: PayloadAction) => { - state.preview = action.payload; - }, - setReplyTo: (state, action: PayloadAction) => { - state.replyTo = action.payload; - }, - setShowAdvanced: (state, action: PayloadAction) => { - state.showAdvanced = action.payload; - }, - setSelectedCustomRelays: (state, action: PayloadAction>) => { - state.selectedCustomRelays = action.payload; - }, - setZapForward: (state, action: PayloadAction) => { - state.zapForward = action.payload; - }, - setSensitive: (state, action: PayloadAction) => { - state.sensitive = action.payload; - }, - setPollOptions: (state, action: PayloadAction | undefined>) => { - state.pollOptions = action.payload; - }, - setOtherEvents: (state, action: PayloadAction>) => { - state.otherEvents = action.payload; - }, - reset: () => InitState, - }, -}); - -export const { - setShow, - setNote, - setError, - setActive, - setPreview, - setReplyTo, - setShowAdvanced, - setSelectedCustomRelays, - setZapForward, - setSensitive, - setPollOptions, - setOtherEvents, - reset, -} = NoteCreatorSlice.actions; - -export const reducer = NoteCreatorSlice.reducer; diff --git a/packages/app/src/State/NoteCreator.tsx b/packages/app/src/State/NoteCreator.tsx new file mode 100644 index 00000000..b2b84e6c --- /dev/null +++ b/packages/app/src/State/NoteCreator.tsx @@ -0,0 +1,94 @@ +import { ExternalStore } from "@snort/shared"; +import { NostrEvent, TaggedNostrEvent } from "@snort/system"; +import { ZapTarget } from "Zapper"; +import { useSyncExternalStore } from "react"; +import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector"; + +interface NoteCreatorDataSnapshot { + show: boolean; + note: string; + error: string; + active: boolean; + advanced: boolean; + preview?: NostrEvent; + replyTo?: TaggedNostrEvent; + selectedCustomRelays?: Array; + zapSplits?: Array; + sensitive?: string; + pollOptions?: Array; + otherEvents?: Array; + reset: () => void; + update: (fn: (v: NoteCreatorDataSnapshot) => void) => void; +} + +class NoteCreatorStore extends ExternalStore { + #data: NoteCreatorDataSnapshot; + + constructor() { + super(); + this.#data = { + show: false, + note: "", + error: "", + active: false, + advanced: false, + reset: () => { + this.#reset(this.#data); + this.notifyChange(this.#data); + }, + update: (fn: (v: NoteCreatorDataSnapshot) => void) => { + fn(this.#data); + this.notifyChange(this.#data); + }, + }; + } + + #reset(d: NoteCreatorDataSnapshot) { + d.show = false; + d.note = ""; + d.error = ""; + d.active = false; + d.advanced = false; + d.preview = undefined; + d.replyTo = undefined; + d.selectedCustomRelays = undefined; + d.zapSplits = undefined; + d.sensitive = undefined; + d.pollOptions = undefined; + d.otherEvents = undefined; + } + + takeSnapshot(): NoteCreatorDataSnapshot { + const sn = { + ...this.#data, + reset: () => { + this.#reset(this.#data); + }, + update: (fn: (v: NoteCreatorDataSnapshot) => void) => { + fn(this.#data); + this.notifyChange(this.#data); + }, + } as NoteCreatorDataSnapshot; + return sn; + } +} + +const NoteCreatorState = new NoteCreatorStore(); + +export function useNoteCreator( + selector?: (v: NoteCreatorDataSnapshot) => T, +) { + if (selector) { + return useSyncExternalStoreWithSelector( + c => NoteCreatorState.hook(c), + () => NoteCreatorState.snapshot(), + undefined, + selector, + ); + } else { + return useSyncExternalStore( + c => NoteCreatorState.hook(c), + () => NoteCreatorState.snapshot() as T, + ); + } +} diff --git a/packages/app/src/State/ReBroadcast.ts b/packages/app/src/State/ReBroadcast.ts deleted file mode 100644 index 23cc253b..00000000 --- a/packages/app/src/State/ReBroadcast.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { NostrEvent } from "@snort/system"; - -interface ReBroadcastStore { - show: boolean; - selectedCustomRelays: false | Array; - note?: NostrEvent; -} - -const InitState: ReBroadcastStore = { - show: false, - selectedCustomRelays: false, -}; - -const ReBroadcastSlice = createSlice({ - name: "ReBroadcast", - initialState: InitState, - reducers: { - setShow: (state, action: PayloadAction) => { - state.show = action.payload; - }, - setNote: (state, action: PayloadAction) => { - state.note = action.payload; - }, - setSelectedCustomRelays: (state, action: PayloadAction>) => { - state.selectedCustomRelays = action.payload; - }, - reset: () => InitState, - }, -}); - -export const { setShow, setNote, setSelectedCustomRelays, reset } = ReBroadcastSlice.actions; - -export const reducer = ReBroadcastSlice.reducer; diff --git a/packages/app/src/State/Store.ts b/packages/app/src/State/Store.ts deleted file mode 100644 index 57bec805..00000000 --- a/packages/app/src/State/Store.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { configureStore } from "@reduxjs/toolkit"; -import { reducer as NoteCreatorReducer } from "State/NoteCreator"; -import { reducer as ReBroadcastReducer } from "State/ReBroadcast"; - -const store = configureStore({ - reducer: { - noteCreator: NoteCreatorReducer, - reBroadcast: ReBroadcastReducer, - }, -}); - -export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; - -export default store; diff --git a/packages/app/src/Subscription/index.ts b/packages/app/src/Subscription/index.ts index 6486f2b6..e3cf86a9 100644 --- a/packages/app/src/Subscription/index.ts +++ b/packages/app/src/Subscription/index.ts @@ -1,4 +1,4 @@ -import { unixNow } from "SnortUtils"; +import { unixNow } from "@snort/shared"; export enum SubscriptionType { Supporter = 0, diff --git a/packages/app/src/Tasks/DonateTask.tsx b/packages/app/src/Tasks/DonateTask.tsx index 0d1b8518..f6cbebe4 100644 --- a/packages/app/src/Tasks/DonateTask.tsx +++ b/packages/app/src/Tasks/DonateTask.tsx @@ -1,4 +1,4 @@ -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Link } from "react-router-dom"; import { BaseUITask } from "Tasks"; diff --git a/packages/app/src/Tasks/Nip5Task.tsx b/packages/app/src/Tasks/Nip5Task.tsx index 9087dd69..2b5cfcf8 100644 --- a/packages/app/src/Tasks/Nip5Task.tsx +++ b/packages/app/src/Tasks/Nip5Task.tsx @@ -1,4 +1,4 @@ -import { FormattedMessage } from "react-intl"; +import FormattedMessage from "Element/FormattedMessage"; import { Link } from "react-router-dom"; import { MetadataCache } from "@snort/system"; import { BaseUITask } from "Tasks"; diff --git a/packages/app/src/Toaster.tsx b/packages/app/src/Toaster.tsx index eecb85ca..1d45a177 100644 --- a/packages/app/src/Toaster.tsx +++ b/packages/app/src/Toaster.tsx @@ -39,7 +39,7 @@ export const Toastore = new ToasterSlots(); export default function Toaster() { const toast = useSyncExternalStore( c => Toastore.hook(c), - () => Toastore.snapshot() + () => Toastore.snapshot(), ); return ( diff --git a/packages/app/src/Upload/VoidCat.ts b/packages/app/src/Upload/VoidCat.ts index 8eb8a11a..0fe5e010 100644 --- a/packages/app/src/Upload/VoidCat.ts +++ b/packages/app/src/Upload/VoidCat.ts @@ -12,7 +12,7 @@ import { magnetURIDecode } from "SnortUtils"; export default async function VoidCatUpload( file: File | Blob, filename: string, - publisher?: EventPublisher + publisher?: EventPublisher, ): Promise { const api = new VoidApi(VoidCatHost); const uploader = api.getUploader(file); diff --git a/packages/app/src/Wallet/Cashu.ts b/packages/app/src/Wallet/Cashu.ts index 541e65ba..b88dc13c 100644 --- a/packages/app/src/Wallet/Cashu.ts +++ b/packages/app/src/Wallet/Cashu.ts @@ -57,6 +57,6 @@ export interface NutStashBackup { mints: [ { mintURL: string; - } + }, ]; } diff --git a/packages/app/src/Wallet/LNCWallet.ts b/packages/app/src/Wallet/LNCWallet.ts index 342dfec8..6093289e 100644 --- a/packages/app/src/Wallet/LNCWallet.ts +++ b/packages/app/src/Wallet/LNCWallet.ts @@ -126,7 +126,7 @@ export class LNCWallet implements LNWallet { err => { this.#log(err); reject(err); - } + }, ); }); } diff --git a/packages/app/src/Wallet/NostrWalletConnect.ts b/packages/app/src/Wallet/NostrWalletConnect.ts index d2d1113d..5930e60d 100644 --- a/packages/app/src/Wallet/NostrWalletConnect.ts +++ b/packages/app/src/Wallet/NostrWalletConnect.ts @@ -182,7 +182,7 @@ export class NostrConnectWallet implements LNWallet { ], () => { // ignored - } + }, ); await this.#conn.SendAsync(evCommand); return await new Promise((resolve, reject) => { diff --git a/packages/app/src/Wallet/WebLN.ts b/packages/app/src/Wallet/WebLN.ts index e9708277..d282edf3 100644 --- a/packages/app/src/Wallet/WebLN.ts +++ b/packages/app/src/Wallet/WebLN.ts @@ -22,7 +22,9 @@ processWorkQueue(WebLNQueue); */ export function setupWebLNWalletConfig(store: WalletStore) { const wallets = store.list(); - if (window.webln && !wallets.some(a => a.kind === WalletKind.WebLN)) { + + const existing = wallets.find(a => a.kind === WalletKind.WebLN); + if (window.webln && !existing) { const newConfig = { id: "webln", kind: WalletKind.WebLN, @@ -32,6 +34,8 @@ export function setupWebLNWalletConfig(store: WalletStore) { }, } as WalletConfig; store.add(newConfig); + } else if (existing) { + store.remove(existing.id); } } @@ -84,7 +88,7 @@ export class WebLNWallet implements LNWallet { await window.webln?.makeInvoice({ amount: req.amount, defaultMemo: req.memo, - }) + }), ); if (rsp) { const invoice = prToWalletInvoice(rsp.paymentRequest); diff --git a/packages/app/src/Wallet/index.ts b/packages/app/src/Wallet/index.ts index ad8a01dd..cb5b98e0 100644 --- a/packages/app/src/Wallet/index.ts +++ b/packages/app/src/Wallet/index.ts @@ -248,7 +248,7 @@ window.document.addEventListener("close", () => { export function useWallet() { const wallet = useSyncExternalStore( h => Wallets.hook(h), - () => Wallets.snapshot() + () => Wallets.snapshot(), ); useEffect(() => { if (wallet.wallet?.isReady() === false && wallet.wallet.canAutoLogin()) { diff --git a/packages/app/src/ZapPoolController.ts b/packages/app/src/ZapPoolController.ts index 0695b5d7..99a290d4 100644 --- a/packages/app/src/ZapPoolController.ts +++ b/packages/app/src/ZapPoolController.ts @@ -1,5 +1,5 @@ import { UserCache } from "Cache"; -import { getDisplayName } from "Element/ProfileImage"; +import { getDisplayName } from "Element/User/ProfileImage"; import { LNURL, ExternalStore, unixNow } from "@snort/shared"; import { Toastore } from "Toaster"; import { LNWallet, WalletInvoiceState, Wallets } from "Wallet"; @@ -54,7 +54,7 @@ class ZapPool extends ExternalStore> { Toastore.push({ element: `Sent ${amtSend.toLocaleString()} sats to ${getDisplayName( profile, - x.pubkey + x.pubkey, )} from your zap pool`, expire: unixNow() + 10, icon: "zap", diff --git a/packages/app/src/Zapper.ts b/packages/app/src/Zapper.ts new file mode 100644 index 00000000..eb211ef8 --- /dev/null +++ b/packages/app/src/Zapper.ts @@ -0,0 +1,205 @@ +import { LNURL } from "@snort/shared"; +import { EventPublisher, NostrEvent, NostrLink, SystemInterface } from "@snort/system"; +import { generateRandomKey } from "Login"; +import { isHex } from "SnortUtils"; +import { LNWallet, WalletInvoiceState } from "Wallet"; + +export interface ZapTarget { + type: "lnurl" | "pubkey"; + value: string; + weight: number; + memo?: string; + name?: string; + zap?: { + pubkey: string; + anon: boolean; + event?: NostrLink; + }; +} + +export interface ZapTargetResult { + target: ZapTarget; + paid: boolean; + sent: number; + fee: number; + pr: string; + error?: Error; +} + +interface ZapTargetLoaded { + target: ZapTarget; + svc?: LNURL; +} + +export class Zapper { + #inProgress = false; + #loadedTargets?: Array; + + constructor( + readonly system: SystemInterface, + readonly publisher?: EventPublisher, + readonly onResult?: (r: ZapTargetResult) => void, + ) {} + + /** + * Create targets from Event + */ + static fromEvent(ev: NostrEvent) { + return ev.tags + .filter(a => a[0] === "zap") + .map(v => { + if (v[1].length === 64 && isHex(v[1]) && v.length === 4) { + // NIP-57.G + return { + type: "pubkey", + value: v[1], + weight: Number(v[3] ?? 0), + zap: { + pubkey: v[1], + event: NostrLink.fromEvent(ev), + }, + } as ZapTarget; + } else { + // assume event specific zap target + return { + type: "lnurl", + value: v[1], + weight: 1, + zap: { + pubkey: ev.pubkey, + event: NostrLink.fromEvent(ev), + }, + } as ZapTarget; + } + }); + } + + async send(wallet: LNWallet | undefined, targets: Array, amount: number) { + if (this.#inProgress) { + throw new Error("Payout already in progress"); + } + this.#inProgress = true; + + const total = targets.reduce((acc, v) => (acc += v.weight), 0); + const ret = [] as Array; + + for (const t of targets) { + const toSend = Math.floor(amount * (t.weight / total)); + try { + const svc = await this.#getService(t); + if (!svc) { + throw new Error(`Failed to get invoice from ${t.value}`); + } + const relays = this.system.Sockets.filter(a => !a.ephemeral).map(v => v.address); + const pub = t.zap?.anon ?? false ? EventPublisher.privateKey(generateRandomKey().privateKey) : this.publisher; + const zap = + t.zap && svc.canZap + ? await pub?.zap(toSend * 1000, t.zap.pubkey, relays, undefined, t.memo, eb => { + if (t.zap?.event) { + const tag = t.zap.event.toEventTag(); + if (tag) { + eb.tag(tag); + } + } + if (t.zap?.anon) { + eb.tag(["anon", ""]); + } + return eb; + }) + : undefined; + const invoice = await svc.getInvoice(toSend, t.memo, zap); + if (invoice?.pr) { + const res = await wallet?.payInvoice(invoice.pr); + ret.push({ + target: t, + paid: res?.state === WalletInvoiceState.Paid, + sent: toSend, + pr: invoice.pr, + fee: res?.fees ?? 0, + }); + this.onResult?.(ret[ret.length - 1]); + } else { + throw new Error(`Failed to get invoice from ${t.value}`); + } + } catch (e) { + ret.push({ + target: t, + paid: false, + sent: 0, + fee: 0, + pr: "", + error: e as Error, + }); + this.onResult?.(ret[ret.length - 1]); + } + } + + this.#inProgress = false; + return ret; + } + + async load(targets: Array) { + const svcs = targets.map(async a => { + return { + target: a, + loading: await this.#getService(a), + }; + }); + const loaded = await Promise.all(svcs); + this.#loadedTargets = loaded.map(a => ({ + target: a.target, + svc: a.loading, + })); + } + + /** + * Any target supports zaps + */ + canZap() { + return this.#loadedTargets?.some(a => a.svc?.canZap ?? false); + } + + /** + * Max comment length which can be sent to all (smallest comment length) + */ + maxComment() { + return ( + this.#loadedTargets + ?.map(a => (a.svc?.canZap ? 255 : a.svc?.maxCommentLength ?? 0)) + .reduce((acc, v) => (acc > v ? v : acc), 255) ?? 0 + ); + } + + /** + * Max of the min amounts + */ + minAmount() { + return this.#loadedTargets?.map(a => a.svc?.min ?? 0).reduce((acc, v) => (acc < v ? v : acc), 1000) ?? 0; + } + + /** + * Min of the max amounts + */ + maxAmount() { + return this.#loadedTargets?.map(a => a.svc?.max ?? 100e9).reduce((acc, v) => (acc > v ? v : acc), 100e9) ?? 0; + } + + async #getService(t: ZapTarget) { + try { + if (t.type === "lnurl") { + const svc = new LNURL(t.value); + await svc.load(); + return svc; + } else if (t.type === "pubkey") { + const profile = await this.system.ProfileLoader.fetchProfile(t.value); + if (profile) { + const svc = new LNURL(profile.lud16 ?? profile.lud06 ?? ""); + await svc.load(); + return svc; + } + } + } catch { + // nothing + } + } +} diff --git a/packages/app/src/benchmarks.ts b/packages/app/src/benchmarks.ts new file mode 100644 index 00000000..735863a1 --- /dev/null +++ b/packages/app/src/benchmarks.ts @@ -0,0 +1,140 @@ +import { bytesToHex } from "@noble/hashes/utils"; +import { DefaultQueryOptimizer, EventExt, FlatReqFilter, PowMiner, QueryOptimizer, ReqFilter } from "@snort/system"; +import { compress, expand_filter, flat_merge, get_diff, pow, default as wasmInit } from "@snort/system-wasm"; +import WasmPath from "@snort/system-wasm/pkg/system_wasm_bg.wasm"; +import { Bench } from "tinybench"; + +const WasmQueryOptimizer = { + expandFilter: (f: ReqFilter) => { + return expand_filter(f) as Array; + }, + getDiff: (prev: Array, next: Array) => { + return get_diff(prev, next) as Array; + }, + flatMerge: (all: Array) => { + return flat_merge(all) as Array; + }, + compress: (all: Array) => { + return compress(all) as Array; + }, +} as QueryOptimizer; + +const makeOnePubkey = () => { + const rnd = globalThis.crypto.getRandomValues(new Uint8Array(32)); + return bytesToHex(rnd); +}; + +const randomPubkeys = (() => { + const ret = []; + for (let x = 0; x < 50; x++) { + ret.push(makeOnePubkey()); + } + return ret; +})(); + +const testExpand = (q: QueryOptimizer) => { + q.expandFilter({ + kinds: [1, 2, 3], + authors: randomPubkeys, + }); +}; +const testGetDiff = (q: QueryOptimizer) => { + q.getDiff( + [ + { + kinds: [1, 2, 3], + authors: randomPubkeys, + }, + ], + [ + { + kinds: [1, 2, 3, 4, 5], + authors: randomPubkeys, + }, + ], + ); +}; +const testFlatMerge = (q: QueryOptimizer) => { + q.flatMerge( + q.expandFilter({ + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }), + ); +}; +const testCompress = (q: QueryOptimizer) => { + q.compress([ + { + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }, + { + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }, + { + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }, + { + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }, + { + kinds: [1, 6, 7, 6969], + authors: randomPubkeys, + }, + ]); +}; + +const wasmSuite = new Bench({ time: 1_000 }); +const suite = new Bench({ time: 1_000 }); + +const addTests = (s: Bench, q: QueryOptimizer, p: PowMiner) => { + s.add("expand", () => testExpand(q)); + s.add("get_diff", () => testGetDiff(q)); + s.add("flat_merge", () => testFlatMerge(q)); + s.add("compress", () => testCompress(q)); + s.add("pow", () => { + const ev = { + id: "", + kind: 1, + created_at: 1234567, + pubkey: "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed", + content: "test", + sig: "", + tags: [], + }; + p.minePow(ev, 12); + }); +}; + +addTests(suite, DefaultQueryOptimizer, { + minePow(ev, target) { + return Promise.resolve(EventExt.minePow(ev, target)); + }, +}); +addTests(wasmSuite, WasmQueryOptimizer, { + minePow(ev, target) { + return Promise.resolve(pow(ev, target)); + }, +}); + +const runAll = async () => { + await wasmInit(WasmPath); + + console.log("DefaultQueryOptimizer"); + await suite.run(); + console.table(suite.table()); + const p0 = document.createElement("pre"); + p0.innerText = JSON.stringify(suite.table(), undefined, " "); + document.body.appendChild(p0); + + console.log("WasmQueryOptimizer"); + await wasmSuite.run(); + console.table(wasmSuite.table()); + const p1 = document.createElement("pre"); + p1.innerText = JSON.stringify(wasmSuite.table(), undefined, " "); + document.body.appendChild(p1); +}; +runAll().catch(console.error); diff --git a/packages/app/src/chat/index.ts b/packages/app/src/chat/index.ts index 246c023b..9f35d109 100644 --- a/packages/app/src/chat/index.ts +++ b/packages/app/src/chat/index.ts @@ -13,13 +13,15 @@ import { UserMetadata, encodeTLVEntries, } from "@snort/system"; -import { unwrap } from "@snort/shared"; +import { unwrap, unixNow } from "@snort/shared"; import { Chats, GiftsCache } from "Cache"; -import { findTag, unixNow } from "SnortUtils"; +import { findTag } from "SnortUtils"; import { Nip29ChatSystem } from "./nip29"; import useModeration from "Hooks/useModeration"; import useLogin from "Hooks/useLogin"; import { Nip24ChatSystem } from "./nip24"; +import { LoginSession } from "Login"; +import { Nip28ChatSystem } from "./nip28"; export enum ChatType { DirectMessage = 1, @@ -60,7 +62,7 @@ export interface ChatSystem { /** * Create a request for this system to get updates */ - subscription(id: string): RequestBuilder | undefined; + subscription(session: LoginSession): RequestBuilder | undefined; onEvent(evs: readonly TaggedNostrEvent[]): Promise | void; listChats(pk: string): Array; @@ -69,6 +71,7 @@ export interface ChatSystem { export const Nip4Chats = new Nip4ChatSystem(Chats); export const Nip29Chats = new Nip29ChatSystem(Chats); export const Nip24Chats = new Nip24ChatSystem(GiftsCache); +export const Nip28Chats = new Nip28ChatSystem(Chats); /** * Extract the P tag of the event @@ -116,7 +119,7 @@ export function createChatLink(type: ChatType, ...params: Array) { type: TLVEntryType.Author, length: params[0].length, value: params[0], - } as TLVEntry + } as TLVEntry, )}`; } case ChatType.PrivateDirectMessage: { @@ -127,7 +130,7 @@ export function createChatLink(type: ChatType, ...params: Array) { type: TLVEntryType.Author, length: params[0].length, value: params[0], - } as TLVEntry + } as TLVEntry, )}`; } case ChatType.PrivateGroupChat: { @@ -139,51 +142,65 @@ export function createChatLink(type: ChatType, ...params: Array) { type: TLVEntryType.Author, length: a.length, value: a, - } as TLVEntry) - ) + }) as TLVEntry, + ), )}`; } + case ChatType.PublicGroupChat: { + return `/messages/${Nip28ChatSystem.chatId(params[0])}`; + } } throw new Error("Unknown chat type"); } export function createEmptyChatObject(id: string) { - if (id.startsWith("chat4")) { + if (id.startsWith("chat41")) { return Nip4ChatSystem.createChatObj(id, []); } - if (id.startsWith("chat24")) { + if (id.startsWith("chat241")) { return Nip24ChatSystem.createChatObj(id, []); } + if (id.startsWith("chat281")) { + return Nip28ChatSystem.createChatObj(id, []); + } throw new Error("Cant create new empty chat, unknown id"); } export function useNip4Chat() { - const { publicKey } = useLogin(); + const { publicKey } = useLogin(s => ({ publicKey: s.publicKey })); return useSyncExternalStore( c => Nip4Chats.hook(c), - () => Nip4Chats.snapshot(publicKey) + () => Nip4Chats.snapshot(publicKey), ); } export function useNip29Chat() { return useSyncExternalStore( c => Nip29Chats.hook(c), - () => Nip29Chats.snapshot() + () => Nip29Chats.snapshot(), ); } export function useNip24Chat() { - const { publicKey } = useLogin(); + const { publicKey } = useLogin(s => ({ publicKey: s.publicKey })); return useSyncExternalStore( c => Nip24Chats.hook(c), - () => Nip24Chats.snapshot(publicKey) + () => Nip24Chats.snapshot(publicKey), + ); +} + +export function useNip28Chat() { + return useSyncExternalStore( + c => Nip28Chats.hook(c), + () => Nip28Chats.snapshot(), ); } export function useChatSystem() { const nip4 = useNip4Chat(); - const nip24 = useNip24Chat(); + //const nip24 = useNip24Chat(); + const nip28 = useNip28Chat(); const { muted, blocked } = useModeration(); - return [...nip4, ...nip24].filter(a => !(muted.includes(a.id) || blocked.includes(a.id))); + return [...nip4, ...nip28].filter(a => !(muted.includes(a.id) || blocked.includes(a.id))); } diff --git a/packages/app/src/chat/nip24.ts b/packages/app/src/chat/nip24.ts index 9344e6fb..9f32adf4 100644 --- a/packages/app/src/chat/nip24.ts +++ b/packages/app/src/chat/nip24.ts @@ -3,7 +3,7 @@ import { EventKind, NostrPrefix, encodeTLVEntries, TLVEntryType, TLVEntry, decod import { GiftWrapCache } from "Cache/GiftWrapCache"; import { UnwrappedGift } from "Db"; import { Chat, ChatSystem, ChatType, lastReadInChat } from "chat"; -import { DefaultPowWorker } from "index"; +import { WasmPowWorker } from "index"; export class Nip24ChatSystem extends ExternalStore> implements ChatSystem { #cache: GiftWrapCache; @@ -39,8 +39,8 @@ export class Nip24ChatSystem extends ExternalStore> implements ChatS value: v, type: TLVEntryType.Author, length: v.length, - } as TLVEntry) - ) + }) as TLVEntry, + ), ); }; return dedupe(messages.map(a => chatId(a))).map(a => { @@ -72,7 +72,7 @@ export class Nip24ChatSystem extends ExternalStore> implements ChatS } as { t: number; title: string | undefined; - } + }, ); return { type: ChatType.PrivateDirectMessage, @@ -105,7 +105,9 @@ export class Nip24ChatSystem extends ExternalStore> implements ChatS const recvSealedN = pub.giftWrap(await pub.sealRumor(gossip, pt.id), pt.id, powTarget); messages.push(recvSealedN); } - messages.push(pub.giftWrap(await pub.sealRumor(gossip, pub.pubKey), pub.pubKey, powTarget, DefaultPowWorker)); + messages.push( + pub.giftWrap(await pub.sealRumor(gossip, pub.pubKey), pub.pubKey, powTarget, new WasmPowWorker()), + ); return await Promise.all(messages); }, sendMessage: (ev, system) => { diff --git a/packages/app/src/chat/nip28.ts b/packages/app/src/chat/nip28.ts index e69de29b..cc0c5d86 100644 --- a/packages/app/src/chat/nip28.ts +++ b/packages/app/src/chat/nip28.ts @@ -0,0 +1,164 @@ +import debug from "debug"; +import { ExternalStore, FeedCache, unixNow, unwrap } from "@snort/shared"; +import { + EventKind, + NostrEvent, + NostrPrefix, + RequestBuilder, + SystemInterface, + TLVEntryType, + TaggedNostrEvent, + UserMetadata, + decodeTLV, + encodeTLVEntries, +} from "@snort/system"; + +import { LoginSession } from "Login"; +import { findTag } from "SnortUtils"; +import { Chat, ChatParticipant, ChatSystem, ChatType, lastReadInChat } from "chat"; +import { Day } from "Const"; + +export class Nip28ChatSystem extends ExternalStore> implements ChatSystem { + #cache: FeedCache; + #log = debug("NIP-04"); + readonly ChannelKinds = [ + EventKind.PublicChatChannel, + EventKind.PublicChatMessage, + EventKind.PublicChatMetadata, + EventKind.PublicChatMuteMessage, + EventKind.PublicChatMuteUser, + ]; + + constructor(cache: FeedCache) { + super(); + this.#cache = cache; + } + + subscription(session: LoginSession): RequestBuilder | undefined { + const chats = (session.extraChats ?? []).filter(a => a.startsWith("chat281")); + if (chats.length === 0) return; + + const chatId = (v: string) => unwrap(decodeTLV(v).find(a => a.type === TLVEntryType.Special)).value as string; + + const messages = this.#chatChannels(); + const rb = new RequestBuilder(`nip28:${session.id}`); + rb.withFilter() + .ids(chats.map(v => chatId(v))) + .kinds([EventKind.PublicChatChannel, EventKind.PublicChatMetadata]); + for (const c of chats) { + const id = chatId(c); + const lastMessage = messages[id]?.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0) ?? 0; + rb.withFilter() + .tag("e", [id]) + .since(lastMessage === 0 ? unixNow() - 2 * Day : lastMessage) + .kinds(this.ChannelKinds); + } + + return rb; + } + + async onEvent(evs: readonly TaggedNostrEvent[]) { + const dms = evs.filter(a => this.ChannelKinds.includes(a.kind)); + if (dms.length > 0) { + await this.#cache.bulkSet(dms); + this.notifyChange(); + } + } + + listChats(): Chat[] { + const chats = this.#chatChannels(); + return Object.entries(chats).map(([k, v]) => { + return Nip28ChatSystem.createChatObj(Nip28ChatSystem.chatId(k), v); + }); + } + + static chatId(id: string) { + return encodeTLVEntries("chat28" as NostrPrefix, { + type: TLVEntryType.Special, + value: id, + length: id.length, + }); + } + + static createChatObj(id: string, messages: Array) { + const last = lastReadInChat(id); + const participants = decodeTLV(id) + .filter(v => v.type === TLVEntryType.Special) + .map( + v => + ({ + type: "generic", + id: v.value as string, + profile: this.#chatProfileFromMessages(messages), + }) as ChatParticipant, + ); + return { + type: ChatType.PublicGroupChat, + id, + unread: messages.reduce((acc, v) => (v.created_at > last ? acc++ : acc), 0), + lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0), + participants, + messages: messages + .filter(a => a.kind === EventKind.PublicChatMessage) + .map(m => ({ + id: m.id, + created_at: m.created_at, + from: m.pubkey, + tags: m.tags, + content: m.content, + needsDecryption: false, + })), + createMessage: async (msg, pub) => { + return [ + await pub.generic(eb => { + return eb.kind(EventKind.PublicChatMessage).content(msg).tag(["e", participants[0].id, "", "root"]); + }), + ]; + }, + sendMessage: (ev, system: SystemInterface) => { + ev.forEach(a => system.BroadcastEvent(a)); + }, + } as Chat; + } + + takeSnapshot(): Chat[] { + return this.listChats(); + } + + static #chatProfileFromMessages(messages: Array) { + const chatDefs = messages.filter( + a => a.kind === EventKind.PublicChatChannel || a.kind === EventKind.PublicChatMetadata, + ); + const chatDef = + chatDefs.length > 0 + ? chatDefs.reduce((acc, v) => (acc.created_at > v.created_at ? acc : v), chatDefs[0]) + : undefined; + return chatDef ? (JSON.parse(chatDef.content) as UserMetadata) : undefined; + } + + #chatChannels() { + const messages = this.#cache.snapshot(); + const chats = messages.reduce( + (acc, v) => { + const k = this.#chatId(v); + if (k) { + acc[k] ??= []; + acc[k].push(v); + } + return acc; + }, + {} as Record>, + ); + return chats; + } + + #chatId(ev: NostrEvent) { + if (ev.kind === EventKind.PublicChatChannel) { + return ev.id; + } else if (ev.kind === EventKind.PublicChatMetadata) { + return findTag(ev, "e"); + } else if (this.ChannelKinds.includes(ev.kind)) { + return ev.tags.find(a => a[0] === "e" && a[3] === "root")?.[1]; + } + } +} diff --git a/packages/app/src/chat/nip29.ts b/packages/app/src/chat/nip29.ts index 956472e7..ab7f3ce5 100644 --- a/packages/app/src/chat/nip29.ts +++ b/packages/app/src/chat/nip29.ts @@ -1,5 +1,6 @@ import { ExternalStore, FeedCache, dedupe } from "@snort/shared"; import { RequestBuilder, NostrEvent, EventKind, SystemInterface, TaggedNostrEvent } from "@snort/system"; +import { LoginSession } from "Login"; import { unwrap } from "SnortUtils"; import { Chat, ChatSystem, ChatType, lastReadInChat } from "chat"; @@ -15,7 +16,9 @@ export class Nip29ChatSystem extends ExternalStore> implements ChatS return this.listChats(); } - subscription(id: string) { + subscription(session: LoginSession) { + const id = session.publicKey; + if (!id) return; const gs = id.split("/", 2); const rb = new RequestBuilder(`nip29:${id}`); const last = this.listChats().find(a => a.id === id)?.lastMessage; @@ -46,12 +49,12 @@ export class Nip29ChatSystem extends ExternalStore> implements ChatS .map(a => a.tags.find(b => b[0] === "g")) .filter(a => a !== undefined) .map(a => unwrap(a)) - .map(a => `${a[2]}${a[1]}`) + .map(a => `${a[2]}${a[1]}`), ); return groups.map(g => { const [relay, channel] = g.split("/", 2); const messages = allMessages.filter( - a => `${a.tags.find(b => b[0] === "g")?.[2]}${a.tags.find(b => b[0] === "g")?.[1]}` === g + a => `${a.tags.find(b => b[0] === "g")?.[2]}${a.tags.find(b => b[0] === "g")?.[1]}` === g, ); const lastRead = lastReadInChat(g); return { diff --git a/packages/app/src/chat/nip4.ts b/packages/app/src/chat/nip4.ts index 52f9a5ab..cc70a44d 100644 --- a/packages/app/src/chat/nip4.ts +++ b/packages/app/src/chat/nip4.ts @@ -1,4 +1,4 @@ -import { ExternalStore, FeedCache, dedupe } from "@snort/shared"; +import { ExternalStore, FeedCache } from "@snort/shared"; import { EventKind, NostrEvent, @@ -6,10 +6,11 @@ import { RequestBuilder, SystemInterface, TLVEntryType, - decodeTLV, encodeTLVEntries, TaggedNostrEvent, + decodeTLV, } from "@snort/system"; +import { LoginSession } from "Login"; import { Chat, ChatSystem, ChatType, inChatWith, lastReadInChat } from "chat"; import { debug } from "debug"; @@ -30,12 +31,15 @@ export class Nip4ChatSystem extends ExternalStore> implements ChatSy } } - subscription(pk: string) { + subscription(session: LoginSession) { + const pk = session.publicKey; + if (!pk || session.readonly) return; + const rb = new RequestBuilder(`nip4:${pk.slice(0, 12)}`); const dms = this.#cache.snapshot(); const dmSince = dms.reduce( (acc, v) => (v.created_at > acc && v.kind === EventKind.DirectMessage ? (acc = v.created_at) : acc), - 0 + 0, ); this.#log("Loading DMS since %s", new Date(dmSince * 1000)); @@ -50,34 +54,42 @@ export class Nip4ChatSystem extends ExternalStore> implements ChatSy listChats(pk: string): Chat[] { const myDms = this.#nip4Events(); - const chatId = (a: NostrEvent) => { - return encodeTLVEntries("chat4" as NostrPrefix, { - type: TLVEntryType.Author, - value: inChatWith(a, pk), - length: 0, - }); - }; + const chats = myDms.reduce( + (acc, v) => { + const chatId = inChatWith(v, pk); + acc[chatId] ??= []; + acc[chatId].push(v); + return acc; + }, + {} as Record>, + ); - return dedupe(myDms.map(chatId)).map(a => { - const messages = myDms.filter(b => chatId(b) === a); - return Nip4ChatSystem.createChatObj(a, messages); - }); + return [...Object.entries(chats)].map(([k, v]) => + Nip4ChatSystem.createChatObj( + encodeTLVEntries("chat4" as NostrPrefix, { + type: TLVEntryType.Author, + value: k, + length: 32, + }), + v, + ), + ); } static createChatObj(id: string, messages: Array) { const last = lastReadInChat(id); - const pk = decodeTLV(id).find(a => a.type === TLVEntryType.Author)?.value as string; + const participants = decodeTLV(id) + .filter(v => v.type === TLVEntryType.Author) + .map(v => ({ + type: "pubkey", + id: v.value as string, + })); return { type: ChatType.DirectMessage, id, unread: messages.reduce((acc, v) => (v.created_at > last ? acc++ : acc), 0), lastMessage: messages.reduce((acc, v) => (v.created_at > acc ? v.created_at : acc), 0), - participants: [ - { - type: "pubkey", - id: pk, - }, - ], + participants, messages: messages.map(m => ({ id: m.id, created_at: m.created_at, @@ -90,7 +102,7 @@ export class Nip4ChatSystem extends ExternalStore> implements ChatSy }, })), createMessage: async (msg, pub) => { - return [await pub.sendDm(msg, pk)]; + return await Promise.all(participants.map(v => pub.sendDm(msg, v.id))); }, sendMessage: (ev, system: SystemInterface) => { ev.forEach(a => system.BroadcastEvent(a)); diff --git a/packages/app/src/index.css b/packages/app/src/index.css index b9bdef1b..a038ce28 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -15,8 +15,8 @@ --live: #f83838; --heart: #ef4444; --zap: #ff710a; - --gray-superlight: #eee; + --bg-secondary: #2a2a2a; --gray-light: #999; --gray-medium: #7b7b7b; --gray: #333; @@ -42,8 +42,11 @@ ); --expired-invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, var(--gray), var(--gray-superdark)); --strike-army-gradient: linear-gradient(to bottom right, #ccff00, #a1c900); - + --sub-bg: #111; --header-padding-tb: 10px; + --btn-color: #fff; + --primary-gradient: linear-gradient(90deg, rgba(239, 150, 68, 1) 0%, rgba(123, 65, 246, 1) 100%); + --cashu-gradient: linear-gradient(90deg, #40b039, #adff2a); } ::-webkit-scrollbar { @@ -70,27 +73,27 @@ html { } html.light { - --bg-color: #f8f8f8; + --bg-color: #fff; --font-color: #2f3f64; - --font-secondary-color: #71717a; + --font-secondary-color: #5c6c92; --font-tertiary-color: #52525b; --border-color: #dee1e8; - --highlight: #7139f1; - --modal-bg-color: rgba(240, 240, 240, 0.8); - - --gray: #999; + --modal-bg-color: #0009; + --gray: #dee1e8; --gray-secondary: #aaa; --gray-tertiary: #bbb; --gray-superlight: #333; --gray-light: #555; --gray-dark: #ccc; - --gray-superdark: #ddd; - --gray-ultradark: #eee; - + --gray-superdark: #fff; + --gray-ultradark: #fff; --dm-gradient: var(--gray); --invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, #f7b73333, #fc4a1a33); --paid-invoice-gradient: linear-gradient(45deg, var(--gray-superdark) 50%, #f7b73399, #fc4a1a99); + --bg-secondary: #fff; + --sub-bg: #fff; + --btn-color: #fff; } body { @@ -130,18 +133,38 @@ code { } } +.b { + border: 1px solid var(--border-color); +} + +.bg-primary { + background: var(--primary-gradient); +} + +.br { + border-radius: 16px; +} + .p { padding: 12px 16px; } +.p24 { + padding: 24px; +} + +.uppercase { + text-transform: uppercase; +} + .card { padding: 12px 16px; - border-bottom: 1px solid var(--gray-superdark); + border-bottom: 1px solid var(--border-color); } /* Card inside card */ .card .card { - border: 1px solid var(--gray-superdark); + border: 1px solid var(--border-color); border-radius: 16px; min-height: 0; } @@ -150,6 +173,7 @@ code { display: flex; flex-direction: row; justify-content: space-between; + margin-bottom: -8px; } .card > .footer { @@ -164,16 +188,28 @@ code { button { cursor: pointer; - padding: 6px 12px; + padding: 10px 16px; font-weight: 600; color: var(--bg-color); font-size: var(--font-size); - background-color: var(--font-color); + background-color: var(--btn-color); border: none; - border-radius: 16px; + border-radius: 100px; outline: none; } +button.primary { + background: rgb(239, 150, 68); + background: var(--primary-gradient); + color: #fff !important; + border: none !important; +} + +button.primary:hover { + box-shadow: 0px 1px 4px rgba(128, 29, 255, 1) !important; + background: var(--primary-gradient) !important; +} + button:disabled { opacity: 0.3; cursor: not-allowed; @@ -187,7 +223,7 @@ button.secondary:disabled { button:disabled:hover { cursor: not-allowed; color: var(--font-color); - border-color: var(--gray-superdark); + border-color: var(--gray-border-color); } .light button.transparent { @@ -211,11 +247,7 @@ button.transparent { font-weight: 400; color: var(--font-secondary); background-color: transparent; - border: 1px solid var(--gray-superdark); -} - -.light button.secondary { - background-color: var(--gray); + border: 1px solid var(--border-color); } button.secondary:hover { @@ -229,15 +261,13 @@ button.transparent:hover { background-color: var(--font-color); } -.light button.secondary:hover { - background-color: var(--gray-secondary); -} - button.icon { border: none; - background: none; + background: var(--bg-secondary); color: var(--font-color); - min-height: 28px; + width: 40px; + height: 40px; + padding: 10px; } button.icon .icon-wrapper { @@ -261,6 +291,10 @@ button.icon:hover { display: inline-flex; } +.light .btn { + color: #64748b; +} + .btn-warn { border-color: var(--error); } @@ -303,13 +337,15 @@ input[type="password"], input[type="number"], select, textarea { - padding: 12px 16px; + padding: 8px 12px; color: var(--font-color); background: transparent; - border: 1px solid rgba(255, 255, 255, 0.1); + border: 2px solid rgba(255, 255, 255, 0.1); border-radius: 12px; outline: none; - line-height: 24px; /* 150% */ + font-size: 15px; + line-height: 24px; + /* 150% */ } .light input[type="text"], @@ -317,7 +353,7 @@ textarea { .light input[type="number"], .light select, .light textarea { - border: 1px solid rgba(0, 0, 0, 0.3); + border: 2px solid var(--border-color); } option, @@ -331,8 +367,8 @@ textarea:placeholder { } input[type="checkbox"] { - width: 24px; - height: 24px; + width: 20px; + height: 20px; } input:disabled { @@ -353,6 +389,7 @@ input:disabled { .f-center { justify-content: center; + align-items: center; } .f-1 { @@ -363,6 +400,14 @@ input:disabled { flex: 2; } +.f-3 { + flex: 3; +} + +.f-4 { + flex: 4; +} + .f-grow { flex-grow: 1; min-width: 0; @@ -397,6 +442,10 @@ input:disabled { justify-content: space-between; } +.f-wrap { + flex-flow: wrap; +} + .g2 { gap: 2px; } @@ -421,6 +470,10 @@ input:disabled { gap: 24px; } +.txt-center { + text-align: center; +} + .w-max { width: 100%; width: stretch; @@ -490,7 +543,7 @@ div.form-col { height: 100vh; } -small.xs { +.xs { font-size: small; } @@ -609,8 +662,9 @@ small.xs { .main-content h2 { font-weight: 600; - font-size: 32px; - line-height: 39px; + font-size: 26px; + line-height: 36px; + margin: 12px 0 0 0; } .main-content .h4 { @@ -618,7 +672,7 @@ small.xs { } .main-content .profile-preview { - margin-bottom: 16px; + margin: 8px 0; } button.tall { @@ -685,18 +739,24 @@ button.tall { .rta__textarea { /* Fix width calculation to account for 32px padding on input */ width: calc(100% - 32px) !important; + font-size: 15px !important; } .ctx-menu { - color: var(--font-secondary-color) !important; + color: var(--font-color) !important; background: transparent !important; min-width: 0 !important; margin: 0 !important; } +.szh-menu { + border-radius: 16px !important; + padding: 0 !important; +} + .ctx-menu li { background: #1e1e1e !important; - padding: 8px 32px 8px 32px !important; + padding: 8px 24px !important; display: grid !important; grid-template-columns: 2rem auto !important; font-size: 16px; @@ -704,7 +764,7 @@ button.tall { } .light .ctx-menu li { - background: var(--gray-superdark) !important; + background: #fff !important; } .ctx-menu li:first-of-type { @@ -712,13 +772,13 @@ button.tall { } .ctx-menu li:nth-child(3) { - padding-top: 24px !important; + padding-top: 16px !important; border-top-left-radius: 16px !important; border-top-right-radius: 16px !important; } .ctx-menu li:last-of-type { - padding-bottom: 24px !important; + padding-bottom: 16px !important; border-bottom-left-radius: 16px !important; border-bottom-right-radius: 16px !important; } @@ -733,8 +793,8 @@ button.tall { } .light .ctx-menu li:hover { - color: white !important; - background: var(--font-secondary-color) !important; + color: var(--font-color) !important; + background: #eee !important; } .ctx-menu .red { @@ -761,3 +821,54 @@ svg.heart-solid { svg.zap-solid { color: var(--zap); } + +.subtier { + background: var(--sub-bg); + border-radius: 16px; +} + +.light .subtier { + border: 1px solid var(--border-color); +} + +.light .spinner-button { + background: #fff; + border: 1px solid var(--border-color); + color: var(--font-secondary); + box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px; +} + +.light .spinner-button:hover { + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px; +} + +.main-content.p { + border-bottom: 0; + border-top: 0; +} + +.light button.icon { + border: 1px solid var(--border-color); + box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px; +} + +.light button.icon:hover { + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px; +} + +.light button, +.light button.secondary { + color: #4b5c83; + background-color: #fff; + border: 1px solid var(--border-color); + box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px; +} + +.light button:hover, +.light button.secondary:hover { + box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px !important; +} + +.light .modal button.secondary:hover { + background: #fff !important; +} diff --git a/packages/app/src/index.tsx b/packages/app/src/index.tsx index bfd248c8..316cf278 100644 --- a/packages/app/src/index.tsx +++ b/packages/app/src/index.tsx @@ -2,21 +2,30 @@ import "./index.css"; import "@szhsin/react-menu/dist/index.css"; import "./fonts/inter.css"; +import { compress, expand_filter, flat_merge, get_diff, pow, default as wasmInit } from "@snort/system-wasm"; +import WasmPath from "@snort/system-wasm/pkg/system_wasm_bg.wasm"; + import { StrictMode } from "react"; import * as ReactDOM from "react-dom/client"; -import { Provider } from "react-redux"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; -import { EventPublisher, NostrSystem, ProfileLoaderService, Nip7Signer, PowWorker } from "@snort/system"; +import { + NostrSystem, + ProfileLoaderService, + QueryOptimizer, + FlatReqFilter, + ReqFilter, + PowMiner, + NostrEvent, +} from "@snort/system"; import { SnortContext } from "@snort/system-react"; import * as serviceWorkerRegistration from "serviceWorkerRegistration"; import { IntlProvider } from "IntlProvider"; import { unwrap } from "SnortUtils"; -import Store from "State/Store"; import Layout from "Pages/Layout"; import LoginPage from "Pages/LoginPage"; import ProfilePage from "Pages/ProfilePage"; -import { RootRoutes } from "Pages/Root"; +import { RootRoutes, RootTabRoutes } from "Pages/Root"; import NotificationsPage from "Pages/Notifications"; import SettingsPage, { SettingsRoutes } from "Pages/SettingsPage"; import ErrorPage from "Pages/ErrorPage"; @@ -28,13 +37,36 @@ import HelpPage from "Pages/HelpPage"; import { NewUserRoutes } from "Pages/new"; import { WalletRoutes } from "Pages/WalletPage"; import NostrLinkHandler from "Pages/NostrLinkHandler"; -import Thread from "Element/Thread"; +import { ThreadRoute } from "Element/Event/Thread"; import { SubscribeRoutes } from "Pages/subscribe"; import ZapPoolPage from "Pages/ZapPool"; import DebugPage from "Pages/Debug"; import { db } from "Db"; import { preload, RelayMetrics, UserCache, UserRelays } from "Cache"; import { LoginStore } from "Login"; +import { SnortDeckLayout } from "Pages/DeckLayout"; + +const WasmQueryOptimizer = { + expandFilter: (f: ReqFilter) => { + return expand_filter(f) as Array; + }, + getDiff: (prev: Array, next: Array) => { + return get_diff(prev, next) as Array; + }, + flatMerge: (all: Array) => { + return flat_merge(all) as Array; + }, + compress: (all: Array) => { + return compress(all) as Array; + }, +} as QueryOptimizer; + +export class WasmPowWorker implements PowMiner { + minePow(ev: NostrEvent, target: number): Promise { + const res = pow(ev, target); + return Promise.resolve(res); + } +} /** * Singleton nostr system @@ -43,14 +75,11 @@ export const System = new NostrSystem({ relayCache: UserRelays, profileCache: UserCache, relayMetrics: RelayMetrics, + queryOptimizer: WasmQueryOptimizer, authHandler: async (c, r) => { - const { publicKey, privateKey } = LoginStore.snapshot(); - if (privateKey) { - const pub = EventPublisher.privateKey(privateKey); - return await pub.nip42Auth(c, r); - } - if (publicKey) { - const pub = new EventPublisher(new Nip7Signer(), publicKey); + const { id } = LoginStore.snapshot(); + const pub = LoginStore.getPublisher(id); + if (pub) { return await pub.nip42Auth(c, r); } }, @@ -61,37 +90,49 @@ export const System = new NostrSystem({ */ export const ProfileLoader = new ProfileLoaderService(System, UserCache); -/** - * Singleton POW worker - */ -export const DefaultPowWorker = new PowWorker("/pow.js"); - serviceWorkerRegistration.register(); +async function initSite() { + await wasmInit(WasmPath); + const login = LoginStore.takeSnapshot(); + db.ready = await db.isAvailable(); + if (db.ready) { + await preload(login.follows.item); + } + + for (const [k, v] of Object.entries(login.relays.item)) { + System.ConnectToRelay(k, v); + } + try { + if ("registerProtocolHandler" in window.navigator) { + window.navigator.registerProtocolHandler("web+nostr", `${window.location.protocol}//${window.location.host}/%s`); + console.info("Registered protocol handler for 'web+nostr'"); + } + } catch (e) { + console.error("Failed to register protocol handler", e); + } + + // inject analytics script + // + if (login.preferences.telemetry ?? true) { + const sc = document.createElement("script"); + sc.src = "https://analytics.v0l.io/js/script.js"; + sc.defer = true; + sc.setAttribute("data-domain", "snort.social"); + document.head.appendChild(sc); + } + return null; +} + +let didInit = false; export const router = createBrowserRouter([ { element: , errorElement: , loader: async () => { - const login = LoginStore.takeSnapshot(); - db.ready = await db.isAvailable(); - if (db.ready) { - await preload(login.follows.item); - } - - for (const [k, v] of Object.entries(login.relays.item)) { - System.ConnectToRelay(k, v); - } - try { - if ("registerProtocolHandler" in window.navigator) { - window.navigator.registerProtocolHandler( - "web+nostr", - `${window.location.protocol}//${window.location.host}/%s` - ); - console.info("Registered protocol handler for 'web+nostr'"); - } - } catch (e) { - console.error("Failed to register protocol handler", e); + if (!didInit) { + didInit = true; + return await initSite(); } return null; }, @@ -107,7 +148,7 @@ export const router = createBrowserRouter([ }, { path: "/e/:id", - element: , + element: , }, { path: "/p/:id", @@ -155,17 +196,27 @@ export const router = createBrowserRouter([ }, ], }, + { + path: "/deck", + element: , + loader: async () => { + if (!didInit) { + didInit = true; + return await initSite(); + } + return null; + }, + children: RootTabRoutes, + }, ]); const root = ReactDOM.createRoot(unwrap(document.getElementById("root"))); root.render( - - - - - - - - + + + + + + , ); diff --git a/packages/app/src/lang.json b/packages/app/src/lang.json index 53f35850..7b59ae7b 100644 --- a/packages/app/src/lang.json +++ b/packages/app/src/lang.json @@ -11,6 +11,9 @@ "+aZY2h": { "defaultMessage": "Zap Type" }, + "+tShPg": { + "defaultMessage": "following" + }, "+vA//S": { "defaultMessage": "Logins" }, @@ -36,6 +39,9 @@ "/RD0e2": { "defaultMessage": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content." }, + "/Xf4UW": { + "defaultMessage": "Send anonymous usage metrics" + }, "/d6vEc": { "defaultMessage": "Make your profile easier to find and share" }, @@ -60,6 +66,9 @@ "0mch2Y": { "defaultMessage": "name has disallowed characters" }, + "0uoY11": { + "defaultMessage": "Show Status" + }, "0yO7wF": { "defaultMessage": "{n} secs" }, @@ -105,6 +114,9 @@ "2ukA4d": { "defaultMessage": "{n} hours" }, + "3KNMbJ": { + "defaultMessage": "Articles" + }, "3Rx6Qo": { "defaultMessage": "Advanced" }, @@ -130,6 +142,9 @@ "3yk8fB": { "defaultMessage": "Wallet" }, + "40VR6s": { + "defaultMessage": "Nostr Connect" + }, "450Fty": { "defaultMessage": "None" }, @@ -154,6 +169,12 @@ "4rYCjn": { "defaultMessage": "Note to Self" }, + "5BVs2e": { + "defaultMessage": "zap" + }, + "5CB6zB": { + "defaultMessage": "Zap Splits" + }, "5JcXdV": { "defaultMessage": "Create Account" }, @@ -172,12 +193,24 @@ "5ykRmX": { "defaultMessage": "Send zap" }, + "6/SF6e": { + "defaultMessage": "

{n}

Cashu sats" + }, + "6/hB3S": { + "defaultMessage": "Watch Replay" + }, "65BmHb": { "defaultMessage": "Failed to proxy image from {host}, click here to load directly" }, + "6OSOXl": { + "defaultMessage": "Reason: {reason}" + }, "6Yfvvp": { "defaultMessage": "Get an identifier" }, + "6bgpn+": { + "defaultMessage": "Not all clients support this, you may still receive some zaps as if zap splits was not configured" + }, "6ewQqw": { "defaultMessage": "Likes ({n})" }, @@ -193,24 +226,34 @@ "7hp70g": { "defaultMessage": "NIP-05" }, - "7xzTiH": { - "defaultMessage": "{action} to {target}" - }, "8/vBbP": { "defaultMessage": "Reposts ({n})" }, "89q5wc": { "defaultMessage": "Confirm Reposts" }, + "8Kboo2": { + "defaultMessage": "Scan this QR code with your signer app to get started" + }, "8QDesP": { "defaultMessage": "Zap {n} sats" }, + "8Rkoyb": { + "defaultMessage": "Recipient" + }, + "8Y6bZQ": { + "defaultMessage": "Invalid zap split: {input}" + }, "8g2vyB": { "defaultMessage": "name too long" }, "8v1NN+": { "defaultMessage": "Pairing phrase" }, + "8xNnhi": { + "defaultMessage": "Nostr Extension", + "description": "Login button for NIP7 key manager extension" + }, "9+Ddtu": { "defaultMessage": "Next" }, @@ -240,6 +283,9 @@ "AGNz71": { "defaultMessage": "Zap All {n} sats" }, + "AN0Z7Q": { + "defaultMessage": "Muted Words" + }, "ASRK0S": { "defaultMessage": "This author has been muted" }, @@ -283,6 +329,9 @@ "BcGMo+": { "defaultMessage": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages." }, + "C1LjMx": { + "defaultMessage": "Lightning Donation" + }, "C5xzTC": { "defaultMessage": "Premium" }, @@ -325,6 +374,9 @@ "Dh3hbq": { "defaultMessage": "Auto Zap" }, + "Dn82AL": { + "defaultMessage": "Live" + }, "DtYelJ": { "defaultMessage": "Transfer" }, @@ -337,6 +389,9 @@ "EPYwm7": { "defaultMessage": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key." }, + "EQKRE4": { + "defaultMessage": "Show badges on profile pages" + }, "EWyQH5": { "defaultMessage": "Global" }, @@ -349,6 +404,9 @@ "EcglP9": { "defaultMessage": "Key" }, + "EjFyoR": { + "defaultMessage": "On-chain Donation Address" + }, "EnCOBJ": { "defaultMessage": "Buy" }, @@ -364,8 +422,8 @@ "FDguSC": { "defaultMessage": "{n} Zaps" }, - "FP+D3H": { - "defaultMessage": "LNURL to forward zaps to" + "FMfjrl": { + "defaultMessage": "Show status messages on profile pages" }, "FS3b54": { "defaultMessage": "Done!" @@ -394,6 +452,9 @@ "GL8aXW": { "defaultMessage": "Bookmarks ({n})" }, + "GQPtfk": { + "defaultMessage": "Join Stream" + }, "GSye7T": { "defaultMessage": "Lightning Address" }, @@ -406,6 +467,9 @@ "GspYR7": { "defaultMessage": "{n} Dislike" }, + "Gxcr08": { + "defaultMessage": "Broadcast Event" + }, "H+vHiz": { "defaultMessage": "Hex Key..", "description": "Hexidecimal 'key' input for improxy" @@ -419,9 +483,6 @@ "HAlOn1": { "defaultMessage": "Name" }, - "HF4YnO": { - "defaultMessage": "Watch Live!" - }, "HFls6j": { "defaultMessage": "name will be available later" }, @@ -434,6 +495,9 @@ "HbefNb": { "defaultMessage": "Open Wallet" }, + "HhcAVH": { + "defaultMessage": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody." + }, "IDjHJ6": { "defaultMessage": "Thanks for using Snort, please consider donating if you can." }, @@ -452,6 +516,9 @@ "Ig9/a1": { "defaultMessage": "Sent {n} sats to {name}" }, + "IoQq+a": { + "defaultMessage": "Click here to load anyway" + }, "Ix8l+B": { "defaultMessage": "Trending Notes" }, @@ -461,6 +528,9 @@ "JCIgkj": { "defaultMessage": "Username" }, + "JGrt9q": { + "defaultMessage": "Send sats to {name}" + }, "JHEHCk": { "defaultMessage": "Zaps ({n})" }, @@ -488,9 +558,6 @@ "KAhAcM": { "defaultMessage": "Enter LNDHub config" }, - "KLo3SP": { - "defaultMessage": "Reason: {reason}" - }, "KQvWvD": { "defaultMessage": "Deleted" }, @@ -503,9 +570,15 @@ "KoFlZg": { "defaultMessage": "Enter mint URL" }, + "KtsyO0": { + "defaultMessage": "Enter Pin" + }, "LF5kYT": { "defaultMessage": "Other Connections" }, + "LR1XjT": { + "defaultMessage": "Pin too short" + }, "LXxsbk": { "defaultMessage": "Anonymous" }, @@ -518,6 +591,13 @@ "Lw+I+J": { "defaultMessage": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}" }, + "LwYmVi": { + "defaultMessage": "Zaps on this note will be split to the following users." + }, + "M10zFV": { + "defaultMessage": "Nostr Connect", + "description": "Login button for NIP-46 signer app" + }, "M3Oirc": { "defaultMessage": "Debug Menus" }, @@ -549,6 +629,9 @@ "N2IrpM": { "defaultMessage": "Confirm" }, + "NAidKb": { + "defaultMessage": "Notifications" + }, "NAuFNH": { "defaultMessage": "You already have a subscription of this type, please renew or pay" }, @@ -585,9 +668,6 @@ "ORGv1Q": { "defaultMessage": "Created" }, - "P04gQm": { - "defaultMessage": "All zaps sent to this note will be received by the following LNURL" - }, "P61BTu": { "defaultMessage": "Copy Event JSON" }, @@ -631,9 +711,6 @@ "R/6nsx": { "defaultMessage": "Subscription" }, - "R1fEdZ": { - "defaultMessage": "Forward Zaps" - }, "R81upa": { "defaultMessage": "People you follow" }, @@ -663,16 +740,15 @@ "defaultMessage": "Sort", "description": "Label for sorting options for people search" }, + "SMO+on": { + "defaultMessage": "Send zap to {name}" + }, "SOqbe9": { "defaultMessage": "Update Lightning Address" }, "SP0+yi": { "defaultMessage": "Buy Subscription" }, - "SX58hM": { - "defaultMessage": "Copy", - "description": "Button: Copy Cashu token" - }, "SYQtZ7": { "defaultMessage": "LN Address Proxy" }, @@ -685,11 +761,14 @@ "Ss0sWu": { "defaultMessage": "Pay Now" }, + "StKzTE": { + "defaultMessage": "The author has marked this note as a sensitive topic" + }, "TDR5ge": { "defaultMessage": "Media in notes will automatically be shown for selected people, otherwise only the link will show" }, - "TMfYfY": { - "defaultMessage": "Cashu token" + "TP/cMX": { + "defaultMessage": "Ended" }, "TpgeGw": { "defaultMessage": "Hex Salt..", @@ -701,9 +780,6 @@ "UDYlxu": { "defaultMessage": "Pending Subscriptions" }, - "ULotH9": { - "defaultMessage": "Amount: {amount} sats" - }, "UT7Nkj": { "defaultMessage": "New Chat" }, @@ -731,10 +807,6 @@ "VnXp8Z": { "defaultMessage": "Avatar" }, - "VtPV/B": { - "defaultMessage": "Login with Extension (NIP-07)", - "description": "Login button for NIP7 key manager extension" - }, "VvaJst": { "defaultMessage": "View Wallets" }, @@ -753,12 +825,18 @@ "WONP5O": { "defaultMessage": "Find your twitter follows on nostr (Data provided by {provider})" }, + "WvGmZT": { + "defaultMessage": "npub / nprofile / nostr address" + }, "WxthCV": { "defaultMessage": "e.g. Jack" }, "X7xU8J": { "defaultMessage": "nsec, npub, nip-05, hex, mnemonic" }, + "XECMfW": { + "defaultMessage": "Send usage metrics" + }, "XICsE8": { "defaultMessage": "File hosts" }, @@ -796,8 +874,8 @@ "ZLmyG9": { "defaultMessage": "Contributors" }, - "ZUZedV": { - "defaultMessage": "Lightning Donation:" + "ZS+jRE": { + "defaultMessage": "Send zap splits to" }, "Zr5TMx": { "defaultMessage": "Setup profile" @@ -817,6 +895,9 @@ "b5vAk0": { "defaultMessage": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address" }, + "bLZL5a": { + "defaultMessage": "Get Address" + }, "bQdA2k": { "defaultMessage": "Sensitive Content" }, @@ -836,6 +917,9 @@ "defaultMessage": "Install Extension", "description": "Heading for install key manager extension" }, + "c2DTVd": { + "defaultMessage": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort." + }, "c35bj2": { "defaultMessage": "If you have an enquiry about your NIP-05 order please DM {link}" }, @@ -880,6 +964,9 @@ "e61Jf3": { "defaultMessage": "Coming soon" }, + "e7VmYP": { + "defaultMessage": "Enter pin to unlock your private key" + }, "e7qqly": { "defaultMessage": "Mark All Read" }, @@ -910,6 +997,9 @@ "flnGvv": { "defaultMessage": "What's on your mind?" }, + "fqwcJ1": { + "defaultMessage": "On-chain Donation" + }, "fsB/4p": { "defaultMessage": "Saved" }, @@ -949,16 +1039,15 @@ "hMzcSq": { "defaultMessage": "Messages" }, - "hWSp+B": { - "defaultMessage": "Nostr Connect (NIP-46)", - "description": "Login button for NIP-46 signer app" - }, "hY4lzx": { "defaultMessage": "Supports" }, "hicxcO": { "defaultMessage": "Show replies" }, + "hmZ3Bz": { + "defaultMessage": "Media" + }, "hniz8Z": { "defaultMessage": "here" }, @@ -980,9 +1069,6 @@ "iNWbVV": { "defaultMessage": "Handle" }, - "iUsU2x": { - "defaultMessage": "Mint: {url}" - }, "iXPL0Z": { "defaultMessage": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead" }, @@ -1167,6 +1253,9 @@ "qdGuQo": { "defaultMessage": "Your Private Key Is (do not share this with anyone)" }, + "qfmMQh": { + "defaultMessage": "This note has been muted" + }, "qkvYUb": { "defaultMessage": "Add to Profile" }, @@ -1176,6 +1265,9 @@ "qtWLmt": { "defaultMessage": "Like" }, + "qz9fty": { + "defaultMessage": "Incorrect pin" + }, "r3C4x/": { "defaultMessage": "Software" }, @@ -1200,9 +1292,18 @@ "rudscU": { "defaultMessage": "Failed to load follows, please try again later" }, + "sKDn4e": { + "defaultMessage": "Show Badges" + }, + "sUNhQE": { + "defaultMessage": "user" + }, "sWnYKw": { "defaultMessage": "Snort is designed to have a similar experience to Twitter." }, + "sZQzjQ": { + "defaultMessage": "Failed to parse zap split: {input}" + }, "svOoEH": { "defaultMessage": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule." }, @@ -1224,12 +1325,12 @@ "u4bHcR": { "defaultMessage": "Check out the code here: {link}" }, - "uD/N6c": { - "defaultMessage": "Zap {target} {n} sats" - }, "uSV4Ti": { "defaultMessage": "Reposts need to be manually confirmed" }, + "uc0din": { + "defaultMessage": "Send sats splits to" + }, "usAvMr": { "defaultMessage": "Edit Profile" }, @@ -1242,9 +1343,6 @@ "vOKedj": { "defaultMessage": "{n,plural,=1{& {n} other} other{& {n} others}}" }, - "vU71Ez": { - "defaultMessage": "Paying with {wallet}" - }, "vZ4quW": { "defaultMessage": "NIP-05 is a DNS based verification spec which helps to validate you as a real user." }, @@ -1267,6 +1365,9 @@ "defaultMessage": "Your key", "description": "Label for key input" }, + "wSZR47": { + "defaultMessage": "Submit" + }, "wWLwvh": { "defaultMessage": "Anon", "description": "Anonymous Zap" @@ -1277,6 +1378,9 @@ "wih7iJ": { "defaultMessage": "name is blocked" }, + "wofVHy": { + "defaultMessage": "Moderation" + }, "wqyN/i": { "defaultMessage": "Find out more info about {service} at {link}" }, @@ -1286,12 +1390,12 @@ "x/Fx2P": { "defaultMessage": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!" }, - "x/q8d5": { - "defaultMessage": "This note has been marked as sensitive, click here to reveal" - }, "x82IOl": { "defaultMessage": "Mute" }, + "xIcAOU": { + "defaultMessage": "Votes by {type}" + }, "xIoGG9": { "defaultMessage": "Go to" }, @@ -1327,6 +1431,9 @@ "defaultMessage": "Read global from", "description": "Label for reading global feed from specific relays" }, + "zCb8fX": { + "defaultMessage": "Weight" + }, "zFegDD": { "defaultMessage": "Contact" }, @@ -1347,5 +1454,8 @@ }, "zvCDao": { "defaultMessage": "Automatically show latest notes" + }, + "zwb6LR": { + "defaultMessage": "Mint: {url}" } } diff --git a/packages/app/src/translations/af_ZA.json b/packages/app/src/translations/af_ZA.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/af_ZA.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/ar_SA.json b/packages/app/src/translations/ar_SA.json index 05105187..7be46b80 100644 --- a/packages/app/src/translations/ar_SA.json +++ b/packages/app/src/translations/ar_SA.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "تخصيص الومضة", + "+tShPg": "following", "+vA//S": "Logins", "+vIQlC": "يرجى التأكد من حفظ كلمة المرور لتتمكن من إدارة المعرّف الخاص بك في المستقبل", "+vVZ/G": "اتصال", @@ -11,6 +12,7 @@ "/JE/X+": "دعم الحساب", "/PCavi": "علنية", "/RD0e2": "يستخدم نوستر تقنية التوقيع الرقمي لنشر المنشورات دون أي تلاعب ويمكن إرسالها بأمان إلى العديد من الموصلات لتكرار تخزينها وضمان استمرارية المحتوى الخاص بك.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "اجعل ملفك الشخصي أسهل في العثور عليه ومشاركته", "/n5KSF": "{n} مللي ثانية", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "بحث...", "0jOEtS": "عنوان LNURL غير صالح", "0mch2Y": "الاسم يحتوي على أحرف غير مسموح بها", + "0uoY11": "Show Status", "0yO7wF": "{n} ثانية", "1A7TZk": "ما هو سنورت وكيف يعمل؟", "1Mo59U": "هل أنت متأكد من حذف هذا المنشور من المنشورات المرجعية؟", @@ -34,6 +37,7 @@ "2a2YiP": "المنشورات المرجعية {n}", "2k0Cv+": "الاستهجان ({n})", "2ukA4d": "{n} ساعات", + "3KNMbJ": "Articles", "3Rx6Qo": "خيارات متقدمة", "3cc4Ct": "فاتح", "3gOsZq": "المترجمون", @@ -42,6 +46,7 @@ "3tVy+Z": "المتابِعون {n}", "3xCwbZ": "أو", "3yk8fB": "المحفظة", + "40VR6s": "Nostr Connect", "450Fty": "لا أحد", "47FYwb": "الغاء", "4IPzdn": "المطورون الأساسيون", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs هي من أوائل مزودي NIP-05 وتقدم مجموعة من المعرفات بأسعار معقولة", "4Z3t5i": "استخدم imgproxy لضغط الصور", "4rYCjn": "ملاحظة خاصة", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Create Account", "5oTnfy": "شراء معرف", "5rOdPG": "بمجرد إعداد ملحق مدير المفاتيح الخاص بك وإنشاء مفتاح، يمكنك المضي لإعداد ملفك الشخصي وسنساعدك في العثور على بعض الحسابات التي قد تثير اهتمامك على نوستر.", "5u6iEc": "تحويل الى عنوان عام", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "أرسل ومضة", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Get an identifier", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "الإعجابات ({n})", "6uMqL1": "غير مدفوع", "7+Domh": "منشورات", "7BX/yC": "Account Switcher", "7hp70g": "NIP-05", - "7xzTiH": "{action} إلى {target}", "8/vBbP": "إعادة النشر ({n})", "89q5wc": "تأكيد إعادة النشر", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "أومض {n} ساتوشي", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "الاسم طويل جدا", "8v1NN+": "عبارة الاقتران", + "8xNnhi": "Nostr Extension", "9+Ddtu": "التالي", "9HU8vw": "رد", "9SvQep": "يتابع {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "فاتورة البرق", "ADmfQT": "السياق", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "تم كتم هذا المستخدم", "Adk34V": "قم بإعداد ملف التعريف الخاص بك", "Ai8VHU": "ابقاء المنشورات لمدة غير محدودة في موصل سنورت", @@ -92,6 +107,7 @@ "BOr9z/": "سنورت هو مشروع مفتوح المصدر أنشأه أشخاص هواة في أوقات فراغهم", "BWpuKl": "تحديث", "BcGMo+": "تحتوي المنشورات على محتوى نصي ، والاستخدام الأكثر شيوعًا لهذه المنشورات هو تخزين رسائل تشابه التغريدات.", + "C1LjMx": "Lightning Donation", "C5xzTC": "متميز", "C81/uG": "تسجيل الخروج", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "عرض أحدث منشورات {n}", "DcL8P+": "داعم", "Dh3hbq": "الومض التلقائي", + "Dn82AL": "Live", "DtYelJ": "تحويل", "E8a4yq": "تابع بعض الحسابات المشهورة", "ELbg9p": "Data Providers", "EPYwm7": "مفتاحك الخاص هو كلمة مرورك. إذا فقدت هذا المفتاح ، فستفقد الوصول إلى حسابك! انسخه واحتفظ به في مكان آمن. لا توجد طريقة لإعادة تعيين مفتاحك الخاص.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "عام", "Ebl/B2": "ترجمة إلى {lang}", "EcZF24": "Custom Relays", "EcglP9": "مفتاح", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "شراء", "Eqjl5K": "فقط سنورت وشركاؤنا المعتمدون يمنحون معرفات ملونة. ولكن يمكنك استخدام مقدمي خدمة آخرون.", "F+B3x1": "لقد دخلنا أيضًا في شراكة مع nostrplebs.com لمنحك المزيد من الخيارات", "F3l7xL": "Add Account", "FDguSC": "وميض {n}", - "FP+D3H": "عنوان LNURL لتحويل الوميض إليه", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "تم!", "FSYL8G": "Trending Users", "FdhSU2": "احجز الان", @@ -129,28 +148,32 @@ "G1BGCg": "اختر محفظة", "GFOoEE": "ملح", "GL8aXW": "المنشورات المرجعية ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "احصل على عنوانك لسنورت", "Gcn9NQ": "رابط مغناطيسي", "GspYR7": "{n} استهجان", + "Gxcr08": "Broadcast Event", "H+vHiz": "مفتاح بصيغة سداسية عشرية ..", "H0JBH6": "تسجيل الخروج", "H6/kLh": "الطلب مدفوع!", "HAlOn1": "اسم المستخدم", - "HF4YnO": "Watch Live!", "HFls6j": "سيكون الاسم متاحًا لاحقًا", "HOzFdo": "كتم", "HWbkEK": "Clear cache and reload", "HbefNb": "افتح المحفظة", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "شكرا لاستخدام سنورت، يرجى التفكير في الدعم إذا تستطيع.", "IEwZvs": "هل أنت متأكد من ازالة تثبيت هذا المنشور؟", "IKKHqV": "Follows", "INSqIz": "اسم مستخدم تويتر...", "IUZC+0": "هذا يعني أنه لا يمكن لأي شخص تعديل المنشورات التي قمت بإنشائها ويمكن للجميع بسهولة التحقق من أن المنشورات التي يقرؤونها تم انشاؤها من حسابك.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "الإشتراكات", "JCIgkj": "اسم المستخدم", + "JGrt9q": "Send sats to {name}", "JHEHCk": "وميض ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "مسح", "K7AkdL": "عرض", "KAhAcM": "أدخل معلومات التهيئة لـ LNDHub", - "KLo3SP": "السبب:{reason}", "KQvWvD": "تم الحذف", "KWuDfz": "لقد حفظت مفاتيحي ، استمرار", "KahimY": "نوع الحدث غير معروف: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "اتصالات أخرى", + "LR1XjT": "Pin too short", "LXxsbk": "هوية مخفية", "LgbKvU": "تعليق", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "قوائم التصحيح", "MBAYRO": "عرض خيار \"نسخ المعرف\" و \"النسخ بصيغة JSON\" في قائمة الخيارات المنسدلة لكل منشور", "MI2jkA": "غير متاح:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "شراء {item}", "N2IrpM": "تأكيد", + "NAidKb": "Notifications", "NAuFNH": "لديك فعلا اشتراك من هذا النوع، يرجى التجديد أو الدفع", "NNSu3d": "Import Twitter Follows", "NdOYJJ": "حسنًا ، لا شيء هنا .. ألق نظرة على {newUsersPage} لمتابعة بعض الحسابات الموصى بها!", @@ -192,7 +219,6 @@ "OLEm6z": "خطأ غير معروف أثناء تسجيل الدخول", "OQXnew": "اشتراكك لا يزال نشطا، لا يمكنك التجديد الآن", "ORGv1Q": "تم الإنشاء", - "P04gQm": "جميع الومضات المرسلة إلى هذا المنشور ستستلم في عنوان LNURL التالي", "P61BTu": "نسخ بصيغة JSON", "P7FD0F": "مطابق لاعدادات جهازك", "P7nJT9": "الإجمالي اليوم (بالتوقيت العالمي ): {amount} ساتوشي", @@ -207,7 +233,6 @@ "QxCuTo": "الفن بواسطة {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Subscription", - "R1fEdZ": "تحويل الوميض", "R81upa": "People you follow", "RDZVQL": "فحص", "RahCRH": "منتهي الصلاحية", @@ -217,19 +242,19 @@ "RoOyAh": "موصّلات", "Rs4kCE": "المنشورات المرجعية", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "تحديث عنوان البرق", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "LN Address Proxy", "ShdEie": "Mark all read", "Sjo1P4": "مخصص", "Ss0sWu": "ادفع الآن", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "الاشتراكات المعلقة", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "سيضطر المستخدمون على قبول التحذير المعروض على منشورك قبل عرضه.", "Up5U7K": "حظر", @@ -239,15 +264,16 @@ "VR5eHw": "مفتاح عام (npub/nprofile)", "VlJkSk": "تم كتم {n}", "VnXp8Z": "صورة الحساب ", - "VtPV/B": "تسجيل الدخول مع ملحق (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "كيف تعمل المفاتيح؟", "W1yoZY": "يبدو أنك غير مشترك، يمكنك الاشتراك من هنا {link}", "W2PiAr": "المحظورون {n}", "W9355R": "رفع الكتم", "WONP5O": "ابحث عن من تتابع على تويتر هنا على نوستر (البيانات مقدمة من {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "على سبيل المثال جاك", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "تفاعل", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "أدخل عبارة الاقتران", "ZKORll": "نشط الآن", "ZLmyG9": "المساهمون", - "ZUZedV": "تبرع البرق:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "ادعم المطورين والمنصات التي تقدم خدمات التحقق من NIP-05", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "عرض المزيد", "b12Goz": "Mnemonic", "b5vAk0": "سيكون معرفك بمثابة عنوان برق وستتم اعادة التوجيه الى عنوان البرق أو LNURL الذي تختاره", + "bLZL5a": "Get Address", "bQdA2k": "محتوى حساس", "bep9C3": "Public Key", "bfvyfs": "Anon", "brAXSu": "اختر اسم مستخدم", "bxv59V": "الآن", "c+oiJe": "تثبيت الإضافة", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "إذا كان لديك استفسار حول طلب NIP-05 الخاص بك ، فيرجى مراسلة {link} على الخاص", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "عنوان البرق", "dOQCL8": "اسم العرض", "e61Jf3": "قريباً", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "تمت قراءة الكل", "eHAneD": "رد فعل تعبيري", "eJj8HD": "توثيق الحساب", @@ -297,6 +326,7 @@ "fWZYP5": "مثبّت", "filwqD": "يقرأ", "flnGvv": "ما الذي يدور في ذهنك؟", + "fqwcJ1": "On-chain Donation", "fsB/4p": "تم الحفظ", "g5pX+a": "نبذة", "g985Wp": "فشل إرسال الصوت", @@ -310,9 +340,9 @@ "h8XMJL": "الأوسمة", "hK5ZDk": "العالم", "hMzcSq": "رسائل", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "يدعم", "hicxcO": "عرض الردود", + "hmZ3Bz": "Media", "hniz8Z": "هنا", "i/dBAR": "Zap Pool", "iCqGww": "التفاعل ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "ترجمة DeepL", "iGT1eE": "منع الحسابات المزيفة من انتحال حسابك", "iNWbVV": "معرف", - "iUsU2x": "Mint: {url}", "iXPL0Z": "لا يمكن تسجيل الدخول باستخدام المفتاح الخاص خلال اتصال غير آمن، الرجاء استخدم إضافة مدير مفاتيح نوستر بدلاً من ذلك", "ieGrWo": "متابعة", "itPgxd": "الملف التعريفي", @@ -381,9 +410,11 @@ "qMx1sA": "قيمة الومضة الافتراضية", "qUJTsT": "محظور", "qdGuQo": "مفتاحك الخاص هو (لا تشارك هذا مع أي شخص)", + "qfmMQh": "This note has been muted", "qkvYUb": "أضف إلى الملف الشخصي", "qmJ8kD": "فشلت الترجمة", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "برنامج", "r5srDR": "أدخل كلمة مرور المحفظة", "rT14Ow": "إضافة موصّلات", @@ -392,7 +423,10 @@ "rmdsT4": "{n} أيام", "rrfdTe": "هذه نفس التقنية التي تستخدم في البتكوين وثبت أنها آمنة للغاية.", "rudscU": "فشل تحميل ما يلي ، يرجى المحاولة مرة أخرى في وقت لاحق", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "تم تصميم سنورت للحصول على تجربة مماثلة لتويتر.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "غير مسموح بانتحال الأسماء والشخصيات. يحتفظ سنورت وشركاؤنا بالحق في الغاء معرّف التوثيق الخاص بك (وليس حسابك - لا يمكن لأحد أن يفعل ذلك) لانتهاك هذه القاعدة.", "tOdNiY": "داكن", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "وسام الداعم", "u/vOPu": "مدفوع", "u4bHcR": "تحقق من الكود هنا: {link}", - "uD/N6c": "ومّض {target} {n} ساتوشي", "uSV4Ti": "إعادة النشر تحتاج إلى تأكيد يدويًا", + "uc0din": "Send sats splits to", "usAvMr": "تعديل الملف الشخصي", "ut+2Cd": "احصل على معرف الشريك", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{و{n} آخر}other{و{n} آخرون}}", - "vU71Ez": "الدفع باستخدام {wallet}", "vZ4quW": "NIP-05 هو أحد طرق التحقق المستندة إلى DNS والتي تساعد في التحقق من هويتك كمستخدم حقيقي.", "vhlWFg": "خيارات الاستطلاع", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "تحرير", "wLtRCF": "مفتاحك", + "wSZR47": "Submit", "wWLwvh": "هوية مخفية", "wYSD2L": "Nostr Adddress", "wih7iJ": "الاسم محظور", + "wofVHy": "Moderation", "wqyN/i": "اكتشف المزيد من المعلومات حول {service} على {link}", "wtLjP6": "نسخ المعرف", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "تم وضع علامة على هذه الملاحظة على أنها حساسة، انقر هنا للعرض", "x82IOl": "كتم", + "xIcAOU": "Votes by {type}", "xIoGG9": "اذهب إلى", "xJ9n2N": "مفتاحك العام", "xKflGN": "يتابع {username} على نوستر", @@ -433,11 +468,13 @@ "y1Z3or": "اللغة", "yCLnBC": "LNURL أو عنوان برق", "yCmnnm": "قراءة العام من", + "zCb8fX": "Weight", "zFegDD": "تواصل", "zINlao": "مالك", "zQvVDJ": "الكل", "zcaOTs": "ومّض ساتوشي", "zjJZBd": "انت جاهز!", "zonsdq": "فشل تحميل خدمة LNURL", - "zvCDao": "تظهر تلقائيا أحدث الملاحظات" + "zvCDao": "تظهر تلقائيا أحدث الملاحظات", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/az_AZ.json b/packages/app/src/translations/az_AZ.json index 706a5f87..e3419e50 100644 --- a/packages/app/src/translations/az_AZ.json +++ b/packages/app/src/translations/az_AZ.json @@ -3,6 +3,7 @@ "+PzQ9Y": "İndi Ödəniş Edin", "+Vxixo": "Gizli qrup söhbəti", "+aZY2h": "Zap Növü", + "+tShPg": "following", "+vA//S": "Girişlər", "+vIQlC": "Gələcəkdə hesabınızı idarə etmək üçün aşağıdakı parolu yadda saxladığınızdan əmin olun", "+vVZ/G": "Qoşul", @@ -11,6 +12,7 @@ "/JE/X+": "Hesab Dəstəyi", "/PCavi": "İctimai", "/RD0e2": "Nostr, məzmununuzun lazımsız saxlanmasını təmin etmək üçün bir çox relelərə təhlükəsiz şəkildə təkrarlana bilən saxta qeydləri təmin etmək üçün rəqəmsal imza texnologiyasından istifadə edir.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Profilinizi tapmağı və paylaşmağı asanlaşdırın", "/n5KSF": "{n} ms", "00LcfG": "Daha çox", @@ -19,6 +21,7 @@ "0BUTMv": "Axtar...", "0jOEtS": "Yanlış LNURL", "0mch2Y": "adda icazə verilməyən simvollar var", + "0uoY11": "Show Status", "0yO7wF": "{n} saniyə", "1A7TZk": "Snort nədir və necə işləyir?", "1Mo59U": "Bu qeydi əlfəcinlərdən silmək istədiyinizə əminsiniz?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Əlfəcinlər", "2k0Cv+": "Bəyənməyənlər ({n})", "2ukA4d": "{n} saat", + "3KNMbJ": "Articles", "3Rx6Qo": "İnkişaf etmiş", "3cc4Ct": "İşıq", "3gOsZq": "Tərcüməçilər", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} İzləyicilər", "3xCwbZ": "Və ya", "3yk8fB": "Pulqabı", + "40VR6s": "Nostr Connect", "450Fty": "Heç biri", "47FYwb": "Ləğv et", "4IPzdn": "Əsas Tərtibatçılar", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs məkanda ilk NIP-05 provayderlərindən biridir və münasib qiymətlərlə yaxşı domen kolleksiyası təklif edir", "4Z3t5i": "Şəkilləri sıxmaq üçün imgproxy istifadə edin", "4rYCjn": "Özünə Qeyd", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Hesab yaradın", "5oTnfy": "Buy Handle", "5rOdPG": "Açar menecer artırmanızı quraşdırdıqdan və açar yaratdıqdan sonra profilinizi qurmaq və izləmək üçün Nostr-da bəzi maraqlı insanları tapmaqda sizə kömək etmək üçün yeni istifadəçilər axınımızı izləyə bilərsiniz.", "5u6iEc": "Pubkey-ə köçürün", "5vMmmR": "İstifadəçi adları Nostr-da unikal deyil. Nostr ünvanı qeydiyyatdan keçdiyiniz zaman sizin üçün unikal olan, insan tərəfindən oxuna bilən unikal ünvanınızdır.", "5ykRmX": "Zap göndər", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "{host} proksi şəklini yükləmək alınmadı, birbaşa yükləmək üçün bura klikləyin", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "İdentifikator alın", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Bəyənmələr ({n})", "6uMqL1": "Ödənilməmiş", "7+Domh": "Qeydlər", "7BX/yC": "Hesab dəyişdiricisi", "7hp70g": "NIP-05", - "7xzTiH": "{action} to {target}", "8/vBbP": "Repostlar ({n})", "89q5wc": "Repostları təsdiqlə", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "ad çox uzun", "8v1NN+": "Pairing phrase", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Növbəti", "9HU8vw": "Cavabla", "9SvQep": "{n} izləyir", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning fakturası", "ADmfQT": "Valideyn", "AGNz71": "Zap bütün {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "This author has been muted", "Adk34V": "Profilinizi qurun", "Ai8VHU": "Snort relayında limitsiz qeyd saxlama", @@ -90,10 +105,11 @@ "BGCM48": "Write access to Snort relay, with 1 year of event retention", "BOUMjw": "No nostr users found for {twitterUsername}", "BOr9z/": "Snort is an open source project built by passionate people in their free time", - "BWpuKl": "Update", + "BWpuKl": "Yenilə", "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", - "C81/uG": "Logout", + "C81/uG": "Çıxış", "C8HhVE": "Suggested Follows", "CHTbO3": "Failed to load invoice", "CVWeJ6": "Trending People", @@ -106,20 +122,23 @@ "DZzCem": "Show latest {n} notes", "DcL8P+": "Supporter", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transfer", "E8a4yq": "Follow some popular accounts", "ELbg9p": "Data Providers", "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Translate to {lang}", "EcZF24": "Custom Relays", "EcglP9": "Key", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Buy", "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", "F3l7xL": "Add Account", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL to forward zaps to", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Done!", "FSYL8G": "Trending Users", "FdhSU2": "Claim Now", @@ -129,28 +148,32 @@ "G1BGCg": "Select Wallet", "GFOoEE": "Salt", "GL8aXW": "Bookmarks ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Claim your included Snort nostr address", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Dislike", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Key..", "H0JBH6": "Log Out", "H6/kLh": "Order Paid!", "HAlOn1": "Name", - "HF4YnO": "Watch Live!", "HFls6j": "name will be available later", "HOzFdo": "Muted", "HWbkEK": "Clear cache and reload", "HbefNb": "Open Wallet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", "IEwZvs": "Are you sure you want to unpin this note?", "IKKHqV": "Follows", "INSqIz": "Twitter username...", "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Subscriptions", "JCIgkj": "Username", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Delete", "K7AkdL": "Show", "KAhAcM": "Enter LNDHub config", - "KLo3SP": "Reason: {reason}", "KQvWvD": "Deleted", "KWuDfz": "I have saved my keys, continue", "KahimY": "Unknown event kind: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Other Connections", + "LR1XjT": "Pin too short", "LXxsbk": "Anonymous", "LgbKvU": "Comment", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Debug Menus", "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", "MI2jkA": "Not available:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Buying {item}", "N2IrpM": "Confirm", + "NAidKb": "Notifications", "NAuFNH": "You already have a subscription of this type, please renew or pay", "NNSu3d": "Import Twitter Follows", "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", @@ -192,7 +219,6 @@ "OLEm6z": "Unknown login error", "OQXnew": "You subscription is still active, you can't renew yet", "ORGv1Q": "Created", - "P04gQm": "All zaps sent to this note will be received by the following LNURL", "P61BTu": "Copy Event JSON", "P7FD0F": "System (Default)", "P7nJT9": "Total today (UTC): {amount} sats", @@ -207,7 +233,6 @@ "QxCuTo": "Art by {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Subscription", - "R1fEdZ": "Forward Zaps", "R81upa": "People you follow", "RDZVQL": "Check", "RahCRH": "Expired", @@ -217,19 +242,19 @@ "RoOyAh": "Relays", "Rs4kCE": "Bookmark", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Update Lightning Address", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "LN Address Proxy", "ShdEie": "Mark all read", "Sjo1P4": "Custom", "Ss0sWu": "Pay Now", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "Pending Subscriptions", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Users must accept the content warning to show the content of your note.", "Up5U7K": "Block", @@ -239,15 +264,16 @@ "VR5eHw": "Public key (npub/nprofile)", "VlJkSk": "{n} muted", "VnXp8Z": "Avatar", - "VtPV/B": "Login with Extension (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "How do keys work?", "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", "W2PiAr": "{n} Blocked", "W9355R": "Unmute", "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "e.g. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reactions", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Enter pairing phrase", "ZKORll": "Activate Now", "ZLmyG9": "Contributors", - "ZUZedV": "Lightning Donation:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Show more", "b12Goz": "Mnemonic", "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bLZL5a": "Get Address", "bQdA2k": "Sensitive Content", "bep9C3": "Public Key", "bfvyfs": "Anon", "brAXSu": "Pick a username", "bxv59V": "Just now", "c+oiJe": "Install Extension", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "LN Address", "dOQCL8": "Display name", "e61Jf3": "Coming soon", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Mark All Read", "eHAneD": "Reaction emoji", "eJj8HD": "Get Verified", @@ -297,6 +326,7 @@ "fWZYP5": "Pinned", "filwqD": "Read", "flnGvv": "What's on your mind?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Saved", "g5pX+a": "About", "g985Wp": "Failed to send vote", @@ -310,9 +340,9 @@ "h8XMJL": "Badges", "hK5ZDk": "the world", "hMzcSq": "Messages", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Supports", "hicxcO": "Show replies", + "hmZ3Bz": "Media", "hniz8Z": "here", "i/dBAR": "Zap Pool", "iCqGww": "Reactions ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL translations", "iGT1eE": "Prevent fake accounts from imitating you", "iNWbVV": "Handle", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", "ieGrWo": "Follow", "itPgxd": "Profile", @@ -381,9 +410,11 @@ "qMx1sA": "Default Zap amount", "qUJTsT": "Bloklanıb", "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qfmMQh": "This note has been muted", "qkvYUb": "Add to Profile", "qmJ8kD": "Translation failed", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Enter wallet password", "rT14Ow": "Add Relays", @@ -392,7 +423,10 @@ "rmdsT4": "{n} days", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rudscU": "Failed to load follows, please try again later", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", "tOdNiY": "Dark", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Supporter Badge", "u/vOPu": "Paid", "u4bHcR": "Check out the code here: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "Reposts need to be manually confirmed", + "uc0din": "Send sats splits to", "usAvMr": "Edit Profile", "ut+2Cd": "Get a partner identifier", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", - "vU71Ez": "Paying with {wallet}", "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", "vhlWFg": "Poll Options", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Düzəliş et", "wLtRCF": "Sənin açarın", + "wSZR47": "Submit", "wWLwvh": "Anon", "wYSD2L": "Nostr Ünvanı", "wih7iJ": "ad bloklanıb", + "wofVHy": "Moderation", "wqyN/i": "{link} ünvanında {service} haqqında ətraflı məlumat əldə edin", "wtLjP6": "ID-ni kopyala", "x/Fx2P": "Bütün zaplarınızın bir hissəsini vəsait hovuzuna bölməklə istifadə etdiyiniz xidmətləri maliyyələşdirin!", - "x/q8d5": "Bu qeyd həssas olaraq qeyd edilib, aşkar etmək üçün bura klikləyin", "x82IOl": "Səssiz", + "xIcAOU": "Votes by {type}", "xIoGG9": "Get", "xJ9n2N": "Sizin açıq açarınız", "xKflGN": "{username}'nin Nostr-da İzləmələri", @@ -433,11 +468,13 @@ "y1Z3or": "Dil", "yCLnBC": "LNURL və ya Lightning Ünvanı", "yCmnnm": "Qlobaldan oxuyun", + "zCb8fX": "Weight", "zFegDD": "Əlaqə", "zINlao": "Sahibi", "zQvVDJ": "Hamısı", "zcaOTs": "Sats'larda Zap məbləği", "zjJZBd": "Hazırsan!", "zonsdq": "LNURL xidmətini yükləmək alınmadı", - "zvCDao": "Ən son qeydləri avtomatik göstərin" + "zvCDao": "Ən son qeydləri avtomatik göstərin", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/ca_ES.json b/packages/app/src/translations/ca_ES.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/ca_ES.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/cs_CZ.json b/packages/app/src/translations/cs_CZ.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/cs_CZ.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/da_DK.json b/packages/app/src/translations/da_DK.json index 6850017d..e1b96dd4 100644 --- a/packages/app/src/translations/da_DK.json +++ b/packages/app/src/translations/da_DK.json @@ -50,6 +50,7 @@ "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", "4Z3t5i": "Use imgproxy to compress images", "4rYCjn": "Note to Self", + "5BVs2e": "zap", "5JcXdV": "Create Account", "5oTnfy": "Buy Handle", "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", @@ -392,6 +393,7 @@ "rmdsT4": "{n} days", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", "sWnYKw": "Snort is designed to have a similar experience to Twitter.", "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", "tOdNiY": "Dark", @@ -422,6 +424,7 @@ "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", "x/q8d5": "This note has been marked as sensitive, click here to reveal", "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", "xIoGG9": "Go to", "xJ9n2N": "Your public key", "xKflGN": "{username}''s Follows on Nostr", diff --git a/packages/app/src/translations/de_DE.json b/packages/app/src/translations/de_DE.json index 3fb56be7..d9b81501 100644 --- a/packages/app/src/translations/de_DE.json +++ b/packages/app/src/translations/de_DE.json @@ -1,31 +1,34 @@ { - "+D82kt": "Sind Sie sicher, dass Sie {id} teilen möchten?", - "+PzQ9Y": "Payout Now", - "+Vxixo": "Secret Group Chat", + "+D82kt": "Bist du sicher, dass du {id} teilen möchtest?", + "+PzQ9Y": "Jetzt auszahlen", + "+Vxixo": "Geheimer Gruppenchat", "+aZY2h": "Zap Typ", + "+tShPg": "folgen", "+vA//S": "Anmeldungen", - "+vIQlC": "Bitte speichern Sie dieses Passwort, um in der Zukunft Ihren Benutzernamen verwalten zu können", + "+vIQlC": "Bitte sichere dir das folgende Passwort, um dein Handle in Zukunft verwalten zu können", "+vVZ/G": "Verbinden", - "+xliwN": "{name} reposted", + "+xliwN": "{name} hat gerepostet", "/4tOwT": "Überspringen", "/JE/X+": "Konto Hilfe", "/PCavi": "Öffentlich", - "/RD0e2": "Nostr benutzt digitale Signaturen um manipulationssichere Notizen zu erstellen, welche sicher auf mehreren Relais repliziert werden um Redundanz für Ihre Inhalte sicher zu stellen.", - "/d6vEc": "Machen Sie Ihr Profil einfacher auffind- und teilbar", + "/RD0e2": "Nostr nutzt digitale Signaturen, um manipulationssichere Notes zu erstellen, welche sicher auf viele Relais repliziert werden können, um eine redundante Speicherung deiner Inhalte zu bieten.", + "/Xf4UW": "Anonyme Nutzungsmetriken senden", + "/d6vEc": "Mach dein Profil leichter zu finden und zu teilen", "/n5KSF": "{n} ms", - "00LcfG": "Load more", + "00LcfG": "Mehr laden", "08zn6O": "Schlüssel exportieren", "0Azlrb": "Verwalten", "0BUTMv": "Suche...", "0jOEtS": "Ungültige LNURL", "0mch2Y": "Der Name enthält unerlaubte Zeichen", + "0uoY11": "Status anzeigen", "0yO7wF": "{n} Sek.", "1A7TZk": "Was ist Snort und wie funktioniert es?", - "1Mo59U": "Sind sie sicher, dass sie diesen Beitrag aus Ihren Lesezeichen entfernen möchten?", - "1R43+L": "Enter Nostr Wallet Connect config", + "1Mo59U": "Bist du sicher, dass du diese Note aus deinen Lesezeichen entfernen möchtest?", + "1R43+L": "Nostr Wallet Connect Konfiguration eingeben", "1c4YST": "Verbunden mit: {node}🎉", "1iQ8GN": "Vorschau ein/aus", - "1nYUGC": "{n} folgen", + "1nYUGC": "{n} Folgen", "1udzha": "Unterhaltungen", "2/2yg+": "Hinzufügen", "25V4l1": "Banner", @@ -34,165 +37,188 @@ "2a2YiP": "{n} Lesezeichen", "2k0Cv+": "Gefällt nicht ({n})", "2ukA4d": "{n} Stunden", + "3KNMbJ": "Artikel", "3Rx6Qo": "Erweitert", "3cc4Ct": "Hell", "3gOsZq": "Übersetzer", "3qnJlS": "Du stimmst mit {amount} sats ab", "3t3kok": "{n,plural,one {}=1{{n} neues Event} other{{n} neue Events}}", - "3tVy+Z": "{n} Folgende", + "3tVy+Z": "{n} Follower", "3xCwbZ": "oder", - "3yk8fB": "Brieftasche", + "3yk8fB": "Wallet", + "40VR6s": "Nostr Connect", "450Fty": "Keine", "47FYwb": "Abbrechen", "4IPzdn": "Primäre Entwickler", - "4L2vUY": "Ihr neuer NIP-05 Name ist:", + "4L2vUY": "Dein neuer NIP-05-Handle ist:", "4OB335": "Gefällt nicht", "4Vmpt4": "Nostr Plebs ist einer der ersten NIP-05 Anbieter und bietet eine gute Auswahl von Domains zu fairen Preisen", - "4Z3t5i": "Benutzen Sie imgproxy um Bilder zu komprimieren", + "4Z3t5i": "Verwende imgproxy um Bilder zu komprimieren", "4rYCjn": "Notiz an mich selbst", - "5JcXdV": "Benutzerkonto anlegen", + "5BVs2e": "Zap", + "5CB6zB": "Zap-Aufteilungen", + "5JcXdV": "Konto erstellen", "5oTnfy": "Handle kaufen", - "5rOdPG": "Sobald Sie Ihre Schlüssel-Manager-Erweiterung eingerichtet haben und einen Schlüssel generiert haben, können Sie unserem neuen Setup Prozess folgen, um Ihr Profil einzurichten und ein paar interessante Profile zu entdecken.", + "5rOdPG": "Sobald du deine Erweiterung für die Schlüsselverwaltung eingerichtet und einen Schlüssel generiert hast, kannst du unserem Prozess für neue Benutzer folgen, um dein Profil einzurichten und einige interessante Leute auf Nostr zu finden, denen du folgen kannst.", "5u6iEc": "An öffentlichen Schlüssel übertragen", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "Benutzernamen sind auf Nostr nicht einzigartig. Die Nostr-Adresse ist deine eindeutige, menschenlesbare Adresse, die dir bei der Registrierung eindeutig zugeordnet ist.", "5ykRmX": "Zap senden", - "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Wiederholung anschauen", + "65BmHb": "Bild von {host} konnte nicht durch Proxy geladen werden, klicke hier, um es direkt zu laden", + "6OSOXl": "Grund: {reason}", "6Yfvvp": "Bekomme eine Identifikation", + "6bgpn+": "Nicht alle Clients unterstützen dies, deshalb kann es sein, dass du immer noch einige Zaps erhältst, als ob Zap-Aufteilungen nicht konfiguriert wäre", "6ewQqw": "Gefällt ({n})", "6uMqL1": "Nicht bezahlt", - "7+Domh": "Notizen", + "7+Domh": "Notes", "7BX/yC": "Konto wechseln", "7hp70g": "NIP-05", - "7xzTiH": "{action} zu {target}", - "8/vBbP": "Erneute Posts ({n})", - "89q5wc": "Erneutes senden bestätigen", - "8QDesP": "Zap {n} sats", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Reposts bestätigen", + "8Kboo2": "Scanne diesen QR-Code mit deiner Signierer-App, um loszulegen", + "8QDesP": "{n} sats zappen", + "8Rkoyb": "Empfänger", + "8Y6bZQ": "Ungültige Zap-Aufteilung: {input}", "8g2vyB": "Name ist zu lang", "8v1NN+": "Verbindungsphrase", + "8xNnhi": "Nostr-Erweiterung", "9+Ddtu": "Weiter", "9HU8vw": "Antworten", "9SvQep": "Folgt {n}", "9WRlF4": "Senden", "9gqH2W": "Anmelden", - "9pMqYs": "Nostr Adresse", + "9pMqYs": "Nostr-Adresse", "9wO4wJ": "Lightning Zahlungsanforderung", "ADmfQT": "Vorherige", - "AGNz71": "Zap All {n} sats", + "AGNz71": "Allen {n} sats zappen", + "AN0Z7Q": "Stummgeschaltete Wörter", "ASRK0S": "Dieser Autor wurde stummgeschalten", "Adk34V": "Profil erstellen", "Ai8VHU": "Unbegrenzte Note Speicherung auf Snort Relais", "AkCxS/": "Grund", - "AnLrRC": "Ohne Zap", + "AnLrRC": "Nicht-Zap", "AyGauy": "Anmelden", "B4C47Y": "Name ist zu kurz", - "B6+XJy": "gezappt", + "B6+XJy": "hat gezappt", "B6H7eJ": "nsec, npub, nip-05, hex", - "BGCM48": "Schreibe Zugriff auf Snort-Relais mit 1 Jahr Event Speicherung", + "BGCM48": "Schreibzugriff auf Snort Relais, mit 1 Jahr Speicherung von Events", "BOUMjw": "Keine Nostr-Benutzer gefunden für {twitterUsername}", "BOr9z/": "Snort ist ein Open-Source-Projekt, von begeisterten in ihrer Freizeit entwickelt", "BWpuKl": "Aktualisieren", - "BcGMo+": "Notizen enthalten Text, der meistgenutzte Anwendungsfall sind \"Tweet ähnliche\" Nachrichten.", + "BcGMo+": "Notes enthalten Text, der meistgenutzte Anwendungsfall sind \"Tweet ähnliche\" Nachrichten.", + "C1LjMx": "Lightning Spende", "C5xzTC": "Premium", "C81/uG": "Abmelden", - "C8HhVE": "Suggested Follows", + "C8HhVE": "Vorgeschlagene Follows", "CHTbO3": "Lightning Zahlungsanforderung konnte nicht geladen werden", - "CVWeJ6": "Trending People", + "CVWeJ6": "Angesagte Personen", "CmZ9ls": "{n} Stummgeschaltet", "CsCUYo": "{n} sats", "Cu/K85": "Übersetzt von {lang}", - "D+KzKd": "Automatisch jeden Beitrag zappen", + "D+KzKd": "Automatisch jede Note beim Laden zappen", "D3idYv": "Einstellungen", "DKnriN": "Sats senden", - "DZzCem": "Letzte {n} Notizen anzeigen", + "DZzCem": "Neueste {n} Notes anzeigen", "DcL8P+": "Unterstützer", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transferieren", "E8a4yq": "Folgen Sie einigen beliebten Konten", - "ELbg9p": "Data Providers", - "EPYwm7": "Ihr Privatschlüssel ist ihr Passwort. Falls Sie Ihn verlieren, werden Sie den Zugriff zu Ihrem Konto verlieren! Bewahren Sie ihn an einem sicheren Ort auf. Es gibt keine Wiederherstellungsmöglichkeit für Ihren Privatschlüssel.", + "ELbg9p": "Datenanbieter", + "EPYwm7": "Dein privater Schlüssel ist dein Passwort. Wenn du diesen Schlüssel verlierst, hast du keinen Zugang mehr zu deinem Konto! Kopiere ihn und bewahre ihn an einem sicheren Ort auf. Es gibt keine Möglichkeit, deinen privaten Schlüssel wiederherzustellen.", + "EQKRE4": "Abzeichen auf Profilseiten anzeigen", "EWyQH5": "Global", "Ebl/B2": "Auf {lang} Übersetzen", - "EcZF24": "Custom Relays", + "EcZF24": "Benutzerdefinierte Relais", "EcglP9": "Schlüssel", + "EjFyoR": "On-chain Spendenadresse", "EnCOBJ": "Kaufen", "Eqjl5K": "Nur Snort und unsere integrierten Partner bieten farbige Domainnamen als NIP-05 Kennzeichen. Sie können aber auch gerne andere Dienste nutzen.", "F+B3x1": "Wir sind auch eine Partnerschaft mit nostrplebs.com eingegangen, um Ihnen mehr Optionen anbieten zu können.", - "F3l7xL": "Benutzerkonto hinzufügen", + "F3l7xL": "Konto hinzufügen", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL zum Weiterleiten der Zaps", - "FS3b54": "Feritg!", - "FSYL8G": "Trending Users", + "FMfjrl": "Statusnachrichten auf Profilseiten anzeigen", + "FS3b54": "Fertig!", + "FSYL8G": "Angesagte Benutzer", "FdhSU2": "Jetzt abholen", "FfYsOb": "Ein Fehler ist aufgetreten!", - "FmXUJg": "Folgt Ihnen", + "FmXUJg": "Folgt dir", "G/yZLu": "Entfernen", "G1BGCg": "Wallet auswählen", "GFOoEE": "Salt", "GL8aXW": "Lesezeichen ({n})", - "GSye7T": "Lightning Address", - "GUlSVG": "Beanspruche deine enthaltene Snort Nostr Adresse", + "GQPtfk": "Stream beitreten", + "GSye7T": "Lightning-Adresse", + "GUlSVG": "Beanspruche deine enthaltene Snort Nostr-Adresse", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Gefällt nicht", - "H+vHiz": "Hex Schlüssel..", + "Gxcr08": "Event verbreiten", + "H+vHiz": "Hex-Schlüssel...", "H0JBH6": "Abmelden", "H6/kLh": "Bestellung bezahlt!", "HAlOn1": "Name", - "HF4YnO": "Watch Live!", "HFls6j": "Name wird später verfügbar sein", "HOzFdo": "Stummgeschaltet", - "HWbkEK": "Clear cache and reload", + "HWbkEK": "Cache leeren und neu laden", "HbefNb": "Wallet öffnen", + "HhcAVH": "Du folgst dieser Person nicht, klicke hier, um Medien von {link}zu laden, oder aktualisiere deine Einstellungen, um Medien immer von allen zu laden.", "IDjHJ6": "Danke für die Verwendung von Snort. Wir würden uns über eine Spende freuen.", "IEwZvs": "Sind sie sicher, dass sie diese Notiz entpinnen möchten?", - "IKKHqV": "Follows", - "INSqIz": "Twitter Benuzername...", - "IUZC+0": "Das bedeutet, dass niemand Ihre Notizen ändern kann, und jeder kann leicht überprüfen ob die Notizen die er liest wirklich von Ihnen erstellt wurden", - "Ig9/a1": "Sent {n} sats to {name}", - "Ix8l+B": "Trending Notes", + "IKKHqV": "Folgt", + "INSqIz": "Twitter-Benutzername...", + "IUZC+0": "Das bedeutet, dass niemand die von dir erstellten Notes ändern kann und jeder kann leicht überprüfen, ob die Note, die er liest, von dir erstellt wurde.", + "Ig9/a1": "Sende {n} sats an {name}", + "IoQq+a": "Klicke hier, um trotzdem zu laden", + "Ix8l+B": "Angesagte Notes", "J+dIsA": "Abonnements", "JCIgkj": "Benutzername", + "JGrt9q": "Sats an {name} senden", "JHEHCk": "Zaps ({n})", - "JPFYIM": "No lightning address", + "JPFYIM": "Keine Lightning-Adresse", "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JjGgXI": "Nutzer suchen", "JkLHGw": "Webseite", "JymXbw": "Privater Schlüssel", "K3r6DQ": "Löschen", "K7AkdL": "Anzeigen", "KAhAcM": "LNDHub Konfiguration eingeben", - "KLo3SP": "Grund: {reason}", "KQvWvD": "Gelöscht", "KWuDfz": "Ich habe meine Schlüssel gesichert, fortsetzen", "KahimY": "Unbekannte Event Art: {kind}", - "KoFlZg": "Enter mint URL", + "KoFlZg": "Mint-URL eingeben", + "KtsyO0": "PIN eingeben", "LF5kYT": "Weitere Verbindungen", + "LR1XjT": "PIN zu kurz", "LXxsbk": "Anonym", - "LgbKvU": "Kommentieren", - "Lu5/Bj": "Open on Zapstr", - "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LgbKvU": "Kommentar", + "Lu5/Bj": "In Zapstr öffnen", + "Lw+I+J": "{n,plural,=0{{name} zappte} other{{name} & {n} andere zappten}}", + "LwYmVi": "Zaps auf diese Note werden an die folgenden Nutzer aufgeteilt.", + "M10zFV": "Nostr Connect", "M3Oirc": "Debug Menüs", "MBAYRO": "Zeigt \"ID kopieren\" und \"Event JSON kopieren\" im Kontextmenu jeder Nachricht an", "MI2jkA": "Nicht verfügbar:", "MP54GY": "Wallet Passwort", - "MRp6Ly": "Twitter Benutzername", + "MRp6Ly": "Twitter-Benutzername", "MWTx65": "Standardseite", "Mrpkot": "Abonnement bezahlen", - "MuVeKe": "Buy nostr address", - "MzRYWH": "{item} Kaufen", + "MuVeKe": "Nostr-Adresse kaufen", + "MzRYWH": "{item} kaufen", "N2IrpM": "Bestätigen", + "NAidKb": "Benachrichtigungen", "NAuFNH": "Du hast bereits ein Abonnement dieser Art, bitte erneuern oder bezahlen", "NNSu3d": "Importiere Folgende von Twitter", "NdOYJJ": "Hmm, nichts da.. Schau auf {newUsersPage} um einigen empfohlen Nostrichs zu folgen!", "NepkXH": "Kann nicht mit {amount} sats abstimmen. Bitte einen anderen Standard-Zap-Betrag festlegen", - "NfNk2V": "Ihr Privatschlüssel", + "NfNk2V": "Dein privater Schlüssel", "NndBJE": "Neue Benutzer Seite", - "O9GTIc": "Profile picture", + "O9GTIc": "Profilbild", "OEW7yJ": "Zaps", "OKhRC6": "Teilen", "OLEm6z": "Unbekannter Anmeldefehler", "OQXnew": "Dein Abonnement ist noch aktiv, du kannst es noch nicht erneuern", "ORGv1Q": "Erstellt", - "P04gQm": "Alle an diesen Beitrag gesendeten Zaps werden von der folgenden LNURL empfangen", "P61BTu": "Event JSON kopieren", "P7FD0F": "System (Standard)", "P7nJT9": "Gesamt heute (UTC): {amount} sats", @@ -202,242 +228,253 @@ "Pe0ogR": "Erscheinungsbild", "PrsIg7": "Reaktionen werden auf jeder Seite angezeigt, wenn Reaktionen deaktiviert sind, werden sie nicht angezeigt", "QDFTjG": "{n} Relais", - "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", - "QawghE": "Sie können Ihren Benutzernamen jederzeit ändern.", + "QWhotP": "Zap Pool funktioniert nur, wenn du eine der unterstützten Wallet-Verbindungen verwenden (WebLN, LNC, LNDHub oder Nostr Wallet Connect)", + "QawghE": "Du kannst deinen Benutzernamen jederzeit ändern.", "QxCuTo": "Kunst von {name}", - "Qxv0B2": "You currently have {number} sats in your zap pool.", + "Qxv0B2": "Du hast aktuell {number} sats in deinem Zap Pool.", "R/6nsx": "Abonnement", - "R1fEdZ": "Zaps weiterleiten", - "R81upa": "People you follow", + "R81upa": "Personen, denen du folgst", "RDZVQL": "Prüfen", "RahCRH": "Abgelaufen", "RfhLwC": "Von: {author}", "RhDAoS": "Sind sie sicher, dass sie {id} löschen möchten?", - "RjpoYG": "Recent", + "RjpoYG": "Neueste", "RoOyAh": "Relais", "Rs4kCE": "Lesezeichen", - "RwFaYs": "Sort", - "SOqbe9": "Lightning Adresse aktualisieren", - "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", - "SYQtZ7": "LN Adresse Proxy", - "ShdEie": "Mark all read", + "RwFaYs": "Sortieren", + "SMO+on": "Zap an {name} senden", + "SOqbe9": "Lightning-Adresse aktualisieren", + "SP0+yi": "Abonnement kaufen", + "SYQtZ7": "LN-Adressen-Proxy", + "ShdEie": "Alle als gelesen markieren", "Sjo1P4": "Benutzerdefiniert", "Ss0sWu": "Jetzt bezahlen", - "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "StKzTE": "Der Autor hat diese Note als ein sensibles Thema markiert", + "TDR5ge": "Medien in Notes werden für ausgewählte Personen automatisch angezeigt, ansonsten wird nur der Link angezeigt", + "TP/cMX": "Beendet", "TpgeGw": "Hex Salt..", - "Tpy00S": "People", + "Tpy00S": "Personen", "UDYlxu": "Ausstehende Abonnements", - "ULotH9": "Amount: {amount} sats", - "UT7Nkj": "New Chat", - "UUPFlt": "Nutzer müssen die Inhaltswarnung akzeptieren, um den Inhalt Ihres Beitrags anzuzeigen.", + "UT7Nkj": "Neuer Chat", + "UUPFlt": "Nutzer müssen die Inhaltswarnung akzeptieren, um den Inhalt deiner Note anzuzeigen.", "Up5U7K": "Blockieren", "VBadwB": "Hmm, es konnte keine Schlüssel-Manager-Erweiterung gefunden werden. Versuchen Sie, die Seite neu zu laden.", "VN0+Fz": "Guthaben: {amount} Sats", - "VOjC1i": "Wählen Sie einen Upload-Dienst für Ihre Anhänge", + "VOjC1i": "Wähle einen Upload-Dienst für deine Anhänge", "VR5eHw": "Öffentlicher Schlüssel (npub/nprofile)", "VlJkSk": "{n} stummgeschaltet", "VnXp8Z": "Profilbild", - "VtPV/B": "Mit Erweiterung anmelden (NIP-07)", - "VvaJst": "View Wallets", + "VvaJst": "Wallets anzeigen", "Vx7Zm2": "Wie funktionieren Schlüssel?", - "W1yoZY": "Es sieht so aus, als hättest du keine Abonnements. Hier kannst du eines erhalten {link}", + "W1yoZY": "Es sieht so aus, als hättest du keine Abonnements, du kannst eines {link} erhalten", "W2PiAr": "{n} Blockiert", "W9355R": "Stummschaltung aufheben", "WONP5O": "Finden Sie Ihre Twitter Kontakte auf Nostr (Daten zur Verfügung gestellt von {provider})", + "WvGmZT": "npub / nprofile / Nostr-Adresse", "WxthCV": "z.B. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", - "XICsE8": "File hosts", + "XECMfW": "Nutzungsmetriken senden", + "XICsE8": "Datei-Hosts", "XgWvGA": "Reaktionen", - "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", - "XrSk2j": "Redeem", + "Xopqkl": "Dein standardmäßiger Zap-Betrag ist {number} sats, Beispielwerte werden daraus berechnet.", + "XrSk2j": "Einlösen", "XzF0aC": "Schlüssel-Manager-Erweiterungen sind sicherer und erlauben es Ihnen, sich ganz einfach bei jedem Nostr-Client anzumelden. Hier sind einige bekannte Erweiterungen:", "Y31HTH": "Unterstütze die Entwicklung von Snort", "YDURw6": "URL des Dienstes", - "YXA3AH": "Reaktionen einschalten", - "Z0FDj+": "Abonniere Snort {plan} für {price} und erhalte folgende Belohnungen", + "YXA3AH": "Reaktionen aktivieren", + "Z0FDj+": "Abonniere Snort {plan} für {price} und erhalte folgende Prämien", "Z4BMCZ": "Verbindungs-Passphrase eingeben", "ZKORll": "Jetzt aktivieren", "ZLmyG9": "Mitwirkende", - "ZUZedV": "Lightning Spende:", - "Zr5TMx": "Setup profile", + "ZS+jRE": "Sende Zap-Aufteilungen an", + "Zr5TMx": "Profil einrichten", "a5UPxh": "Finanzieren Sie Entwickler und Plattformen, die NIP-05-Verifizierungsdienste anbieten", - "a7TDNm": "Notes will stream in real time into global and notes tab", + "a7TDNm": "Notes werden in Echtzeit in Global- und Notes-Tab gestreamt", "aWpBzj": "Mehr anzeigen", "b12Goz": "Mnemonik", - "b5vAk0": "Dein Handle ist wie eine Lightning-Adresse leitet dich zu deiner gewählten LNURL oder Lightning-Adresse weiter", + "b5vAk0": "Dein Handle ist wie eine Lightning-Adresse und leitet dich zu deiner gewählten LNURL oder Lightning-Adresse weiter", + "bLZL5a": "Adresse erhalten", "bQdA2k": "Empfindlicher Inhalt", "bep9C3": "Öffentlicher Schlüssel", "bfvyfs": "Anon", "brAXSu": "Wählen Sie einen Benutzernamen", "bxv59V": "Gerade eben", "c+oiJe": "Erweiterung installieren", - "c35bj2": "Wenn Sie eine Frage zu Ihrer NIP-05-Bestellung haben, wenden Sie sich bitte per Direktnachricht an {link}", - "c3g2hL": "Broadcast Again", - "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "c2DTVd": "Gib eine PIN ein, um deinen privaten Schlüssel zu verschlüsseln. Du musst diese PIN jedes Mal eingeben, wenn du Snort öffnest.", + "c35bj2": "Wenn du eine Anfrage zu deiner NIP-05-Bestellung hast, schreibe bitte eine DM an {link}", + "c3g2hL": "Erneut verbreiten", + "cFbU1B": "Du benutzt Alby? Gehe zu {link} um deine NWC Konfiguration zu erhalten!", "cPIKU2": "Folge ich", "cQfLWb": "URL..", "cWx9t8": "Alle stummschalten", - "cg1VJ2": "Brieftasche verbinden", + "cg1VJ2": "Wallet verbinden", "cuP16y": "Unterstützung für mehrere Konten", "cuV2gK": "Name ist registriert", "cyR7Kh": "Zurück", "d6CyG5": "Verlauf", - "d7d0/x": "LN Adresse", + "d7d0/x": "LN-Adresse", "dOQCL8": "Anzeigename", "e61Jf3": "Kommt bald", + "e7VmYP": "PIN eingeben, um deinen privaten Schlüssel zu entsperren", "e7qqly": "Alle als gelesen markieren", "eHAneD": "Reaktions-Emoji", "eJj8HD": "Verifiziert werden", - "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", - "eXT2QQ": "Group Chat", + "eSzf2G": "Ein einzelner Zap von {nIn} sats wird {nOut} sats dem Zap Pool zuweisen.", + "eXT2QQ": "Gruppenchat", "fBI91o": "Zap", "fOksnD": "Abstimmung nicht möglich, da der LNURL-Dienst keine Zaps unterstützt", "fWZYP5": "Angeheftet", - "filwqD": "Gelesen", + "filwqD": "Lesen", "flnGvv": "Was ist gerade los?", + "fqwcJ1": "On-Chain Spende", "fsB/4p": "Gespeichert", - "g5pX+a": "Über uns", - "g985Wp": "Fehler beim Senden der Bewertung", - "gBdUXk": "Speichern Sie Ihre Schlüssel!", - "gDZkld": "Snort ist eine Nostr Benutzeroberfläche, Nostr ist ein dezentralisiertes Protokoll zur Distribution von \"Notizen\" (Text).", - "gDzDRs": "Emoji, das gesendet werden soll, wenn auf eine Notiz reagiert wird", + "g5pX+a": "Über mich", + "g985Wp": "Stimmabgabe fehlgeschlagen", + "gBdUXk": "Speicher deine Schlüssel!", + "gDZkld": "Snort ist eine Nostr Benutzeroberfläche, Nostr ist ein dezentralisiertes Protokoll zur Distribution von \"Notes\" (Text).", + "gDzDRs": "Emoji, das gesendet werden soll, wenn auf eine Note reagiert wird", "gXgY3+": "Nicht alle Clients unterstützen das", "gczcC5": "Abonnieren", "gjBiyj": "Lädt...", - "grQ+mI": "Proof of Work", + "grQ+mI": "Arbeitsnachweis", "h8XMJL": "Auszeichnungen", "hK5ZDk": "Die Welt", "hMzcSq": "Nachrichten", - "hWSp+B": "Nostr Connect (NIP-46)", - "hY4lzx": "Unterstützungen", + "hY4lzx": "Unterstützt", "hicxcO": "Antworten anzeigen", + "hmZ3Bz": "Medien", "hniz8Z": "hier", "i/dBAR": "Zap Pool", "iCqGww": "Reaktionen ({n})", "iDGAbc": "Holen dir einen Snort Identifikator", "iEoXYx": "DeepL Übersetzungen", - "iGT1eE": "Verhindern Sie, dass gefälschte Konten Sie imitieren", - "iNWbVV": "Name", - "iUsU2x": "Mint: {url}", + "iGT1eE": "Verhindere, dass gefälschte Konten dich imitieren", + "iNWbVV": "Handle", "iXPL0Z": "Anmeldung mit privatem Schlüssel bei einer unsicheren Verbindung nicht möglich, bitte verwenden Sie stattdessen eine Nostr Schlüssel Manager Erweiterung", "ieGrWo": "Folgen", "itPgxd": "Profil", - "izWS4J": "Nicht mehr folgen", + "izWS4J": "Entfolgen", "jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}", "jCA7Cw": "Vorschau auf Snort", "jMzO1S": "Interner Fehler: {msg}", "jfV8Wr": "Zurück", - "juhqvW": "Verbessern Sie die Anmeldesicherheit mit Browsererweiterungen", + "juhqvW": "Verbessere die Anmeldesicherheit mit Browsererweiterungen", "jvo0vs": "Speichern", "jzgQ2z": "{n} Reaktionen", "k2veDA": "Schreiben", - "k7sKNy": "Unser eigener NIP-05-Verifizierungsdienst unterstützt die Entwicklung dieser Website und gibt Ihnen ein glänzendes Sonderabzeichen auf unserer Webseite!", - "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "k7sKNy": "Unser eigener NIP-05-Verifizierungsdienst unterstützt die Entwicklung dieser Website. Unterstütze uns und erhalte ein Abzeichen auf unserer Seite!", + "kJYo0u": "{n,plural,=0{{name} hat gerepostet} other{{name} & {n} andere haben gerepostet}}", "kaaf1E": "jetzt", - "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", - "l+ikU1": "Alles im {plan}", + "kuPHYE": "{n,plural,=0{{name} gefällt das} other{{name} & {n} anderen gefällt das}}", + "l+ikU1": "Alles aus {plan}", "lBboHo": "Wenn du ein paar andere ausprobieren möchtest, besuche {link} für mehr!", "lCILNz": "Jetzt kaufen", "lD3+8a": "Bezahlen", - "lPWASz": "Snort Nostr Adresse", - "lTbT3s": "Brieftaschen-Passwort", + "lPWASz": "Snort Nostr-Adresse", + "lTbT3s": "Wallet Passwort", "lgg1KN": "Kontoseite", - "ll3xBp": "Bild Proxy Dienst", + "ll3xBp": "Bildproxy-Dienst", "lnaT9F": "Folgt {n}", "lsNFM1": "Klicken, um Inhalt von {link} zu laden", "lvlPhZ": "Lightning Zahlungsanforderung bezahlen", - "mErPop": "Es sieht so aus, als hättest du keine, überprüfe {link} um eins zu kaufen!", - "mH91FY": "Jeder Mitwirkende erhält einen Anteil aller Spenden und NIP-05-Bestellungen, die Aufteilung können Sie unten sehen", + "mErPop": "Es sieht so aus, als hättest du keine, checke {link} um eine zu kaufen!", + "mH91FY": "Jeder Mitwirkende erhält einen Anteil aller Spenden und NIP-05-Bestellungen. Die Aufteilung kannst du unten sehen", "mKAr6h": "Allen folgen", "mKh2HS": "Datei-Upload-Dienst", - "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", - "mTJFgF": "Popular", + "mKhgP9": "{n,plural,=0{} =1{hat gezappt} other{haben gezappt}}", + "mTJFgF": "Beliebt", "mfe8RW": "Option: {n}", "n1Whvj": "Wechseln", "nDejmx": "Freigeben", "nGBrvw": "Lesezeichen", - "nN9XTz": "Teilen Sie Ihre Gedanken mit {link}", + "nN9XTz": "Teile deine Gedanken mit {link}", "nOaArs": "Profil einrichten", "nWQFic": "Erneuern", - "nn1qb3": "Ihre Spenden werden sehr geschätzt", + "nn1qb3": "Deine Spenden werden sehr geschätzt", "nwZXeh": "{n} blockiert", "o6Uy3d": "Nur der Privatschlüssel kann genutzt werden um zu veröffentlichen (Events signieren), alles andere wird im schreibgeschützten Modus geladen.", - "o7e+nJ": "{n} Abonnenten", + "o7e+nJ": "{n} Follower", "oJ+JJN": "Keine Treffer :/", "odFwjL": "Nur für Folgende", "odhABf": "Anmelden", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", - "osUr8O": "Sie können diese Erweiterungen auch verwenden, um sich bei den meisten Nostr-Webseiten anzumelden.", + "ojzbwv": "Hey, es sieht so aus, als hättest du noch keine Nostr-Adresse, du solltest eine anlegen! Sieh dir {link} mal an", + "osUr8O": "Du kannst diese Erweiterungen auch verwenden, um dich bei den meisten Nostr-Webseiten anzumelden.", "oxCa4R": "Das Erhalten eines Identifikators hilft Menschen, die Sie kennen, Ihre Identität zu bestätigen. Viele Leute können einen Benutzernamen wie @jack haben, aber es kann nur einen jack@cash.app geben.", - "p4N05H": "Upload", + "p4N05H": "Hochladen", "p85Uwy": "Aktive Abonnements", - "pI+77w": "Herunterladbare Backups von Snort Relais", - "puLNUJ": "Pin", - "pzTOmv": "Abonnenten", - "qD9EUF": "E-Mail <> DM Brücke für deine Snort Nostr Adresse", + "pI+77w": "Herunterladbare Backups vom Snort Relais", + "puLNUJ": "Anheften", + "pzTOmv": "Follower", + "qD9EUF": "E-Mail <> DM Brücke für deine Snort Nostr-Adresse", "qDwvZ4": "Unbekannter Fehler", "qMx1sA": "Standard Zap Betrag", "qUJTsT": "Blockiert", "qdGuQo": "Dein privater Schlüssel ist (Teile diesen mit niemanden!)", + "qfmMQh": "Diese Note wurde stummgeschaltet", "qkvYUb": "Zum Profil hinzufügen", "qmJ8kD": "Übersetzung fehlgeschlagen", - "qtWLmt": "Like", + "qtWLmt": "Gefällt mir", + "qz9fty": "Falsche PIN", "r3C4x/": "Software", - "r5srDR": "Brieftaschen-Passwort eingeben", + "r5srDR": "Wallet Passwort eingeben", "rT14Ow": "Relais hinzufügen", "reJ6SM": "Wenn Sie einen PC benutzen wird es empfohlen eine der folgenden Browsererweiterungen zu verwenden, um Ihren Schlüssel zu sichern:", "rfuMjE": "(Standard)", "rmdsT4": "{n} Tage", "rrfdTe": "Dies ist die gleiche Technologie, die auch von Bitcoin verwendet wird, und sich als äußerst sicher erwiesen hat.", "rudscU": "Abonnements konnten nicht geladen werden, bitte versuchen Sie es später erneut", + "sKDn4e": "Abzeichen anzeigen", + "sUNhQE": "Nutzer", "sWnYKw": "Snort wurde darauf konzipiert ein Twitter ähnliches Erlebnis zu bieten.", - "svOoEH": "Namesquatting und Nachahmung sind nicht erlaubt. Snort und unsere Partner behalten sich das Recht vor, Ihren Identifikator (nicht Ihr Konto, niemand kann Ihnen das wegnehmen) wegen Verstoßes gegen diese Regel zu kündigen.", + "sZQzjQ": "Fehler beim Parsen der Zap-Aufteilung: {input}", + "svOoEH": "Namens-Squatting und Impersonation sind nicht erlaubt. Snort und unsere Partner behalten sich das Recht vor, bei einem Verstoß gegen diese Regel dein Handle zu löschen (nicht dein Konto - das kann dir niemand wegnehmen).", "tOdNiY": "Dunkel", - "th5lxp": "Send note to a subset of your write relays", + "th5lxp": "Sende Note zu einer Untergruppe deiner Schreib-Relays", "thnRpU": "Eine NIP-05 Verifizierung kann helfen:", - "ttxS0b": "Unterstützer Abzeichen", + "ttxS0b": "Unterstützer-Abzeichen", "u/vOPu": "Bezahlt", - "u4bHcR": "Sehen Sie sich den Code hier an: {link}", - "uD/N6c": "Zap {target} {n} sats", + "u4bHcR": "Sieh dir hier den Code an: {link}", "uSV4Ti": "Reposts müssen manuell bestätigt werden", + "uc0din": "Sende Sats-Aufteilungen an", "usAvMr": "Profil anpassen", "ut+2Cd": "Holen Sie sich einen Partner-Identifikator", - "v8lolG": "Start chat", - "vOKedj": "{n,plural,one {}=1{& {n} other} other{& {n} andere}}", - "vU71Ez": "Bezahlen mit {wallet}", - "vZ4quW": "NIP-05 ist eine DNS-basierte Verifizierungsspezifikation, die dabei hilft, Sie als echten Benutzer zu validieren.", + "v8lolG": "Chat starten", + "vOKedj": "{n,plural,one {}=1{& {n} anderer} other{& {n} andere}}", + "vZ4quW": "NIP-05 ist eine DNS-basierte Verifizierungsspezifikation, die dabei hilft, dich als echten Benutzer zu validieren.", "vhlWFg": "Umfrageoptionen", - "vlbWtt": "Get a free one", + "vlbWtt": "Eine kostenlos erhalten", "vrTOHJ": "{amount} sats", - "vxwnbh": "Amount of work to apply to all published events", + "vxwnbh": "Maß an Arbeit, die auf alle veröffentlichte Events angewendet werden soll", "wEQDC6": "Anpassen", "wLtRCF": "Dein Schlüssel", + "wSZR47": "Absenden", "wWLwvh": "Anon", - "wYSD2L": "Nostr Adresse", + "wYSD2L": "Nostr-Adresse", "wih7iJ": "Name ist blockiert", + "wofVHy": "Moderation", "wqyN/i": "Erfahren Sie mehr über {service} auf {link}", "wtLjP6": "ID kopieren", - "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Dieser Beitrag wurde als sensibel markiert, klicken Sie hier um den Inhalt zu zeigen", + "x/Fx2P": "Unterstütze die von dir genutzten Dienste, indem du einen Teil deiner Zaps in einen Spendenpool einzahlst!", "x82IOl": "Stummschalten", + "xIcAOU": "Stimmen nach {type}", "xIoGG9": "Gehe zu", "xJ9n2N": "Dein öffentlicher Schlüssel", "xKflGN": "{username}''s folgt auf Nostr", "xQtL3v": "Entsperren", - "xaj9Ba": "Provider", + "xaj9Ba": "Anbieter", "xbVgIm": "Medien automatisch laden", "xhQMeQ": "Ablaufdatum", "xmcVZ0": "Suche", "y1Z3or": "Sprache", - "yCLnBC": "LNURL oder Lightning Adresse", + "yCLnBC": "LNURL oder Lightning-Adresse", "yCmnnm": "Global lesen von", + "zCb8fX": "Gewichtung", "zFegDD": "Kontakt", "zINlao": "Eigentümer", "zQvVDJ": "Alle", - "zcaOTs": "Zap Betrag in sats", - "zjJZBd": "Sie sind bereit!", + "zcaOTs": "Zap Betrag in Sats", + "zjJZBd": "Du bist bereit!", "zonsdq": "Fehler beim Laden des LNURL-Dienstes", - "zvCDao": "Neueste Beiträge automatisch anzeigen" + "zvCDao": "Neueste Notes automatisch anzeigen", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/el_GR.json b/packages/app/src/translations/el_GR.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/el_GR.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/en.json b/packages/app/src/translations/en.json index 6850017d..6659d9e0 100644 --- a/packages/app/src/translations/en.json +++ b/packages/app/src/translations/en.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "Zap Type", + "+tShPg": "following", "+vA//S": "Logins", "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", "+vVZ/G": "Connect", @@ -11,6 +12,7 @@ "/JE/X+": "Account Support", "/PCavi": "Public", "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Make your profile easier to find and share", "/n5KSF": "{n} ms", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "Search...", "0jOEtS": "Invalid LNURL", "0mch2Y": "name has disallowed characters", + "0uoY11": "Show Status", "0yO7wF": "{n} secs", "1A7TZk": "What is Snort and how does it work?", "1Mo59U": "Are you sure you want to remove this note from bookmarks?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Bookmarks", "2k0Cv+": "Dislikes ({n})", "2ukA4d": "{n} hours", + "3KNMbJ": "Articles", "3Rx6Qo": "Advanced", "3cc4Ct": "Light", "3gOsZq": "Translators", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Followers", "3xCwbZ": "OR", "3yk8fB": "Wallet", + "40VR6s": "Nostr Connect", "450Fty": "None", "47FYwb": "Cancel", "4IPzdn": "Primary Developers", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", "4Z3t5i": "Use imgproxy to compress images", "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Create Account", "5oTnfy": "Buy Handle", "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", "5u6iEc": "Transfer to Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "Send zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Get an identifier", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Likes ({n})", "6uMqL1": "Unpaid", "7+Domh": "Notes", "7BX/yC": "Account Switcher", "7hp70g": "NIP-05", - "7xzTiH": "{action} to {target}", "8/vBbP": "Reposts ({n})", "89q5wc": "Confirm Reposts", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "name too long", "8v1NN+": "Pairing phrase", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Next", "9HU8vw": "Reply", "9SvQep": "Follows {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning Invoice", "ADmfQT": "Parent", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "This author has been muted", "Adk34V": "Setup your Profile", "Ai8VHU": "Unlimited note retention on Snort relay", @@ -92,6 +107,7 @@ "BOr9z/": "Snort is an open source project built by passionate people in their free time", "BWpuKl": "Update", "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Logout", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "Show latest {n} notes", "DcL8P+": "Supporter", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transfer", "E8a4yq": "Follow some popular accounts", "ELbg9p": "Data Providers", "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Translate to {lang}", "EcZF24": "Custom Relays", "EcglP9": "Key", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Buy", "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", "F3l7xL": "Add Account", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL to forward zaps to", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Done!", "FSYL8G": "Trending Users", "FdhSU2": "Claim Now", @@ -129,28 +148,32 @@ "G1BGCg": "Select Wallet", "GFOoEE": "Salt", "GL8aXW": "Bookmarks ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Claim your included Snort nostr address", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Dislike", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Key..", "H0JBH6": "Log Out", "H6/kLh": "Order Paid!", "HAlOn1": "Name", - "HF4YnO": "Watch Live!", "HFls6j": "name will be available later", "HOzFdo": "Muted", "HWbkEK": "Clear cache and reload", "HbefNb": "Open Wallet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", "IEwZvs": "Are you sure you want to unpin this note?", "IKKHqV": "Follows", "INSqIz": "Twitter username...", "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Subscriptions", "JCIgkj": "Username", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Delete", "K7AkdL": "Show", "KAhAcM": "Enter LNDHub config", - "KLo3SP": "Reason: {reason}", "KQvWvD": "Deleted", "KWuDfz": "I have saved my keys, continue", "KahimY": "Unknown event kind: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Other Connections", + "LR1XjT": "Pin too short", "LXxsbk": "Anonymous", "LgbKvU": "Comment", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Debug Menus", "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", "MI2jkA": "Not available:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Buying {item}", "N2IrpM": "Confirm", + "NAidKb": "Notifications", "NAuFNH": "You already have a subscription of this type, please renew or pay", "NNSu3d": "Import Twitter Follows", "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", @@ -192,7 +219,6 @@ "OLEm6z": "Unknown login error", "OQXnew": "You subscription is still active, you can't renew yet", "ORGv1Q": "Created", - "P04gQm": "All zaps sent to this note will be received by the following LNURL", "P61BTu": "Copy Event JSON", "P7FD0F": "System (Default)", "P7nJT9": "Total today (UTC): {amount} sats", @@ -207,7 +233,6 @@ "QxCuTo": "Art by {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Subscription", - "R1fEdZ": "Forward Zaps", "R81upa": "People you follow", "RDZVQL": "Check", "RahCRH": "Expired", @@ -217,19 +242,19 @@ "RoOyAh": "Relays", "Rs4kCE": "Bookmark", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Update Lightning Address", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "LN Address Proxy", "ShdEie": "Mark all read", "Sjo1P4": "Custom", "Ss0sWu": "Pay Now", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "Pending Subscriptions", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Users must accept the content warning to show the content of your note.", "Up5U7K": "Block", @@ -239,15 +264,16 @@ "VR5eHw": "Public key (npub/nprofile)", "VlJkSk": "{n} muted", "VnXp8Z": "Avatar", - "VtPV/B": "Login with Extension (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "How do keys work?", "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", "W2PiAr": "{n} Blocked", "W9355R": "Unmute", "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "e.g. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reactions", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Enter pairing phrase", "ZKORll": "Activate Now", "ZLmyG9": "Contributors", - "ZUZedV": "Lightning Donation:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Show more", "b12Goz": "Mnemonic", "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bLZL5a": "Get Address", "bQdA2k": "Sensitive Content", "bep9C3": "Public Key", "bfvyfs": "Anon", "brAXSu": "Pick a username", "bxv59V": "Just now", "c+oiJe": "Install Extension", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "LN Address", "dOQCL8": "Display name", "e61Jf3": "Coming soon", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Mark All Read", "eHAneD": "Reaction emoji", "eJj8HD": "Get Verified", @@ -297,6 +326,7 @@ "fWZYP5": "Pinned", "filwqD": "Read", "flnGvv": "What's on your mind?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Saved", "g5pX+a": "About", "g985Wp": "Failed to send vote", @@ -310,9 +340,9 @@ "h8XMJL": "Badges", "hK5ZDk": "the world", "hMzcSq": "Messages", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Supports", "hicxcO": "Show replies", + "hmZ3Bz": "Media", "hniz8Z": "here", "i/dBAR": "Zap Pool", "iCqGww": "Reactions ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL translations", "iGT1eE": "Prevent fake accounts from imitating you", "iNWbVV": "Handle", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", "ieGrWo": "Follow", "itPgxd": "Profile", @@ -381,9 +410,11 @@ "qMx1sA": "Default Zap amount", "qUJTsT": "Blocked", "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qfmMQh": "This note has been muted", "qkvYUb": "Add to Profile", "qmJ8kD": "Translation failed", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Enter wallet password", "rT14Ow": "Add Relays", @@ -392,7 +423,10 @@ "rmdsT4": "{n} days", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rudscU": "Failed to load follows, please try again later", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", "tOdNiY": "Dark", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Supporter Badge", "u/vOPu": "Paid", "u4bHcR": "Check out the code here: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "Reposts need to be manually confirmed", + "uc0din": "Send sats splits to", "usAvMr": "Edit Profile", "ut+2Cd": "Get a partner identifier", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", - "vU71Ez": "Paying with {wallet}", "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", "vhlWFg": "Poll Options", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Edit", "wLtRCF": "Your key", + "wSZR47": "Submit", "wWLwvh": "Anon", "wYSD2L": "Nostr Adddress", "wih7iJ": "name is blocked", + "wofVHy": "Moderation", "wqyN/i": "Find out more info about {service} at {link}", "wtLjP6": "Copy ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "This note has been marked as sensitive, click here to reveal", "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", "xIoGG9": "Go to", "xJ9n2N": "Your public key", "xKflGN": "{username}''s Follows on Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Language", "yCLnBC": "LNURL or Lightning Address", "yCmnnm": "Read global from", + "zCb8fX": "Weight", "zFegDD": "Contact", "zINlao": "Owner", "zQvVDJ": "All", "zcaOTs": "Zap amount in sats", "zjJZBd": "You're ready!", "zonsdq": "Failed to load LNURL service", - "zvCDao": "Automatically show latest notes" + "zvCDao": "Automatically show latest notes", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/es_ES.json b/packages/app/src/translations/es_ES.json index 13b7985b..18552253 100644 --- a/packages/app/src/translations/es_ES.json +++ b/packages/app/src/translations/es_ES.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "Tipo de Zap", + "+tShPg": "following", "+vA//S": "Ingresos", "+vIQlC": "Asegúrate de guardar ésta contraseña para gestionar tu NIP-05 en el futuro", "+vVZ/G": "Conectar", @@ -11,6 +12,7 @@ "/JE/X+": "Soporte de Cuenta", "/PCavi": "Público", "/RD0e2": "Nostr utiliza firmas digitales para proveer de notas inmutables que pueden ser replicadas a muchos relays para que tu contenido esté distribuido redundantemente.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Haz tu perfil más fácil de encontrar y compartir", "/n5KSF": "{n} ms", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "Buscar...", "0jOEtS": "LNURL inválida", "0mch2Y": "el nombre tiene caracteres inválidos", + "0uoY11": "Show Status", "0yO7wF": "{n} seg", "1A7TZk": "¿Qué es Snort y cómo funciona?", "1Mo59U": "¿Estás seguro de que quieres eliminar esta nota de tus favoritos?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Marcadores", "2k0Cv+": "No me gusta ({n})", "2ukA4d": "{n} horas", + "3KNMbJ": "Articles", "3Rx6Qo": "Avanzado", "3cc4Ct": "Claro", "3gOsZq": "traductores", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Seguidores", "3xCwbZ": "O", "3yk8fB": "Cartera", + "40VR6s": "Nostr Connect", "450Fty": "Ninguno", "47FYwb": "Cancelar", "4IPzdn": "Desarrolladores principales", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plbes es uno de los primeros proveedores de NIP-05 y ofrece una buena colección de dominios a precios razonables.", "4Z3t5i": "Usar imgproxy para comprimir imágenes", "4rYCjn": "Mensajes guardados", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Crear una cuenta", "5oTnfy": "Comprar Handle", "5rOdPG": "Una vez configurada la extensión del gestor de claves y generada una clave, puedes seguir nuestro flujo de nuevos usuarios para configurar tu perfil y ayudarte a encontrar gente interesante en Nostr.", "5u6iEc": "Transferir a Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "Enviar zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Obténer un identificador de Snort", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Me gusta ({n})", "6uMqL1": "No pagado", "7+Domh": "Notas", "7BX/yC": "Conmutador de Cuentas", "7hp70g": "NIP-05", - "7xzTiH": "{action} a {target}", "8/vBbP": "Reposts ({n})", "89q5wc": "Confirmar reposts", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zapear {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "el nombre es demasiado largo", "8v1NN+": "Frase de emparejado", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Siguiente", "9HU8vw": "Responder", "9SvQep": "Siguiendo a {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Factura Lightning", "ADmfQT": "Pariente", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "Este autor ha sido silenciado", "Adk34V": "Configura tu Perfil", "Ai8VHU": "Retención de nota ilimitada en relé Snort", @@ -92,6 +107,7 @@ "BOr9z/": "Snort es un proyecto de código abierto creado por personas apasionadas en su tiempo libre.", "BWpuKl": "Actualización", "BcGMo+": "Las notas contienen contenido textual, el uso más popular de dichas notas es almacenar mensajes parecidos a \"tweets\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Salir", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "Mostrar últimas {n} notas", "DcL8P+": "Seguidor", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transferir", "E8a4yq": "Sigue a cuentas populares", "ELbg9p": "Data Providers", "EPYwm7": "Tu clave privada es tu contraseña. Si pierdes esta clave perderás acceso a tu cuenta. Cópiala y guárdala en un lugar seguro. No hay manera de recuperar tu clave privada.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Traducir a {lang}", "EcZF24": "Custom Relays", "EcglP9": "Clave", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Comprar", "Eqjl5K": "Sólo los identificadores de Snort y nuestros socios se mostrarán con un dominio colorido, pero todos los servicios están soportados.", "F+B3x1": "Nos hemos asociado con nostrplebs.com para darte más opciones", "F3l7xL": "Añadir Cuenta", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL para reenviar zaps", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Hecho!", "FSYL8G": "Trending Users", "FdhSU2": "Reclama Ahora", @@ -129,28 +148,32 @@ "G1BGCg": "Seleccionar cartera", "GFOoEE": "Salt", "GL8aXW": "Marcadores ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Reclama tu dirección de Snort nostr incluida", "Gcn9NQ": "Enlace magnet", "GspYR7": "{n} No me gusta", + "Gxcr08": "Broadcast Event", "H+vHiz": "Llave hexagonal..", "H0JBH6": "Salir", "H6/kLh": "Orden pagada!", "HAlOn1": "Nombre", - "HF4YnO": "Watch Live!", "HFls6j": "el nombre estará disponible más tarde", "HOzFdo": "Silenciados", "HWbkEK": "Clear cache and reload", "HbefNb": "Abrir Wallet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Gracias por usar Snort, por favor considera donar si puede.", "IEwZvs": "¿Estás seguro de que quieres desmarcar esta nota?", "IKKHqV": "Follows", "INSqIz": "Usuario en Twitter...", "IUZC+0": "Esto significa que nadie puede modificar notas que tú has creado y que todo el mundo puede verificar que las notas que leen han sido escritas por ti.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Suscripciones", "JCIgkj": "Usuario", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Eliminar", "K7AkdL": "Mostrar", "KAhAcM": "Introducir configuración de LNDHub", - "KLo3SP": "Motivo: {reason}", "KQvWvD": "Eliminado", "KWuDfz": "He guardado mis claves, continuar", "KahimY": "Evento de tipo desconocido {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Otras conexiones", + "LR1XjT": "Pin too short", "LXxsbk": "Anónimo", "LgbKvU": "Comentario", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Menús de desarrolladores", "MBAYRO": "Se mostrarán opciones adicionales para desarrolladores en el menú de las notas", "MI2jkA": "No disponible:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Comprando {item}", "N2IrpM": "Confirmar", + "NAidKb": "Notifications", "NAuFNH": "Ya tiene una suscripción de este tipo, por favor renueve o pague", "NNSu3d": "Importar Seguidores de Twitter", "NdOYJJ": "Hmmm no hay nada que mostrarte aquí... Echa un vistazo a la {newUsersPage} para seguir a algunas cuentas recomendadas!", @@ -192,7 +219,6 @@ "OLEm6z": "Error de inicio de sesión", "OQXnew": "Tu suscripción todavía está activa, aún no puedes renovarla", "ORGv1Q": "Creado", - "P04gQm": "Todos los zaps enviados a esta nota serán recibidos por la siguiente LNURL", "P61BTu": "Copiar JSON", "P7FD0F": "Sistema (por defecto)", "P7nJT9": "Total hoy (UTC): {amount} sáb", @@ -207,7 +233,6 @@ "QxCuTo": "Arte de {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Suscripción", - "R1fEdZ": "Reenviar Zaps", "R81upa": "People you follow", "RDZVQL": "Comprobar", "RahCRH": "Caducada", @@ -217,19 +242,19 @@ "RoOyAh": "Relays", "Rs4kCE": "Favorito", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Actualizar la dirección de relámpago", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "LN Dirección Proxy", "ShdEie": "Mark all read", "Sjo1P4": "Personalizar", "Ss0sWu": "Paga Ahora", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "sal hexagonal..", "Tpy00S": "People", "UDYlxu": "Suscripciones pendientes", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Los usuarios deben aceptar la advertencia de contenido para mostrar el contenido de tu nota.", "Up5U7K": "Bloquear", @@ -239,15 +264,16 @@ "VR5eHw": "Clave pública (npub/nprofile)", "VlJkSk": "{n} silenciados", "VnXp8Z": "Avatar", - "VtPV/B": "Iniciar sesión con extensión (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "¿Cómo funcionan las claves?", "W1yoZY": "Parece que no tienes ninguna suscripción, puedes obtener una {link}", "W2PiAr": "{n} Bloqueados", "W9355R": "Desilenciar", "WONP5O": "Encuentra a tus seguidos de twitter en nostr (información facilitada por {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "p.e. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemónico", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reacciones", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Introduce la frase de emparejamiento", "ZKORll": "Activar Ahora", "ZLmyG9": "Colaboradores", - "ZUZedV": "Donación de rayos:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Configurar el perfil", "a5UPxh": "Apoya a las plataformas y desarrolladores que proporcionan servicios de verificación", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Mostrar más", "b12Goz": "Mnemotécnico", "b5vAk0": "Tu usuario actuará como una dirección de relámpago y redirigirá a tu LNURL o dirección de relámpago seleccionada", + "bLZL5a": "Get Address", "bQdA2k": "Contenido sensible", "bep9C3": "Llave pública", "bfvyfs": "Anon", "brAXSu": "Elige un nombre de usuario", "bxv59V": "Justo ahora", "c+oiJe": "Instalar extensión", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Si tiene una consulta sobre su pedido de NIP-05, envíe un mensaje privado {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "Dirección Lightning", "dOQCL8": "Nombre", "e61Jf3": "Disponible próximamente", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Marcar todo como leído", "eHAneD": "Emoji de reacción", "eJj8HD": "Verifica tu perfil", @@ -297,6 +326,7 @@ "fWZYP5": "Fijado", "filwqD": "Leer", "flnGvv": "¿Qué tienes en mente?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Guardado", "g5pX+a": "Sobre ti", "g985Wp": "No se pudo enviar el voto", @@ -310,9 +340,9 @@ "h8XMJL": "Medallas", "hK5ZDk": "el mundo", "hMzcSq": "Mensajes", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Soporta", "hicxcO": "Mostrar respuestas", + "hmZ3Bz": "Media", "hniz8Z": "aquí", "i/dBAR": "Zap Pool", "iCqGww": "Reacciones ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "Traducción de DeepL", "iGT1eE": "Evita que cuentas falsas se hagan pasar por ti", "iNWbVV": "Usuario", - "iUsU2x": "Mint: {url}", "iXPL0Z": "No se puede iniciar sesión con la clave privada en una conexión insegura, por favor utilice una extensión gestora de claves de Nostr", "ieGrWo": "Seguir", "itPgxd": "Perfil", @@ -381,9 +410,11 @@ "qMx1sA": "Importe por defecto", "qUJTsT": "Bloqueados", "qdGuQo": "Tu Clave Privada (no la compartas con nadie) es", + "qfmMQh": "This note has been muted", "qkvYUb": "Añadir a tu perfil", "qmJ8kD": "Traducción fallida", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Introducir contraseña de cartera", "rT14Ow": "Añadir Relays", @@ -392,7 +423,10 @@ "rmdsT4": "{n} días", "rrfdTe": "Esta es la misma tecnología que usa Bitcoin y ha demostrado ser extremadamente segura.", "rudscU": "Error al cargar seguidos, inténtalo más tarde", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort está diseñado para tener una experience similar a Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Ocupar nombres y hacerse pasar por terceros no está permitido. Snort y nuestros socios se reservan el derecho de terminar tu identificador (no tu cuenta, nadie te la puede arrebatar) si violas ésta norma.", "tOdNiY": "Oscuro", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Insignia de Apoyo", "u/vOPu": "Pagado", "u4bHcR": "Mira el código aquí: {link}", - "uD/N6c": "Zapear {target} {n} sats", "uSV4Ti": "Los reposts necesitarán ser confirmados", + "uc0din": "Send sats splits to", "usAvMr": "Editar Perfil", "ut+2Cd": "Obtén un identificador de nuestros socios", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} otro} other{& {n} otros}}", - "vU71Ez": "Pagando con {wallet}", "vZ4quW": "NIP-05 es un sistema de verification basado en DNS que permite verificarte como un usuario real.", "vhlWFg": "Opciones de encuesta", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Editar", "wLtRCF": "Tu llave", + "wSZR47": "Submit", "wWLwvh": "Anónimo", "wYSD2L": "Dirección Nostr", "wih7iJ": "el nombre no está permitido", + "wofVHy": "Moderation", "wqyN/i": "Aprende más sobre {service} en {link}", "wtLjP6": "Copiar ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Esta nota ha sido marcada como sensible, haz clic aquí para revelarla", "x82IOl": "Silenciar", + "xIcAOU": "Votes by {type}", "xIoGG9": "Ir a", "xJ9n2N": "Tu clave pública", "xKflGN": "Seguidos de {username} en Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Idioma", "yCLnBC": "LNURL o dirección lightning", "yCmnnm": "Leer global de", + "zCb8fX": "Weight", "zFegDD": "Contacto", "zINlao": "Dueño", "zQvVDJ": "Todos", "zcaOTs": "Cantidad en sats", "zjJZBd": "Estás listo!", "zonsdq": "Error al contactar con el servicio LNURL", - "zvCDao": "Mostrar notas nuevas automáticamente" + "zvCDao": "Mostrar notas nuevas automáticamente", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/fa_IR.json b/packages/app/src/translations/fa_IR.json index e040243a..018f5b51 100644 --- a/packages/app/src/translations/fa_IR.json +++ b/packages/app/src/translations/fa_IR.json @@ -1,24 +1,27 @@ { "+D82kt": "مطمئنید می خواهید بازنشر کنید: {id}", "+PzQ9Y": "اکنون بپردازید", - "+Vxixo": "Secret Group Chat", + "+Vxixo": "گپ گروهی خصوصی", "+aZY2h": "نوع زپ", + "+tShPg": "following", "+vA//S": "ورود", "+vIQlC": "لطفا از ذخیره پسورد روبرو به منظور مدیرت شناسه کاربریتان در آینده اطمینان حاصل کنید", "+vVZ/G": "وصل شدن", - "+xliwN": "{name} reposted", + "+xliwN": "{name} بازنشر کرد", "/4tOwT": "رد کردن", "/JE/X+": "پشتیبانی حساب کاربری", "/PCavi": "عمومی", "/RD0e2": "ناستر از تکنولوژی امضای دیجیتال برای فراهم نمودن یادداشت های غیرقابل دستکاری استفاده می کند که می تواند به طور امن در رله های زیادی تکرار شود تا انبار اضافی برای محتوایتان تامین کند.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "یافتن و اشتراک گذاری نمایه خود را آسان تر کنید", "/n5KSF": "{n} میلی ثانیه", - "00LcfG": "Load more", + "00LcfG": "بارگیری بیشتر", "08zn6O": "استخراج کلید", "0Azlrb": "مدیریت", "0BUTMv": "جستجو...", "0jOEtS": "LNURL نامعتبر", "0mch2Y": "نام دارای کاراکتر غیرمجاز است", + "0uoY11": "Show Status", "0yO7wF": "{n} ثانیه", "1A7TZk": "اسنورت چیست و چگونه کار می کند؟", "1Mo59U": "مطمئنید می خواهید این یادداشت را از نشانک ها خارج کنید؟", @@ -34,6 +37,7 @@ "2a2YiP": "{n} نشانک", "2k0Cv+": "({n}) ناپسند", "2ukA4d": "{n} ساعت", + "3KNMbJ": "Articles", "3Rx6Qo": "پیشرفته", "3cc4Ct": "روشن", "3gOsZq": "مترجمان", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} دنبال کننده", "3xCwbZ": "یا", "3yk8fB": "کیف پول", + "40VR6s": "Nostr Connect", "450Fty": "هیچ‌کدام", "47FYwb": "لغو", "4IPzdn": "توسعه دهندگان اصلی", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs یکی از اولین فراهم کنندگان NIP-05 در این محیط است و مجموعه خوبی از دامین ها را با قیمت مناسب ارائه می کند", "4Z3t5i": "از imgproxy برای فشرده سازی تصویر استفاده کنید", "4rYCjn": "یادداشت برای خود", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "ساخت حساب کاربری", "5oTnfy": "خرید شناسه", "5rOdPG": "هنگامی که افزونه مدیریت کلید خود را راه‌اندازی کنید و یک کلید بسازید،می توانید روند کاربر جدید را پی بگیرید تا نمایه خود را راه بیندازید، و نیز به شما کمک می کند تا افراد مورد علاقه تان را در ناستر برای دنبال کردن بیابید.", "5u6iEc": "انتقال به کلید عمومی", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "نام کاربری در ناستر یکتا نیست. آدرس ناستر، آدرس یکتای قابل خواندن توسط انسان است که پس از ثبت مختص شما است.", "5ykRmX": "ارسال زپ", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "تصویر از {host} آورده نشد، اینجا کلیک کنید تا مستقیم بارگیری شود", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "یک شناسه بگیرید", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "({n}) لایک", "6uMqL1": "پرداخت نشده", "7+Domh": "یادداشت ها", "7BX/yC": "تعویض حساب کاربری", "7hp70g": "NIP-05", - "7xzTiH": "{action} به {target}", "8/vBbP": "({n}) بازنشر", "89q5wc": "تایید بازنشر", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "زپ کردن {n} ساتوشی", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "نام زیادی طولانی است", "8v1NN+": "عبارت جفت", + "8xNnhi": "Nostr Extension", "9+Ddtu": "بعدی", "9HU8vw": "پاسخ", "9SvQep": "دنبال شوندگان {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "صورتحساب لایتنینگ", "ADmfQT": "والد", "AGNz71": "همه را {n} ساتوشی زپ کن", + "AN0Z7Q": "Muted Words", "ASRK0S": "این نویسنده بیصدا شده است", "Adk34V": "نمایه خود را راه اندازی کنید", "Ai8VHU": "نگهداری نامحدود یادداشت ها روی رله", @@ -92,6 +107,7 @@ "BOr9z/": "اسنورت یک پروژه متن-باز است که افراد علاقه مند در اوقات فراغت خود ساخته اند", "BWpuKl": "به‌روزآوری", "BcGMo+": "یادداشت محتوای متنی نگه می دارند، بیشترین موارد استفاده از این یادداشت ها ذخیره پیام هایی \"مشابه توییت\" است.", + "C1LjMx": "Lightning Donation", "C5xzTC": "نسخه ویژه", "C81/uG": "خروج از حساب", "C8HhVE": "پیشنهاد برای دنبال کردن", @@ -106,22 +122,25 @@ "DZzCem": "{n} یادداشت اخیر را نشان بده", "DcL8P+": "پشتیبان", "Dh3hbq": "زپ خودکار", + "Dn82AL": "Live", "DtYelJ": "انتقال", "E8a4yq": "حساب های کاربری مشهور را دنبال کنید", "ELbg9p": "ارائه دهندگان داده", "EPYwm7": "کلید خصوصی شما گذرواژه شماست. اگر این کلید را گم کنید، دسترسی به حسابتان را از دست می دهید! آن را کپی کرده و در جای امنی نگه دارد. هیچ راهی برای بازیابی کلید خصوصی وجود ندارد.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "همگانی", "Ebl/B2": "ترجمه به {lang}", "EcZF24": "رله های سفارشی", "EcglP9": "کلید", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "خرید", "Eqjl5K": "فقط اسنورت و شرکای شناسایی یکپارچه ما نام دامنه رنگارنگ ارائه می کنند، ولی شما می توانید از خدمات دیگران استفاده کنید.", "F+B3x1": "ما با nostrplebs.com هم شریک شده ایم تا گزینه های بیشتری در اختیار شما بگذاریم", "F3l7xL": "افزودن حساب کاربری", "FDguSC": "{n} زپ", - "FP+D3H": "LNURL برای فرستادن زپ به", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "انجام شد!", - "FSYL8G": "Trending Users", + "FSYL8G": "پسند کاربران", "FdhSU2": "حالا برداشت کن", "FfYsOb": "خطایی رخ داده است!", "FmXUJg": "شما را دنبال می کند", @@ -129,47 +148,54 @@ "G1BGCg": "انتخاب کیف پول", "GFOoEE": "نمک", "GL8aXW": "نشانک ها ({n})", - "GSye7T": "Lightning Address", + "GQPtfk": "Join Stream", + "GSye7T": "آدرس لایتنینگ", "GUlSVG": "آدرس ناستر شامل اسنورت خود را بردارید", "Gcn9NQ": "لینک مگنت", "GspYR7": "{n} ناپسند", + "Gxcr08": "Broadcast Event", "H+vHiz": "کلید Hex...", "H0JBH6": "خروج", "H6/kLh": "سفارش پرداخت شده!", "HAlOn1": "نام", - "HF4YnO": "Watch Live!", "HFls6j": "نام در دسترس خواهد بود", "HOzFdo": "بيصدا شده", "HWbkEK": "پاک کردن حافظه کش و بارگیری مجدد", "HbefNb": "باز کردن کیف پول", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "برای استفاده از اسنورت سپاسگذاریم، لطفا در صورت امکان کمک مالی بفرمایید.", "IEwZvs": "مطمئنید می خواهید سنجاق یادداشت را بردارید؟", - "IKKHqV": "Follows", + "IKKHqV": "دنبال شوندگان", "INSqIz": "نام کاربری توییتر...", "IUZC+0": "این بدان معنی است که هیچ کس نمی تواند یادداشتی که ایجاد کرده اید را تغییر دهد و همه می توانند به آسانی تایید کنند که یادداشت هایی که می خوانند توسط شما ایجاد شده اند.", "Ig9/a1": "{n} ساتوشی به {name} فرستاده شد", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "داغ‌ترین‌ یادداشت ها", "J+dIsA": "اشتراک", "JCIgkj": "نام کاربری", + "JGrt9q": "Send sats to {name}", "JHEHCk": "زپ ({n})", "JPFYIM": "آدرس لایتنینگی وجود ندارد", - "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JeoS4y": "بازنشر", + "JjGgXI": "جستجوی کاربران", "JkLHGw": "وب سایت", "JymXbw": "کلید خصوصی", "K3r6DQ": "حذف", "K7AkdL": "نشان دادن", "KAhAcM": "پیکربندی LNDHub را وارد کنید", - "KLo3SP": "دلیل: {reason}", "KQvWvD": "حذف شده", "KWuDfz": "کلیدهای خود را ذخیره کرده ام، ادامه بده", "KahimY": "نوع ناشناخته رویداد: {kind}", "KoFlZg": "mint URL را وارد کنید", + "KtsyO0": "Enter Pin", "LF5kYT": "دیگر اتصالات", + "LR1XjT": "Pin too short", "LXxsbk": "ناشناس", "LgbKvU": "نظر", "Lu5/Bj": "در Zapstr بازکنید", - "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "Lw+I+J": "{n,plural,one {}=0{{name} زپ زد} other {{{name} و {n} نفر دیگر زپ زدند}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "منو اشکال زدایی", "MBAYRO": "\"کپی شناسه\" و \"کپی JSON رویداد\" را در منو زمینه هر پیام نشان می دهد", "MI2jkA": "در دسترس نیست:", @@ -177,9 +203,10 @@ "MRp6Ly": "نام کاربری توییتر", "MWTx65": "صفحه پیش‌فرض", "Mrpkot": "پرداخت برای اشتراک", - "MuVeKe": "Buy nostr address", + "MuVeKe": "خرید آدرس ناستر", "MzRYWH": "خرید {item}", "N2IrpM": "تایید", + "NAidKb": "Notifications", "NAuFNH": "درحال حاضر یک اشتراک از این نوع دارید، لطفا تجدید کنید یا بپردازید", "NNSu3d": "دنبال شوندگان توییر را بیاورید", "NdOYJJ": "همم چیزی اینجا نیست... {newUsersPage} را ببینید تا چند ناستریچ توصیه شده دنبال کنید!", @@ -192,7 +219,6 @@ "OLEm6z": "خطای ورود ناشناخته", "OQXnew": "اشتراک شما هنوز فعال است، نمی توانید تجدید کنید", "ORGv1Q": "ایجاد شد", - "P04gQm": "تمام زپ های ارسال شده به این یادداشت توسط LNURL روبرو دریافت می گردند", "P61BTu": "کپی رویداد JSON", "P7FD0F": "سیستم (پیش فرض)", "P7nJT9": "جمع امروز (UTC): {amount} ساتوشی", @@ -207,8 +233,7 @@ "QxCuTo": "هنر {name}", "Qxv0B2": "در حال حاضر {number} ساتوشی در استخر زپ خود دارید.", "R/6nsx": "اشتراک", - "R1fEdZ": "انتقال زپ", - "R81upa": "People you follow", + "R81upa": "کسانی که دنبال می کنید", "RDZVQL": "بررسی", "RahCRH": "منقضی شده", "RfhLwC": "توسط: {author}", @@ -217,20 +242,20 @@ "RoOyAh": "رله", "Rs4kCE": "نشانک", "RwFaYs": "چینش", + "SMO+on": "Send zap to {name}", "SOqbe9": "بروزرسانی آدرس لایتنینگ", "SP0+yi": "خرید اشتراک", - "SX58hM": "کپی", "SYQtZ7": "پروکسی آدرس لایتنینگ", - "ShdEie": "Mark all read", + "ShdEie": "علامت‌گذاری همه به‌ عنوان خوانده شده", "Sjo1P4": "سفارشی سازی", "Ss0sWu": "همین الان پرداخت کنید", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "رسانه درون یادداشت به طور خودکار برای افراد منتخب نمایش داده می شود، وگرنه فقط لینک نشان داده می شود", - "TMfYfY": "توکن کشو Cashu", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "افراد", "UDYlxu": "اشتراک معطل", - "ULotH9": "مبلغ: {amount} ساتوشی", - "UT7Nkj": "New Chat", + "UT7Nkj": "گپ جدید", "UUPFlt": "کاربر باید هشدار محتوا را بپذیرد تا محتوای یادداشت نمایش داده شود.", "Up5U7K": "مسدود کردن", "VBadwB": "همم، نمی توان افزونه ای برای مدیریت کلید یافت.. بارگیری دوباره صفحه را امتحان کنید.", @@ -239,15 +264,16 @@ "VR5eHw": "کلید عمومی (npub/nprofile)", "VlJkSk": "{n} بیصدا شده", "VnXp8Z": "آواتار", - "VtPV/B": "ورود با افزونه (NIP-07)", - "VvaJst": "View Wallets", + "VvaJst": "دیدن کیف پول", "Vx7Zm2": "کلیدها چگونه کار می کنند؟", "W1yoZY": "بنظر می رسد اشتراک ندارید، می توانید یکی تهیه کنید {link}", "W2PiAr": "{n} مسدود", "W9355R": "صدادار", "WONP5O": "دنبال شوندگان توییتر را در ناستر بیابید (داده های فراهم شده توسط{provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "برای مثال Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "میزبانان فایل", "XgWvGA": "واکنش ها", "Xopqkl": "مبلغ پیش فرض زپ شما {number} ساتوشی است، مقادیر نمونه از روی این محاسبه شده اند.", @@ -260,19 +286,21 @@ "Z4BMCZ": "عبارت جفت را وارد کنید", "ZKORll": "اکنون فعال کن", "ZLmyG9": "مشارکت کنندگان", - "ZUZedV": "کمک مالی به لایتنینگ:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "تنظیم نمایه", "a5UPxh": "توسعه دهندگان و پلتفرم های ارائه دهنده خدمات تایید NIP-05 را پیدا کن", "a7TDNm": "یادداشت ها در لحظه در سربرگ همگانی و یادداشت ها پخش می شوند", "aWpBzj": "نمایش بیشتر", "b12Goz": "یادسپارها", "b5vAk0": "شناسه شما به عنوان آدرس لایتنینگ عمل نموده و به LNURL انتخابی و آدرس لایتنینگ ارجاع خواهد داد", + "bLZL5a": "Get Address", "bQdA2k": "محتوای حساس", "bep9C3": "کلید عمومی", - "bfvyfs": "Anon", + "bfvyfs": "ناشناس", "brAXSu": "یک نام کاربری برگزینید", "bxv59V": "همین الان", "c+oiJe": "نصب افزونه", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "اگر درباره سفارش NIP-05 سوالی دارید لطفا مستقیما به {link} پیام دهید", "c3g2hL": "دوباره منتشر کن", "cFbU1B": "از Alby استفاده می کنید؟ به {link} بروید تا پیکربندی NWC خود را بگیرید!", @@ -287,16 +315,18 @@ "d7d0/x": "آدرس لایتنینگ", "dOQCL8": "نام نمایشی", "e61Jf3": "به زودی", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "علامت‌گذاری همه به‌عنوان خوانده شده", "eHAneD": "شکلک واکنش", "eJj8HD": "تایید کن", "eSzf2G": "یک زپ تک {nIn} ساتوشی مبلغ {nOut} ساتوشی به استخر زپ تخصیص خواهد داد.", - "eXT2QQ": "Group Chat", - "fBI91o": "Zap", + "eXT2QQ": "گپ گروهی", + "fBI91o": "زَپ", "fOksnD": "نمی توان رای داد زیرا این خدمات LNURL از زپ پشتیبانی نمی کند", "fWZYP5": "سنجاق شد", "filwqD": "خوانده شده", "flnGvv": "چی تو ذهنت میگذره؟", + "fqwcJ1": "On-chain Donation", "fsB/4p": "ذخیره شد", "g5pX+a": "درباره", "g985Wp": "رای ارسال نشد", @@ -306,13 +336,13 @@ "gXgY3+": "هنوز همه کلاینت ها از این پشتیبانی نمی کنند", "gczcC5": "اشتراک", "gjBiyj": "در حال بارگیری...", - "grQ+mI": "Proof of Work", + "grQ+mI": "اثبات کار", "h8XMJL": "مدال ها", "hK5ZDk": "جهان", "hMzcSq": "پیام‌ها", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "پشتیبانی", "hicxcO": "نمایش پاسخ ها", + "hmZ3Bz": "Media", "hniz8Z": "اینجا", "i/dBAR": "استخر زپ", "iCqGww": "({n}) واکنش", @@ -320,7 +350,6 @@ "iEoXYx": "مترجم DeepL", "iGT1eE": "از تقلید شدن توسط حساب های کاربری تقلبی جلوگیری کنید", "iNWbVV": "شناسه", - "iUsU2x": "Mint: {url}", "iXPL0Z": "در اتصال ناامن با کلید خصوصی نمی توان وارد شد، لطفا در عوض از افزونه مدیریت کلید ناستر استفاده کنید", "ieGrWo": "دنبال کردن", "itPgxd": "نمایه", @@ -334,9 +363,9 @@ "jzgQ2z": "{n} واکنش", "k2veDA": "نوشتن", "k7sKNy": "خدمات تایید NIP-05 خودمان، به پشتیبانی از توسعه این سایت کمک کنید و یک مدال ویژه درخشان در سایت ما بگیرید!", - "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kJYo0u": "{n,plural,one {}=0{{name} بازنشر کرد} other{{name} و {n} نفر دیگر بازنشر کردند}}", "kaaf1E": "الان", - "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "kuPHYE": "{n,plural,one {}=0{{name} پسندید} other{{name} و {n} نفر دیگر پسندیدند}}", "l+ikU1": "همه چیز در {plan}", "lBboHo": "اگر می خواهید آفزونه های پیماینده بیشتری امتحان کنید، {link} را ببینید!", "lCILNz": "هم اکنون خریداری کنید", @@ -368,10 +397,10 @@ "oJ+JJN": "چیزی پیدا نشد", "odFwjL": "فقط دنبال شوندگان", "odhABf": "ورود", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "ojzbwv": "هی، بنظر می رسد هنوز آدرس ناستر ندارید، باید یکی بگیرید! {link} را ببینید", "osUr8O": "از افزونه ها هم می توانید برا ورود به اکثر کلاینت های ناستر استفاده کنید.", "oxCa4R": "گرفتن یک شناسه میتواند به تایید هویت شما توسط کسانی که شما می شناسند کمک کند. افراد بسیاری می توانند نام کاربری @jack داشته باشند اما فقط یک jack@cash.app می تواند وجود داشته باشد.", - "p4N05H": "Upload", + "p4N05H": "بارگذاری", "p85Uwy": "اشتراک فعال", "pI+77w": "نسخه بازیابی قابل دانلود رله اسنورت", "puLNUJ": "سنجاق", @@ -381,9 +410,11 @@ "qMx1sA": "مبلغ پیش فرض زپ", "qUJTsT": "مسدود شده", "qdGuQo": "کلید خصوصی شما (با هیچ کس درمیان نگذارید)", + "qfmMQh": "This note has been muted", "qkvYUb": "افزودن به نمایه", "qmJ8kD": "ترجمه انجام نشد", - "qtWLmt": "Like", + "qtWLmt": "پسند", + "qz9fty": "Incorrect pin", "r3C4x/": "نرم افزار", "r5srDR": "رمز کیف‌پول را وارد کنید", "rT14Ow": "افزودن رله", @@ -392,7 +423,10 @@ "rmdsT4": "{n} روز", "rrfdTe": "این همان تکنولوژی است که در بیتکوین استفاده شده و اثبات شده که به شدت امن است.", "rudscU": "بارگیری دنبال شوندگان انجام نشد ، لطفاً بعداً دوباره امتحان کنید", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "اسنورت طوری طراحی شده تا تجربه مشابه توییتر ارائه دهد.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "جعل نام و جعل هویت مجاز نیست. در صورت سرپیچی، اسنورت و شرکا حق دارند شناسه کاربریتان را لغو کنند (نه حساب کاربریتان - هیچ کس نمیتواند آن را از شما بگیرد).", "tOdNiY": "تاریک", "th5lxp": "یادداشت را به زیرشاخه ای از رله نوشتنی تان بفرستید", @@ -400,28 +434,29 @@ "ttxS0b": "مدال پشتیبان", "u/vOPu": "پرداخت شده", "u4bHcR": "کد را اینجا ببینید: {link}", - "uD/N6c": "{n} ساتوشی به {target} زپ کن", "uSV4Ti": "بازنشر لازم است به طور دستی تایید شود", + "uc0din": "Send sats splits to", "usAvMr": "ویرایش نمایه", "ut+2Cd": "یک شناسه از شرکا بگیرید", - "v8lolG": "Start chat", + "v8lolG": "شروع گپ", "vOKedj": "{n,plural,one {}=1{و {n} دیگر} other{و {n} دیگر}}", - "vU71Ez": "با {wallet} پرداخت کن", "vZ4quW": "NIP-05 یک ویژگی بر مبنای DNS است که کمک می کند هویت شما به عنوان کاربر تصدیق شود.", "vhlWFg": "گزینه های نظرسنجی", - "vlbWtt": "Get a free one", + "vlbWtt": "یکی مجانی بگیرید", "vrTOHJ": "{amount} ساتوشی", - "vxwnbh": "Amount of work to apply to all published events", + "vxwnbh": "مقداری کاری که برای اِعمال همه رویدادهای منتشر شده باید انجام گیرد", "wEQDC6": "ویرایش", "wLtRCF": "کلید شما", + "wSZR47": "Submit", "wWLwvh": "ناشناس", "wYSD2L": "آدرس ناستر", "wih7iJ": "نام مسدود شده است", + "wofVHy": "Moderation", "wqyN/i": "درباره {service} در {link} بیشتر بدانید", "wtLjP6": "کپی کردن شناسه", "x/Fx2P": "با تقسیم بخشی از زپ های خود و اختصاص آن به استخر زپ، خدماتی را که استفاده می کنید تامین مالی نمایید!", - "x/q8d5": "این یادداشت به عنوان حساس علامت گذاری شده است، اینجا کلیک کنید تا آشکار شود", "x82IOl": "بيصدا", + "xIcAOU": "Votes by {type}", "xIoGG9": "برو به", "xJ9n2N": "کلید عمومی شما", "xKflGN": "دنبال شوندگان {username} در ناستر", @@ -433,11 +468,13 @@ "y1Z3or": "زبان", "yCLnBC": "LNURL یا آدرس لایتنینگ", "yCmnnm": "خبرنامه همگانی را بخوانید", + "zCb8fX": "Weight", "zFegDD": "مخاطب", "zINlao": "مالک", "zQvVDJ": "همه", "zcaOTs": "مبلغ زپ به ساتوشی", "zjJZBd": "شما آماده هستید!", "zonsdq": "خدمات LNURL بارگیری نشد", - "zvCDao": "به طور خودکار یادداشت های اخیر را نشان بده" + "zvCDao": "به طور خودکار یادداشت های اخیر را نشان بده", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/fi_FI.json b/packages/app/src/translations/fi_FI.json new file mode 100644 index 00000000..6105dea9 --- /dev/null +++ b/packages/app/src/translations/fi_FI.json @@ -0,0 +1,480 @@ +{ + "+D82kt": "Haluatko varmasti jakaa uudelleen: {id}", + "+PzQ9Y": "Maksa nyt", + "+Vxixo": "Salainen ryhmäkeskutelu", + "+aZY2h": "Zap tyyppi", + "+tShPg": "following", + "+vA//S": "Kirjautumiset", + "+vIQlC": "Muista tallentaa seuraava salasana pystyäksesi hallitsemaan käyttäjätunnustasi tulevaisuudessa", + "+vVZ/G": "Yhdistä", + "+xliwN": "{name} jakoi uudelleen", + "/4tOwT": "Ohita", + "/JE/X+": "Tilituki", + "/PCavi": "Julkinen", + "/RD0e2": "Nostr käyttää digitaalista allekirjoitusteknologiaa tarjotakseen väärentämättömiä viestejä, joita voidaan turvallisesti replikoida monille välittäjille tarjoten näin varastointia sisällöllesi.", + "/Xf4UW": "Lähetä nimettömiä käyttötietoja", + "/d6vEc": "Tee profiilistasi helpompi löytää ja jakaa", + "/n5KSF": "{n} ms", + "00LcfG": "Lataa lisää", + "08zn6O": "Vie avaimet", + "0Azlrb": "Hallitse", + "0BUTMv": "Etsi...", + "0jOEtS": "Virheellinen LNURL", + "0mch2Y": "nimi sisältää sallimattomia merkkejä", + "0uoY11": "Show Status", + "0yO7wF": "{n} sek", + "1A7TZk": "Mikä Snort on ja miten se toimii?", + "1Mo59U": "Haluatko varmasti poistaa tämän viestin kirjanmerkeistä?", + "1R43+L": "Anna Nostr Wallet Connect asetukset", + "1c4YST": "Yhdistetty: {node} 🎉", + "1iQ8GN": "Esikatselun vaihtaminen", + "1nYUGC": "{n} Seuraa", + "1udzha": "Keskustelut", + "2/2yg+": "Lisää", + "25V4l1": "Banneri", + "2IFGap": "Lahjoita", + "2LbrkB": "Anna salasana", + "2a2YiP": "{n} Kirjanmerkit", + "2k0Cv+": "Ei-tykkäyksiä ({n})", + "2ukA4d": "{n} tuntia", + "3KNMbJ": "Articles", + "3Rx6Qo": "Edistynyt", + "3cc4Ct": "Vaalea", + "3gOsZq": "Kääntäjät", + "3qnJlS": "Äänestät {amount} satsilla", + "3t3kok": "{n,plural,=1{{n} uusi viesti} muuta{{n} uutta viestiä}}", + "3tVy+Z": "{n} Seuraajaa", + "3xCwbZ": "TAI", + "3yk8fB": "Lompakko", + "40VR6s": "Nostr Connect", + "450Fty": "Ei mitään", + "47FYwb": "Peruuta", + "4IPzdn": "Pääkehittäjät", + "4L2vUY": "Uusi NIP-05-käyttäjätunnuksesi on:", + "4OB335": "Ei-tykkäys", + "4Vmpt4": "Nostr Plebs on yksi ensimmäisistä NIP-05-tarjoajista alalla ja tarjoaa kohtuuhintaisia verkkotunnuksia", + "4Z3t5i": "Käytä imgproxya pakataksesi kuvat", + "4rYCjn": "Muistiinpano itselle", + "5BVs2e": "zap", + "5CB6zB": "Zap-jaot", + "5JcXdV": "Luo tili", + "5oTnfy": "Osta käyttäjätunnus", + "5rOdPG": "Kun olet määrittänyt avainhallintaohjelman ja generoinut avaimen, voit seurata uusille käyttäjille tarkoitettua opasta määrittääksesi profiilisi ja löytääksesi kiinnostavia henkilöitä seurattavaksi Nostrissa.", + "5u6iEc": "Siirrä julkiselle avaimelle", + "5vMmmR": "Käyttäjänimet eivät ole yksilöllisiä Nostrissa. Nostr-osoite on yksilöllinen ihmisystävällinen osoitteesi, joka on yksilöllinen sinulle rekisteröinnin yhteydessä.", + "5ykRmX": "Lähetä zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", + "65BmHb": "Kuvan välittäminen epäonnistui palvelimelta {host}, napsauta tätä ladataksesi suoraan", + "6OSOXl": "Reason: {reason}", + "6Yfvvp": "Hanki tunniste", + "6bgpn+": "Kaikki asiakkaat eivät tue tätä, saatat silti saada joitakin zappeja ikään kuin zap-jako ei olisi määritetty", + "6ewQqw": "Tykkäyksiä ({n})", + "6uMqL1": "Maksamaton", + "7+Domh": "Viestit", + "7BX/yC": "Tilin vaihtaja", + "7hp70g": "NIP-05", + "8/vBbP": "Uudelleenjakoja ({n})", + "89q5wc": "Vahvista uudelleenjaot", + "8Kboo2": "Scan this QR code with your signer app to get started", + "8QDesP": "Zap {n} satsia", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Virheellinen zap-jako: {input}", + "8g2vyB": "nimi on liian pitkä", + "8v1NN+": "Pariliitoksen lause", + "8xNnhi": "Nostr Extension", + "9+Ddtu": "Seuraava", + "9HU8vw": "Vastaa", + "9SvQep": "Seuraa {n}", + "9WRlF4": "Lähetä", + "9gqH2W": "Kirjaudu sisään", + "9pMqYs": "Nostr-osoite", + "9wO4wJ": "Lightning-lasku", + "ADmfQT": "Vanhempi", + "AGNz71": "Zap kokonaan {n} satsia", + "AN0Z7Q": "Muted Words", + "ASRK0S": "Tämä kirjoittaja on mykistetty", + "Adk34V": "Aseta profiilisi", + "Ai8VHU": "Rajoittamaton viestien säilytys Snort-välittäjällä", + "AkCxS/": "Syy", + "AnLrRC": "Ei-Zap", + "AyGauy": "Kirjaudu sisään", + "B4C47Y": "nimi on liian lyhyt", + "B6+XJy": "zappattu", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Kirjoitusoikeus Snort-välittäjällä, 1 vuoden tapahtumahistorialla", + "BOUMjw": "Nostr-käyttäjiä {twitterUsername} ei löytynyt", + "BOr9z/": "Snort on avoimen lähdekoodin projekti, jota intohimoiset ihmiset tekevät vapaa-ajallaan", + "BWpuKl": "Päivitä", + "BcGMo+": "Viestit sisältävät tekstisisältöä, suosituin käyttötapa näille viesteille on tallentaa \"tweetin kaltaisia\" viestejä.", + "C1LjMx": "Lightning Donation", + "C5xzTC": "Premium", + "C81/uG": "Kirjaudu ulos", + "C8HhVE": "Ehdotettuja seurattavia", + "CHTbO3": "Epäonnistunut laskun lataus", + "CVWeJ6": "Suositut henkilöt", + "CmZ9ls": "{n} Mykistettyä", + "CsCUYo": "{n} satsia", + "Cu/K85": "Käännetty kieleltä {lang}", + "D+KzKd": "Zappää automaattisesti jokainen viesti, kun se ladataan", + "D3idYv": "Asetukset", + "DKnriN": "Lähetä satsit", + "DZzCem": "Näytä viimeisimmät {n} viestiä", + "DcL8P+": "Tukija", + "Dh3hbq": "Automaattiset zapit", + "Dn82AL": "Live", + "DtYelJ": "Siirto", + "E8a4yq": "Seuraa joitakin suosittuja tilejä", + "ELbg9p": "Datantoimittajat", + "EPYwm7": "Yksityisavaimesi on salasanasi. Jos menetät tämän avaimen, menetät pääsyn tilillesi! Kopioi se ja säilytä turvallisessa paikassa. Yksityisavainta ei voi palauttaa.", + "EQKRE4": "Show badges on profile pages", + "EWyQH5": "Yleinen", + "Ebl/B2": "Käännä kielelle {lang}", + "EcZF24": "Omat välittäjät", + "EcglP9": "Avain", + "EjFyoR": "On-chain Donation Address", + "EnCOBJ": "Osta", + "Eqjl5K": "Vain Snort ja kumppanuustunniste tarjoavat värikästä verkkotunnusta, mutta voit käyttää myös muiden palveluja.", + "F+B3x1": "Olemme myös kumppaneita nostrplebs.com:n kanssa tarjotaksemme sinulle lisää vaihtoehtoja", + "F3l7xL": "Lisää tili", + "FDguSC": "{n} Zappia", + "FMfjrl": "Show status messages on profile pages", + "FS3b54": "Valmis!", + "FSYL8G": "Suositut käyttäjät", + "FdhSU2": "Lunasta nyt", + "FfYsOb": "Tapahtui virhe!", + "FmXUJg": "seuraa sinua", + "G/yZLu": "Poista", + "G1BGCg": "Valitse lompakko", + "GFOoEE": "Suola", + "GL8aXW": "Kirjanmerkkejä ({n})", + "GQPtfk": "Join Stream", + "GSye7T": "Lightning-osoite", + "GUlSVG": "Lunasta sisältyvä Snort nostr-osoite", + "Gcn9NQ": "Magneettilinkki", + "GspYR7": "{n} Ei-tykkäys", + "Gxcr08": "Broadcast Event", + "H+vHiz": "Hex-avain..", + "H0JBH6": "Kirjaudu ulos", + "H6/kLh": "Tilaus maksettu!", + "HAlOn1": "Nimi", + "HFls6j": "nimi on myöhemmin saatavilla", + "HOzFdo": "Mykistetty", + "HWbkEK": "Tyhjennä välimuisti ja lataa uudelleen", + "HbefNb": "Avaa lompakko", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", + "IDjHJ6": "Kiitos Snortin käytöstä, harkitse tukemista, jos voit.", + "IEwZvs": "Haluatko varmasti poistaa tämän viestin kiinnitetyistä?", + "IKKHqV": "Seuraa", + "INSqIz": "Twitter-käyttäjätunnus...", + "IUZC+0": "Tämä tarkoittaa, että kukaan ei voi muokata luomiasi viestejä ja kaikki voivat helposti vahvistaa, että viestit ovat peräisin sinulta.", + "Ig9/a1": "Lähetettiin {n} satsia käyttäjälle {name}", + "IoQq+a": "Click here to load anyway", + "Ix8l+B": "Suositut viestit", + "J+dIsA": "Tilaukset", + "JCIgkj": "Käyttäjätunnus", + "JGrt9q": "Lähetä satseja käyttäjälle {name}", + "JHEHCk": "Zapit ({n})", + "JPFYIM": "Ei lightning-osoitetta", + "JeoS4y": "Jaa uudelleen", + "JjGgXI": "Etsi käyttäjiä", + "JkLHGw": "Verkkosivusto", + "JymXbw": "Yksityisavain", + "K3r6DQ": "Poista", + "K7AkdL": "Näytä", + "KAhAcM": "Anna LNDHub-asetukset", + "KQvWvD": "Poistettu", + "KWuDfz": "Olen tallentanut avaimeni, jatka", + "KahimY": "Tuntematon tapahtumatyyppi: {kind}", + "KoFlZg": "Anna mint-URL", + "KtsyO0": "Enter Pin", + "LF5kYT": "Muut yhteydet", + "LR1XjT": "Pin too short", + "LXxsbk": "Nimetön", + "LgbKvU": "Kommentoi", + "Lu5/Bj": "Avaa Zapstrissa", + "Lw+I+J": "{n,plural,=0{{name} zappasi} other{{name} & {n} muuta zappasivat}}", + "LwYmVi": "Zapit tässä viestissä jaetaan seuraaville käyttäjille.", + "M10zFV": "Nostr Connect", + "M3Oirc": "Debug-valikot", + "MBAYRO": "Näyttää \"Kopioi tunniste\" ja \"Kopioi tapahtuman JSON\" jokaisen viestin valikossa", + "MI2jkA": "Ei saatavilla:", + "MP54GY": "Lompakon salasana", + "MRp6Ly": "Twitter-käyttäjätunnus", + "MWTx65": "Oletussivu", + "Mrpkot": "Maksa tilaus", + "MuVeKe": "Osta nostr-osoite", + "MzRYWH": "Ostaa {item}", + "N2IrpM": "Vahvista", + "NAidKb": "Notifications", + "NAuFNH": "Sinulla on jo tämäntyyppinen tilaus, ole hyvä ja uudista tai maksa", + "NNSu3d": "Tuo Twitter-seuratut", + "NdOYJJ": "Hmm ei mitään täällä.. Katso {newUsersPage} seurataksesi joitakin suositeltuja nostricheja!", + "NepkXH": "Ei voida äänestää {amount} satsilla, aseta toinen oletuszap-määrä", + "NfNk2V": "Yksityisavaimesi", + "NndBJE": "Uusien käyttäjien sivu", + "O9GTIc": "Profiilikuva", + "OEW7yJ": "Zapit", + "OKhRC6": "Jaa", + "OLEm6z": "Tuntematon kirjautumisvirhe", + "OQXnew": "Tilauksesi on yhä aktiivinen, et voi uusia sitä vielä", + "ORGv1Q": "Luotu", + "P61BTu": "Kopioi tapahtuman JSON", + "P7FD0F": "Järjestelmä (oletus)", + "P7nJT9": "Tänään yhteensä (UTC): {amount} satsia", + "PCSt5T": "Asetukset", + "PLSbmL": "Sinun mnemonic-lauseesi", + "PamNxw": "Tuntematon tiedostotunniste: {name}", + "Pe0ogR": "Teema", + "PrsIg7": "Reaktiot näytetään jokaisella sivulla, jos poistettu käytöstä reaktioita ei näytetä", + "QDFTjG": "{n} Välittäjää", + "QWhotP": "Zap-pool toimii vain tuetuilla lompakkoyhteyksillä (WebLN, LNC, LNDHub tai Nostr Wallet Connect)", + "QawghE": "Voit vaihtaa käyttäjänimeäsi milloin tahansa.", + "QxCuTo": "Taidetta käyttäjältä {name}", + "Qxv0B2": "Sinulla on {number} satsia zap-poolissa.", + "R/6nsx": "Tilaus", + "R81upa": "Henkilöt joita seuraat", + "RDZVQL": "Tarkista", + "RahCRH": "Vanhentunut", + "RfhLwC": "Tekijä: {author}", + "RhDAoS": "Haluatko varmasti poistaa {id}", + "RjpoYG": "Viimeaikaiset", + "RoOyAh": "Välittäjät", + "Rs4kCE": "Kirjanmerkki", + "RwFaYs": "Lajittelu", + "SMO+on": "Lähetä satseja käyttäjälle {name}", + "SOqbe9": "Päivitä Lightning-osoite", + "SP0+yi": "Osta tilaus", + "SYQtZ7": "LN-osoitteen välityspalvelin", + "ShdEie": "Merkitse kaikki luetuiksi", + "Sjo1P4": "Mukautettu", + "Ss0sWu": "Maksa nyt", + "StKzTE": "The author has marked this note as a sensitive topic", + "TDR5ge": "Viestien mediasisältö näytetään automaattisesti valituille henkilöille, muille näytetään vain linkki", + "TP/cMX": "Ended", + "TpgeGw": "Hex-suola..", + "Tpy00S": "Ihmiset", + "UDYlxu": "Odottavat tilaukset", + "UT7Nkj": "Uusi keskustelu", + "UUPFlt": "Käyttäjien täytyy hyväksyä sisältövaroitus nähdäkseen viestisi sisällön.", + "Up5U7K": "Estä", + "VBadwB": "Hmm, avainhallintaohjelmaa ei löydy.. yritä ladata sivu uudelleen.", + "VN0+Fz": "Saldo: {amount} satsia", + "VOjC1i": "Valitse palvelu, johon haluat ladata liitteitä", + "VR5eHw": "Julkinen avain (npub/nprofile)", + "VlJkSk": "{n} mykistettyä", + "VnXp8Z": "Avatar", + "VvaJst": "Näytä lompakot", + "Vx7Zm2": "Miten avaimet toimivat?", + "W1yoZY": "Et ole vielä tilannut, mutta käy {link} tilataksesi.", + "W2PiAr": "{n} Estettyä", + "W9355R": "Poista mykistys", + "WONP5O": "Etsi Twitter-seurantasi Nostrista (tiedot tarjoaa {provider})", + "WvGmZT": "npub / nprofile / nostr-osoite", + "WxthCV": "esim. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Lähetä käyttötietoja", + "XICsE8": "Tiedostojen isäntäpalvelut", + "XgWvGA": "Reaktiot", + "Xopqkl": "Oletus zap-määräsi on {number} satsia, esimerkit on laskettu tämän mukaan.", + "XrSk2j": "Lunasta", + "XzF0aC": "Avainhallintaohjelmat ovat turvallisempia ja mahdollistavat helpon kirjautumisen mihin tahansa Nostr-asiakasohjelmaan, tässä joitakin tunnettuja laajennuksia:", + "Y31HTH": "Tue Snortin kehitystä", + "YDURw6": "Palvelun URL", + "YXA3AH": "Ota reaktiot käyttöön", + "Z0FDj+": "Tilaa Snort {plan} hintaan {price} ja saat seuraavat edut", + "Z4BMCZ": "Anna pariliitoslause", + "ZKORll": "Aktivoi nyt", + "ZLmyG9": "Avustajat", + "ZS+jRE": "Send zap splits to", + "Zr5TMx": "Aseta profiili", + "a5UPxh": "Tue kehittäjiä ja alustoja, jotka tarjoavat NIP-05-varmennuspalveluita", + "a7TDNm": "Viestit striimautuvat reaaliajassa yleiseen ja viestit-välilehteen", + "aWpBzj": "Näytä lisää", + "b12Goz": "Mnemonic-lause", + "b5vAk0": "Käyttäjätunnuksesi toimii kuin lightning-osoite ja uudelleenohjaa valitsemaasi LNURLiin tai lightning-osoitteeseen", + "bLZL5a": "Get Address", + "bQdA2k": "Arkaluontoinen sisältö", + "bep9C3": "Julkinen avain", + "bfvyfs": "Anon", + "brAXSu": "Valitse käyttäjänimi", + "bxv59V": "Juuri nyt", + "c+oiJe": "Asenna laajennus", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", + "c35bj2": "Jos sinulla on kysymyksiä NIP-05-tilauksestasi, lähetä yksityisviesti {link}", + "c3g2hL": "Lähetä uudelleen", + "cFbU1B": "Käytätkö Albya? Mene {link} saadaksesi NWC-asetuksesi!", + "cPIKU2": "Seuraa", + "cQfLWb": "URL..", + "cWx9t8": "Mykistä kaikki", + "cg1VJ2": "Yhdistä lompakko", + "cuP16y": "Usean tilin tuki", + "cuV2gK": "nimi on rekisteröity", + "cyR7Kh": "Takaisin", + "d6CyG5": "Historia", + "d7d0/x": "LN-osoite", + "dOQCL8": "Näyttönimi", + "e61Jf3": "Tulossa pian", + "e7VmYP": "Enter pin to unlock your private key", + "e7qqly": "Merkitse kaikki luetuksi", + "eHAneD": "Reaktion emoji", + "eJj8HD": "Hanki varmennus", + "eSzf2G": "Yksittäinen {nIn} satsin zap jakaa {nOut} satsia zap-pooliin.", + "eXT2QQ": "Ryhmächat", + "fBI91o": "Zap", + "fOksnD": "Ei voi äänestää, koska LNURL-palvelu ei tue zappeja", + "fWZYP5": "Kiinnitetty", + "filwqD": "Lue", + "flnGvv": "Mitä mielessä?", + "fqwcJ1": "On-chain Donation", + "fsB/4p": "Tallennettu", + "g5pX+a": "Tietoja", + "g985Wp": "Äänestyksen lähetys epäonnistui", + "gBdUXk": "Tallenna avaimesi!", + "gDZkld": "Snort on Nostr-käyttöliittymä, nostr on hajautettu protokolla viestien tallentamiseen ja jakamiseen.", + "gDzDRs": "Emoji reagoitaessa viestiin", + "gXgY3+": "Ei tueta kaikissa asiakasohjelmissa vielä", + "gczcC5": "Tilaa", + "gjBiyj": "Ladataan...", + "grQ+mI": "Todiste työstä", + "h8XMJL": "Merkit", + "hK5ZDk": "maailma", + "hMzcSq": "Viestit", + "hY4lzx": "Tukee", + "hicxcO": "Näytä vastaukset", + "hmZ3Bz": "Media", + "hniz8Z": "täällä", + "i/dBAR": "Zap-pooli", + "iCqGww": "Reaktioita ({n})", + "iDGAbc": "Hanki Snort-tunniste", + "iEoXYx": "DeepL käännökset", + "iGT1eE": "Estä väärennettyjä tilejä jäljittelemästä sinua", + "iNWbVV": "Käyttäjätunnus", + "iXPL0Z": "Ei voi kirjautua yksityisavaimella epäturvallisella yhteydellä, käytä Nostr-avainhallintaohjelmalaajennusta", + "ieGrWo": "Seuraa", + "itPgxd": "Profiili", + "izWS4J": "Lopeta seuraaminen", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} satia}}", + "jCA7Cw": "Esikatsele Snortissa", + "jMzO1S": "Sisäinen virhe: {msg}", + "jfV8Wr": "Takaisin", + "juhqvW": "Paranna kirjautumisturvallisuutta selainlaajennuksilla", + "jvo0vs": "Tallenna", + "jzgQ2z": "{n} Reaktiota", + "k2veDA": "Kirjoita", + "k7sKNy": "Oma NIP-05-varmennuspalvelumme, auta tukemaan tämän sivuston kehitystä ja saat kiiltävän erikoismerkin sivuillemme!", + "kJYo0u": "{n,plural,=0{{name} jakoi uudelleen} other{{name} & {n} muuta jakoivat uudelleen}}", + "kaaf1E": "nyt", + "kuPHYE": "{n,plural,=0{{name} tykkäsi} other{{name} & {n} muuta tykkäsivät}}", + "l+ikU1": "Kaikki {plan}-suunnitelmassa", + "lBboHo": "Jos haluat kokeilla muita, tarkista {link} lisää!", + "lCILNz": "Osta nyt", + "lD3+8a": "Maksa", + "lPWASz": "Snort nostr-osoite", + "lTbT3s": "Lompakon salasana", + "lgg1KN": "tili", + "ll3xBp": "Kuvan välityspalvelin", + "lnaT9F": "Seuraa {n}", + "lsNFM1": "Napsauta ladataksesi sisältö {link}", + "lvlPhZ": "Maksa lasku", + "mErPop": "Sinulla ei taida olla vielä yhtään, mutta voit korjata asian nopeasti täältä: {link}!", + "mH91FY": "Jokainen avustaja saa prosenttiosuuden kaikista lahjoituksista ja NIP-05-tilauksista, voit nähdä jakomäärät alla", + "mKAr6h": "Seuraa kaikkia", + "mKh2HS": "Tiedostojen latauspalvelu", + "mKhgP9": "{n,plural,=0{} =1{zappasi} other{zappasivat}}", + "mTJFgF": "Suosittu", + "mfe8RW": "Vaihtoehto: {n}", + "n1Whvj": "Vaihda", + "nDejmx": "Poista esto", + "nGBrvw": "Kirjanmerkit", + "nN9XTz": "Jaa ajatuksiasi {link}", + "nOaArs": "Aseta profiili", + "nWQFic": "Uusi", + "nn1qb3": "Tukeasi arvostetaan paljon", + "nwZXeh": "{n} estettyä", + "o6Uy3d": "Vain salainen avain voi julkaista (allekirjoittaa tapahtumia), kaikki muu kirjaa sinut sisään vain lukutilassa.", + "o7e+nJ": "{n} seuraajaa", + "oJ+JJN": "Mitään ei löytynyt :/", + "odFwjL": "Kirjaudu sisään", + "odhABf": "Kirjaudu sisään", + "ojzbwv": "Hei, näyttää siltä ettei sinulla ole vielä Nostr-osoitetta, sinun pitäisi hankkia sellainen! Katso {link}", + "osUr8O": "Voit myös käyttää näitä laajennuksia kirjautuaksesi useimpiin Nostr-sivustoihin.", + "oxCa4R": "Tunnisteen hankkiminen auttaa vahvistamaan profiilisi ihmisille jotka tuntevat sinut. Monella voi olla käyttäjänimi @jack, mutta vain yksi jack@cash.app.", + "p4N05H": "Lähetä", + "p85Uwy": "Aktiiviset tilaukset", + "pI+77w": "Ladattavat varmuuskopiot Snort-välittäjältä", + "puLNUJ": "Kiinnitä", + "pzTOmv": "Seuraajat", + "qD9EUF": "Sähköposti-silta Snort nostr-osoitteellesi", + "qDwvZ4": "Tuntematon virhe", + "qMx1sA": "Oletus zap-määrä", + "qUJTsT": "Estetty", + "qdGuQo": "Yksityisavaimen on (älä jaa tätä kenellekään)", + "qfmMQh": "This note has been muted", + "qkvYUb": "Lisää profiiliin", + "qmJ8kD": "Käännös epäonnistui", + "qtWLmt": "Tykkää", + "qz9fty": "Incorrect pin", + "r3C4x/": "Ohjelmisto", + "r5srDR": "Anna lompakon salasana", + "rT14Ow": "Lisää välittäjiä", + "reJ6SM": "On suositeltavaa käyttää jotakin seuraavista selainlaajennuksista, jos käytät tietokonetta turvataksesi avaimesi:", + "rfuMjE": "(Oletus)", + "rmdsT4": "{n} päivää", + "rrfdTe": "Tämä käyttää samaa teknologiaa kuin Bitcoin ja on osoittautunut erittäin turvalliseksi.", + "rudscU": "Seurannan lataaminen epäonnistui, yritä myöhemmin uudelleen", + "sKDn4e": "Show Badges", + "sUNhQE": "käyttäjä", + "sWnYKw": "Snort on suunniteltu tarjoamaan Twitterin kaltainen käyttökokemus", + "sZQzjQ": "Zap-jaon jäsentäminen epäonnistui: {input}", + "svOoEH": "Nimien hamstraus ja identiteettivarkaus eivät ole sallittua. Snort ja kumppanimme pidättävät oikeuden lopettaa käyttäjätunnuksesi (ei tiliäsi - sitä kukaan ei voi ottaa pois) tämän säännön rikkomisesta.", + "tOdNiY": "Tumma", + "th5lxp": "Lähetä viesti osajoukolle kirjoitusvälittäjiäsi", + "thnRpU": "NIP-05-varmennuksen hankkiminen voi auttaa:", + "ttxS0b": "Tukijamerkki", + "u/vOPu": "Maksettu", + "u4bHcR": "Tarkista koodi täältä: {link}", + "uSV4Ti": "Uudelleenjaot vaativat manuaalisen vahvistuksen", + "uc0din": "Send sats splits to", + "usAvMr": "Muokkaa profiilia", + "ut+2Cd": "Hanki kumppanitunniste", + "v8lolG": "Aloita keskustelu", + "vOKedj": "{n,plural,=1{& {n} muu} other{& {n} muuta}}", + "vZ4quW": "NIP-05 on DNS-pohjainen varmennusspesifikaatio, joka auttaa vahvistamaan sinut oikeaksi käyttäjäksi.", + "vhlWFg": "Äänestyksen vaihtoehdot", + "vlbWtt": "Hanki ilmainen", + "vrTOHJ": "{amount} satsia", + "vxwnbh": "Tehtävän työmäärä julkaistuille tapahtumille", + "wEQDC6": "Muokkaa", + "wLtRCF": "Avaimesi", + "wSZR47": "Submit", + "wWLwvh": "Anon", + "wYSD2L": "Nostr-osoite", + "wih7iJ": "nimi on estetty", + "wofVHy": "Moderation", + "wqyN/i": "Lue lisää {service} palvelusta osoitteessa {link}", + "wtLjP6": "Kopioi tunniste", + "x/Fx2P": "Tue käyttämiesi palveluiden kehitystä jakamalla osa zapeistasi rahastoon!", + "x82IOl": "Mykistä", + "xIcAOU": "{type}:n äänet", + "xIoGG9": "Siirry", + "xJ9n2N": "Julkinen avaimesi", + "xKflGN": "{username}''n seuranta Nostrissa", + "xQtL3v": "Avaa lukitus", + "xaj9Ba": "Tarjoaja", + "xbVgIm": "Lataa media automaattisesti", + "xhQMeQ": "Vanhenee", + "xmcVZ0": "Etsi", + "y1Z3or": "Kieli", + "yCLnBC": "LNURL tai Lightning-osoite", + "yCmnnm": "Lue yleinen", + "zCb8fX": "Paino", + "zFegDD": "Ota yhteyttä", + "zINlao": "Omistaja", + "zQvVDJ": "Kaikki", + "zcaOTs": "Satsien zap-määrä", + "zjJZBd": "Olet valmis!", + "zonsdq": "Epäonnistunut LNURL-palvelun lataus", + "zvCDao": "Näytä uusimmat viestit automaattisesti", + "zwb6LR": "Mint: {url}" +} diff --git a/packages/app/src/translations/fr_FR.json b/packages/app/src/translations/fr_FR.json index b3b50bf4..3c521a24 100644 --- a/packages/app/src/translations/fr_FR.json +++ b/packages/app/src/translations/fr_FR.json @@ -1,30 +1,33 @@ { "+D82kt": "Êtes-vous sûr que vous voulez republier: {id}", - "+PzQ9Y": "Payout Now", - "+Vxixo": "Secret Group Chat", + "+PzQ9Y": "Payer Maintenant", + "+Vxixo": "Chat de Groupe Secret", "+aZY2h": "Type de Zap", - "+vA//S": "Logins", + "+tShPg": "following", + "+vA//S": "Connexions", "+vIQlC": "Assurez-vous d'enregistrer le mot de passe suivant afin de gérer votre identifiant à l'avenir", - "+vVZ/G": "Connexion", - "+xliwN": "{name} reposted", + "+vVZ/G": "Connecter", + "+xliwN": "{name} a reposté", "/4tOwT": "Passer", - "/JE/X+": "Prise en charge du compte", + "/JE/X+": "Support de compte", "/PCavi": "Publique", "/RD0e2": "Nostr utilise la technologie de signature numérique pour fournir des notes inviolables qui peuvent être répliquées en toute sécurité sur de nombreux relais pour fournir un stockage redondant de votre contenu.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Rendez votre profil plus facile à trouver et à partager", "/n5KSF": "{n} ms", - "00LcfG": "Load more", - "08zn6O": "Export Keys", - "0Azlrb": "Manage", + "00LcfG": "Charger plus", + "08zn6O": "Exporter les clés", + "0Azlrb": "Gérer", "0BUTMv": "Chercher...", "0jOEtS": "LNURL invalide", "0mch2Y": "le nom contient des caractères non autorisés", + "0uoY11": "Show Status", "0yO7wF": "{n} secondes", "1A7TZk": "Qu'est-ce que Snort et comment ça marche ?", "1Mo59U": "Êtes-vous sûr de vouloir supprimer cette note de vos favoris ?", - "1R43+L": "Enter Nostr Wallet Connect config", + "1R43+L": "Accéder à la configuration de Nostr Wallet Connect", "1c4YST": "Connecté à : {node} 🎉", - "1iQ8GN": "Prévisualisation du changement de fenêtre", + "1iQ8GN": "Activer/désactiver la prévisualisation", "1nYUGC": "{n} Abonnements", "1udzha": "Conversations", "2/2yg+": "Ajouter", @@ -33,7 +36,8 @@ "2LbrkB": "Saisissez le mot de passe", "2a2YiP": "{n} Favoris", "2k0Cv+": "N'aime pas ({n})", - "2ukA4d": "{n} hours", + "2ukA4d": "{n} heures", + "3KNMbJ": "Articles", "3Rx6Qo": "Avancé", "3cc4Ct": "Clair", "3gOsZq": "Traducteurs", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Abonnés", "3xCwbZ": "OU", "3yk8fB": "Portefeuille", + "40VR6s": "Nostr Connect", "450Fty": "Aucun", "47FYwb": "Annuler", "4IPzdn": "Développeurs principaux", @@ -49,26 +54,35 @@ "4OB335": "Ne pas aimer", "4Vmpt4": "Nostr Plebs est l'un des premiers fournisseurs NIP-05 dans l'espace et offre une bonne collection de domaines à des prix raisonnables", "4Z3t5i": "Utiliser imgproxy pour compresser les images", - "4rYCjn": "Note pour moi-même", - "5JcXdV": "Create Account", - "5oTnfy": "Buy Handle", + "4rYCjn": "Note à Soi-même", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", + "5JcXdV": "Créer un compte", + "5oTnfy": "Acheter Identifiant", "5rOdPG": "Une fois que vous avez configuré votre extension de gestionnaire de clés et généré une clé, vous pouvez suivre notre flux d'utilisateurs pour configurer votre profil et vous aider à trouver des personnes intéressantes à suivre sur Nostr.", "5u6iEc": "Transférer vers la clé publique", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "Les noms d'utilisateur ne sont pas uniques sur Nostr. L'adresse nostr est votre adresse unique lisible par l'homme qui vous est unique lors de votre inscription.", "5ykRmX": "Envoyer zap", - "65BmHb": "Failed to proxy image from {host}, click here to load directly", - "6Yfvvp": "Get an identifier", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", + "65BmHb": "Échec du proxy de l'image de {host}, cliquez ici pour la charger directement", + "6OSOXl": "Reason: {reason}", + "6Yfvvp": "Obtenir un identifiant", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Aime ({n})", - "6uMqL1": "Unpaid", + "6uMqL1": "Non payé", "7+Domh": "Notes", - "7BX/yC": "Account Switcher", + "7BX/yC": "Changer de compte", "7hp70g": "NIP-05", - "7xzTiH": "{action} à {target}", - "8/vBbP": "Republie ({n})", + "8/vBbP": "Reposte ({n})", "89q5wc": "Confirmer la republication", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zapper {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "le nom est trop long", "8v1NN+": "Phrase d'appairage", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Suivant", "9HU8vw": "Répondre", "9SvQep": "Suit {n}", @@ -77,26 +91,28 @@ "9pMqYs": "Adresse Nostr", "9wO4wJ": "Facture Lightning", "ADmfQT": "Parent", - "AGNz71": "Zap All {n} sats", - "ASRK0S": "Cet auteur a été mis en sourdine", + "AGNz71": "Zapper tous les sats {n}", + "AN0Z7Q": "Muted Words", + "ASRK0S": "Cet auteur a été rendu muet", "Adk34V": "Configurer votre profil", - "Ai8VHU": "Unlimited note retention on Snort relay", + "Ai8VHU": "Conservation illimitée des notes sur le relais Snort", "AkCxS/": "Raison", "AnLrRC": "Non-Zap", "AyGauy": "Se Connecter", "B4C47Y": "le nom est trop court", "B6+XJy": "zappé", "B6H7eJ": "nsec, npub, nip-05, hex", - "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BGCM48": "Accès en écriture au relais Snort, avec conservation des évènements pendant 1 an", "BOUMjw": "Aucun utilisateur nostr trouvé pour {twitterUsername}", "BOr9z/": "Snort est un projet open source construit par des passionnés pendant leur temps libre", - "BWpuKl": "Update", + "BWpuKl": "Actualiser", "BcGMo+": "Les notes contiennent du texte, l'utilisation la plus courante de ces notes est de stocker des messages similaires à des tweets.", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Se déconnecter", - "C8HhVE": "Suggested Follows", + "C8HhVE": "Utilisateurs recommandés", "CHTbO3": "Échec du chargement de la facture", - "CVWeJ6": "Trending People", + "CVWeJ6": "Personnes tendances", "CmZ9ls": "{n} Mis en sourdine", "CsCUYo": "{n} sats", "Cu/K85": "Traduit de {lang}", @@ -104,133 +120,142 @@ "D3idYv": "Paramètres", "DKnriN": "Envoyer des sats", "DZzCem": "Afficher les {n} dernières notes", - "DcL8P+": "Supporter", + "DcL8P+": "Contributeurs", "Dh3hbq": "Zap automatique", + "Dn82AL": "Live", "DtYelJ": "Transférer", "E8a4yq": "Suivez quelques comptes populaires", - "ELbg9p": "Data Providers", + "ELbg9p": "Fournisseurs de données", "EPYwm7": "Votre clé privée est votre mot de passe. Si vous perdez cette clé, vous perdrez l'accès à votre compte ! Copiez-le et conservez-le en lieu sûr. Il n'y a aucun moyen de réinitialiser votre clé privée.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Traduire en {lang}", - "EcZF24": "Custom Relays", + "EcZF24": "Relais personnalisés", "EcglP9": "Clé", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Acheter", - "Eqjl5K": "Seuls Snort et notre identifiant de partenaire d'intégration vous donnent un nom de domaine coloré, mais vous pouvez également utiliser d'autres services.", - "F+B3x1": "Nous nous sommes également associés à nosrplebs.com pour vous offrir plus d'options", - "F3l7xL": "Add Account", + "Eqjl5K": "Seul Snort et l'identifiant de notre partenaire d'intégration vous donnent un nom de domaine coloré, mais vous pouvez également utiliser d'autres services.", + "F+B3x1": "Nous nous sommes également associés à nosrplebs.com pour vous offrir plus d'options", + "F3l7xL": "Ajouter un Compte", "FDguSC": "{n} Zaps", - "FP+D3H": "LNRURL où transférer les Zap", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Fait!", - "FSYL8G": "Trending Users", - "FdhSU2": "Claim Now", + "FSYL8G": "Utilisateurs tendances", + "FdhSU2": "Obtenir maintenant", "FfYsOb": "Une erreur est survenue!", "FmXUJg": "vous suit", "G/yZLu": "Retirer", "G1BGCg": "Sélectionnez un portefeuille", "GFOoEE": "Sel", "GL8aXW": "Favoris ({n})", - "GSye7T": "Lightning Address", - "GUlSVG": "Claim your included Snort nostr address", + "GQPtfk": "Join Stream", + "GSye7T": "Adresse Lightning", + "GUlSVG": "Réclamez votre adresse nostr Snort incluse", "Gcn9NQ": "Lien Magnet", "GspYR7": "{n} N'aime pas", + "Gxcr08": "Broadcast Event", "H+vHiz": "Clé hexagonale..", "H0JBH6": "Se Déconnecter", "H6/kLh": "Commande Payée!", "HAlOn1": "Nom", - "HF4YnO": "Watch Live!", "HFls6j": "le nom sera disponible plus tard", "HOzFdo": "Mis en sourdine", - "HWbkEK": "Clear cache and reload", + "HWbkEK": "Vider le cache et recharger", "HbefNb": "Ouvrir le Wallet", - "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", + "IDjHJ6": "Merci d'utiliser Snort, envisagez de faire un don si vous pouvez.", "IEwZvs": "Êtes-vous sûr de vouloir désépingler cette note?", - "IKKHqV": "Follows", - "INSqIz": "Nom d'utilisateur Twitter...", + "IKKHqV": "Abonnements", + "INSqIz": "Nom d'utilisateur Twitter...", "IUZC+0": "Cela signifie que personne ne peut modifier les notes que vous avez créées et que tout le monde peut facilement vérifier que les notes qu'ils lisent sont créées par vous.", - "Ig9/a1": "Sent {n} sats to {name}", - "Ix8l+B": "Trending Notes", - "J+dIsA": "Subscriptions", - "JCIgkj": "Nom d'utilisateur", + "Ig9/a1": "Envoyé {n} sats à {name}", + "IoQq+a": "Click here to load anyway", + "Ix8l+B": "Notes tendances", + "J+dIsA": "Abonnements", + "JCIgkj": "Nom d’utilisateur", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", - "JPFYIM": "No lightning address", - "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JPFYIM": "Aucune adresse Lightning", + "JeoS4y": "Reposter", + "JjGgXI": "Rechercher des utilisateurs", "JkLHGw": "Site Internet", - "JymXbw": "Private Key", + "JymXbw": "Clé Privée", "K3r6DQ": "Supprimer", "K7AkdL": "Montrer", "KAhAcM": "Entrez la configuration LNDHub", - "KLo3SP": "Raison : {reason}", "KQvWvD": "Supprimé", "KWuDfz": "J'ai enregistré mes clés, continuer", "KahimY": "Type d'événement inconnu : {kind}", - "KoFlZg": "Enter mint URL", + "KoFlZg": "Saisir l'URL de mint", + "KtsyO0": "Enter Pin", "LF5kYT": "Autres Connexions", + "LR1XjT": "Pin too short", "LXxsbk": "Anonyme", "LgbKvU": "Commenter", - "Lu5/Bj": "Open on Zapstr", - "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "Lu5/Bj": "Ouvrir avec Zapstr", + "Lw+I+J": "{n,plural,=0{{name} a zappé} other{{name} & {n} autres ont zappé}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Menus de débogage", "MBAYRO": "Affiche \"Copy ID\" et \"Copy Event JSON\" dans le menu contextuel de chaque message", "MI2jkA": "Pas disponible:", "MP54GY": "Mot de passe du portefeuille", "MRp6Ly": "Nom d'utilisateur Twitter", "MWTx65": "Page par défaut", - "Mrpkot": "Pay for subscription", - "MuVeKe": "Buy nostr address", + "Mrpkot": "Payer pour l'abonnement", + "MuVeKe": "Acheter une adresse nostr", "MzRYWH": "Acheter {item}", "N2IrpM": "Confirmer", - "NAuFNH": "You already have a subscription of this type, please renew or pay", - "NNSu3d": "Import Twitter Follows", - "NdOYJJ": "Hmm rien ici .. Essayez {newUsersPage} pour suivre quelques recommandations de naustriches!", + "NAidKb": "Notifications", + "NAuFNH": "Vous avez déjà un abonnement de ce type, veuillez renouveler ou payer", + "NNSu3d": "Importer des abonnements Twitter", + "NdOYJJ": "Hmm rien ici.. Essayez {newUsersPage} pour suivre quelques recommandations de nostriches!", "NepkXH": "Impossible de voter avec {amount} sats, veuillez définir un autre montant zap par défaut", "NfNk2V": "Votre clé privée", "NndBJE": "Page des nouveaux utilisateurs", - "O9GTIc": "Profile picture", + "O9GTIc": "Photo du profil", "OEW7yJ": "Zaps", "OKhRC6": "Partager", "OLEm6z": "Erreur de connexion inconnue", - "OQXnew": "You subscription is still active, you can't renew yet", - "ORGv1Q": "Created", - "P04gQm": "Tous les zaps envoyés à cette note seront reçus par le LNURL suivant", + "OQXnew": "Votre abonnement est toujours actif, vous ne pouvez pas le renouveler pour le moment", + "ORGv1Q": "Créé", "P61BTu": "Copier l'événement JSON", "P7FD0F": "Système (Défaut)", "P7nJT9": "Total aujourd'hui (UTC) : {amount} sats", "PCSt5T": "Préférences", "PLSbmL": "Votre phrase mnémonique", - "PamNxw": "Unknown file header: {name}", + "PamNxw": "En-tête du fichier inconnu : {name}", "Pe0ogR": "Thème", "PrsIg7": "Les réactions seront affichées sur chaque page, si désactivées aucune réaction ne sera affichée", "QDFTjG": "{n} Relais", - "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QWhotP": "Zap Pool ne fonctionne que si vous utilisez l'une des connexions de portefeuille supportées (WebLN, LNC, LNDHub ou Nostr Wallet Connect)", "QawghE": "Vous pouvez modifier votre nom d'utilisateur à tout moment.", "QxCuTo": "Illustration par {name}", - "Qxv0B2": "You currently have {number} sats in your zap pool.", - "R/6nsx": "Subscription", - "R1fEdZ": "Zaps transféré", - "R81upa": "People you follow", + "Qxv0B2": "Vous avez actuellement {number} sats dans votre réserve de zap.", + "R/6nsx": "Abonnement", + "R81upa": "Personnes que vous suivez", "RDZVQL": "Vérifier", "RahCRH": "Expiré", "RfhLwC": "Par : {author}", "RhDAoS": "Êtes-vous sûr que vous voulez supprimer {id}", - "RjpoYG": "Recent", + "RjpoYG": "Récent", "RoOyAh": "Relais", "Rs4kCE": "Favori", - "RwFaYs": "Sort", - "SOqbe9": "Update Lightning Address", - "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", - "SYQtZ7": "LN Address Proxy", - "ShdEie": "Mark all read", + "RwFaYs": "Trier", + "SMO+on": "Send zap to {name}", + "SOqbe9": "Mettre à jour l'adresse Lightning", + "SP0+yi": "Acheter un abonnement", + "SYQtZ7": "Proxy d'adresse LN", + "ShdEie": "Tout marquer comme lu", "Sjo1P4": "Personnaliser", - "Ss0sWu": "Pay Now", - "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "Ss0sWu": "Payer maintenant", + "StKzTE": "The author has marked this note as a sensitive topic", + "TDR5ge": "Les médias dans les notes seront automatiquement affichés pour les personnes sélectionnées, sinon seul le lien sera affiché", + "TP/cMX": "Ended", "TpgeGw": "Sel Hex..", - "Tpy00S": "People", + "Tpy00S": "Personnes", "UDYlxu": "Demandes en attente", - "ULotH9": "Amount: {amount} sats", - "UT7Nkj": "New Chat", + "UT7Nkj": "Nouvelle Discussion", "UUPFlt": "Les utilisateur-trice-s doivent accepter l'avertissement de contenu pour afficher le contenu de votre note.", "Up5U7K": "Bloquer", "VBadwB": "Hmm, impossible de trouver une extension de gestionnaire de clés. Essayez de recharger la page.", @@ -239,64 +264,69 @@ "VR5eHw": "Clé publique (npub/nprofile)", "VlJkSk": "{n} mis en sourdine", "VnXp8Z": "Avatar", - "VtPV/B": "Connexion avec extension (NIP-07)", - "VvaJst": "View Wallets", + "VvaJst": "Voir les portefeuilles", "Vx7Zm2": "Comment fonctionnent les clés ?", - "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W1yoZY": "Il semble que vous n'avez aucun abonnement, vous pouvez en obtenir un {link}", "W2PiAr": "{n} Bloqué", "W9355R": "Retirer sourdine", "WONP5O": "Trouvez vos suivis Twitter sur nostr (Données fournies par {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "par exemple Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnémonique", - "XICsE8": "File hosts", + "XECMfW": "Send usage metrics", + "XICsE8": "Hébergeurs de fichiers", "XgWvGA": "Réactions", - "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", - "XrSk2j": "Redeem", + "Xopqkl": "Votre montant de zap par défaut est {number} sats, les valeurs d'exemple sont calculées à partir de ceci.", + "XrSk2j": "Retirer", "XzF0aC": "Les extensions de gestionnaire de clés sont plus sécurisées et vous permettent de vous connecter facilement à n'importe quel client Nostr, voici quelques extensions réputées :", "Y31HTH": "Aidez à financer le développement de Snort", "YDURw6": "URL de service", "YXA3AH": "Activer les réactions", - "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z0FDj+": "Abonnez-vous à Snort {plan} pour {price} et recevez les récompenses suivantes", "Z4BMCZ": "Entrez la phrase d'appairage", "ZKORll": "Activer Maintenant", "ZLmyG9": "Contributeurs", - "ZUZedV": "Don éclair :", - "Zr5TMx": "Setup profile", + "ZS+jRE": "Send zap splits to", + "Zr5TMx": "Configurer le profil", "a5UPxh": "Financer les développeurs et plateformes fournissant des services de vérification NIP-05", - "a7TDNm": "Notes will stream in real time into global and notes tab", + "a7TDNm": "Les notes seront diffusées en temps réel dans l'onglet global et dans l'onglet notes", "aWpBzj": "Montrer plus", - "b12Goz": "Mnemonic", - "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "b12Goz": "Mnémonique", + "b5vAk0": "Votre identifiant agira comme une adresse Lightning et redirigera vers l'adresse LNURL ou Lightning de votre choix", + "bLZL5a": "Get Address", "bQdA2k": "Contenu sensible", - "bep9C3": "Public Key", - "bfvyfs": "Anon", + "bep9C3": "Clé publique", + "bfvyfs": "Anonyme", "brAXSu": "Choisis un nom d'utilisateur", "bxv59V": "Juste maintenant", "c+oiJe": "Installer l'extension", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Si vous avez une question concernant votre commande NIP-05, veuillez contacter en DM {link}", - "c3g2hL": "Broadcast Again", - "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "c3g2hL": "Republier", + "cFbU1B": "Vous utilisez Alby ? Allez sur {link} pour obtenir votre configuration NWC !", "cPIKU2": "Abonnements", "cQfLWb": "URL..", "cWx9t8": "Tout mettre en sourdine", "cg1VJ2": "Connecter un portefeuille", - "cuP16y": "Multi account support", + "cuP16y": "Support multi-comptes", "cuV2gK": "le nom est enregistré", "cyR7Kh": "Retourner", "d6CyG5": "Historique", "d7d0/x": "Adresse LN", "dOQCL8": "Nom à afficher", - "e61Jf3": "Coming soon", + "e61Jf3": "Prochainement", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Marquer tout comme lu", "eHAneD": "Émoji de réaction", "eJj8HD": "Se faire vérifier", - "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", - "eXT2QQ": "Group Chat", + "eSzf2G": "Un seul zap de {nIn} sats allouera {nOut} sats à la pool de zap.", + "eXT2QQ": "Discussion de groupe", "fBI91o": "Zap", "fOksnD": "Impossible de voter car le service LNURL ne prend pas en charge les zaps", "fWZYP5": "Épinglé", "filwqD": "Lire", "flnGvv": "Qu'avez-vous en tête?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Enregistré", "g5pX+a": "À propos", "g985Wp": "Échec de l'envoi du vote", @@ -304,44 +334,43 @@ "gDZkld": "Snort est une interface utilisateur Nostr, nostr est un protocole décentralisé pour enregistrer et distribuer des "notes".", "gDzDRs": "Emoji à envoyer en cas de réaction à une note", "gXgY3+": "Tous les clients ne supportent pas encore ceci", - "gczcC5": "Subscribe", + "gczcC5": "S'abonner", "gjBiyj": "Chargement...", - "grQ+mI": "Proof of Work", + "grQ+mI": "Preuve de Travail", "h8XMJL": "Badges", "hK5ZDk": "le monde", "hMzcSq": "Messages", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Supporte", "hicxcO": "Afficher les réponses", - "hniz8Z": "here", + "hmZ3Bz": "Media", + "hniz8Z": "ici", "i/dBAR": "Zap Pool", "iCqGww": "Réactions ({n})", "iDGAbc": "Obtenir un identifiant Snort", - "iEoXYx": "DeepL translations", + "iEoXYx": "Traductions DeepL", "iGT1eE": "Empêcher les faux comptes de vous imiter", "iNWbVV": "Gérer", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Impossible de se connecter avec une clé privée via une connexion non sécurisée, veuillez utiliser une extension de gestionnaire de clés Nostr à la place", "ieGrWo": "Suivre", "itPgxd": "Profil", "izWS4J": "Ne plus suivre", "jA3OE/": "{n} {n, plural, =1 {sat} other {sats}}", "jCA7Cw": "Aperçu sur snort", - "jMzO1S": "Internal error: {msg}", + "jMzO1S": "Erreur interne : {msg}", "jfV8Wr": "Retourner", "juhqvW": "Améliorez la sécurité de connexion avec les extensions de navigateur", "jvo0vs": "Sauvegarder", "jzgQ2z": "{n} Réactions", "k2veDA": "Écrire", "k7sKNy": "Notre propre service de vérification NIP-05, aidez à soutenir le développement de ce site et obtenez un badge spécial brillant sur notre site !", - "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kJYo0u": "{n,plural,=0{{name} a reposté} other{{name} & {n} autres ont reposté}}", "kaaf1E": "maintenant", - "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", - "l+ikU1": "Everything in {plan}", + "kuPHYE": "{n,plural,=0{{name} a liké} other{{name} & {n} autres ont liké}}", + "l+ikU1": "Tout dans {plan}", "lBboHo": "Si vous voulez en essayer d'autres, consultez {link} pour en savoir plus!", "lCILNz": "Acheter Maintenant", "lD3+8a": "Payer", - "lPWASz": "Snort nostr address", + "lPWASz": "Adresse nostr Snort", "lTbT3s": "Mot de passe du portefeuille", "lgg1KN": "compte", "ll3xBp": "Service proxy d'images", @@ -353,14 +382,14 @@ "mKAr6h": "Suivre tout", "mKh2HS": "Service d'hébergement de fichiers", "mKhgP9": "{n,plural,=0{} =1{zappé} other{zappé}}", - "mTJFgF": "Popular", + "mTJFgF": "Populaire", "mfe8RW": "Option : {n}", - "n1Whvj": "Switch", + "n1Whvj": "Changer", "nDejmx": "Débloquer", "nGBrvw": "Favoris", "nN9XTz": "Partagez vos pensées avec {link}", "nOaArs": "Configuration du profil", - "nWQFic": "Renew", + "nWQFic": "Renouveler", "nn1qb3": "Vos dons sont grandement appréciés", "nwZXeh": "{n} bloqué", "o6Uy3d": "Seule la clé secrète peut être utilisée pour publier (signer des événements), tout le reste vous connecte en mode lecture seule.", @@ -368,76 +397,84 @@ "oJ+JJN": "Aucun résultat :/", "odFwjL": "Abonnements seulement", "odhABf": "Se Connecter", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "ojzbwv": "Hé, il semble que vous n'avez pas encore d'adresse Nostr, vous devriez en obtenir une ! Consultez {link}", "osUr8O": "Vous pouvez également utiliser ces extensions pour vous connecter à la plupart des sites Nostr.", "oxCa4R": "L'obtention d'un identifiant permet de confirmer votre identité réelle aux personnes qui vous connaissent. De nombreuses personnes peuvent avoir un nom d'utilisateur @jack, mais il n'y a qu'un seul jack@cash.app.", - "p4N05H": "Upload", + "p4N05H": "Importer", "p85Uwy": "Abonnements actifs", - "pI+77w": "Downloadable backups from Snort relay", + "pI+77w": "Sauvegardes téléchargeables depuis le relais Snort", "puLNUJ": "Épingler", "pzTOmv": "Abonnés", - "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qD9EUF": "Email <> pont DM pour votre adresse Snort nostr", "qDwvZ4": "Une erreur inconnue s'est produite", "qMx1sA": "Montant des Zaps par défaut", "qUJTsT": "Bloqué", "qdGuQo": "Votre Clé Privée Est (ne la partagez avec personne)", + "qfmMQh": "This note has been muted", "qkvYUb": "Ajouter au Profil", "qmJ8kD": "La traduction a échoué", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Logiciel", "r5srDR": "Entrez le mot de passe du portefeuille", "rT14Ow": "Ajouter Relais", "reJ6SM": "Il est recommandé d'utiliser l'une des extensions de navigateur suivantes si vous êtes sur un ordinateur de bureau pour sécuriser votre clé :", "rfuMjE": "(Défaut)", - "rmdsT4": "{n} days", + "rmdsT4": "{n} jours", "rrfdTe": "Il s'agit de la même technologie que celle utilisée par Bitcoin et qui s'est avérée extrêmement sécurisée.", "rudscU": "Échec du chargement des suivis, veuillez réessayer plus tard", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort est conçu pour avoir une expérience similaire à Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Le squattage de noms et l'usurpation d'identité ne sont pas autorisés. Snort et nos partenaires se réservent le droit de résilier votre identifiant (pas votre compte - personne ne peut le retirer) pour avoir enfreint cette règle.", "tOdNiY": "Sombre", - "th5lxp": "Send note to a subset of your write relays", + "th5lxp": "Envoyer la note à un sous-ensemble de vos relais d'écriture", "thnRpU": "Obtenir la vérification NIP-05 peut aider :", - "ttxS0b": "Supporter Badge", + "ttxS0b": "Badge de Supporter", "u/vOPu": "Payé", "u4bHcR": "Découvrez le code ici : {link}", - "uD/N6c": "Zapper {target} {n} sats", "uSV4Ti": "Les republications seront automatiquement confirmées", + "uc0din": "Send sats splits to", "usAvMr": "Modifier le Profil", "ut+2Cd": "Obtenir un identifiant partenaire", - "v8lolG": "Start chat", + "v8lolG": "Démarrer la discussion", "vOKedj": "{n,plural,=1{& {n} autre} other{& {n} autres}}", - "vU71Ez": "Payer avec {wallet}", "vZ4quW": "NIP-05 est une spécification de vérification basée sur DNS qui permet de vous valider en tant qu'utilisateur réel.", "vhlWFg": "Choix proposés", - "vlbWtt": "Get a free one", + "vlbWtt": "En obtenir un gratuit", "vrTOHJ": "{amount} sats", - "vxwnbh": "Amount of work to apply to all published events", + "vxwnbh": "Quantité de travail à appliquer à tous les événements publiés", "wEQDC6": "Modifier", "wLtRCF": "Ta clé", + "wSZR47": "Submit", "wWLwvh": "Anon", - "wYSD2L": "Nostr Adddress", + "wYSD2L": "Adresse Nostr", "wih7iJ": "le nom est bloqué", + "wofVHy": "Moderation", "wqyN/i": "En savoir plus sur {service} sur {link}", "wtLjP6": "Copier Identifiant", - "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Cette note a été marquée comme sensible, cliquez ici pour la révéler", + "x/Fx2P": "Financez les services que vous utilisez en fractionnant une partie de tous vos zaps en un ensemble de fonds !", "x82IOl": "Mode Sourdine", + "xIcAOU": "Votes by {type}", "xIoGG9": "Aller à", "xJ9n2N": "Votre clé publique", "xKflGN": "{username}'' suit sur Nostr", "xQtL3v": "Déverrouiller", - "xaj9Ba": "Provider", + "xaj9Ba": "Fournisseur", "xbVgIm": "Charger automatiquement le média", - "xhQMeQ": "Expires", + "xhQMeQ": "Expire", "xmcVZ0": "Chercher", "y1Z3or": "Langue", - "yCLnBC": "LNURL or Lightning Address", + "yCLnBC": "LNURL ou adresse Lightning", "yCmnnm": "Lire le flux global depuis", + "zCb8fX": "Weight", "zFegDD": "Contacted", "zINlao": "Propriétaire", "zQvVDJ": "Tout", "zcaOTs": "Zapper montant en sats", "zjJZBd": "Tu es prêt!", "zonsdq": "Échec du chargement du service LNURL", - "zvCDao": "Afficher automatiquement les dernières notes" + "zvCDao": "Afficher automatiquement les dernières notes", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/he_IL.json b/packages/app/src/translations/he_IL.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/he_IL.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/hr_HR.json b/packages/app/src/translations/hr_HR.json index 75679a5c..3ccd6afc 100644 --- a/packages/app/src/translations/hr_HR.json +++ b/packages/app/src/translations/hr_HR.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "Vrsta Zap-a", + "+tShPg": "following", "+vA//S": "Prijave", "+vIQlC": "Obavezno spremite sljedeću lozinku kako biste u budućnosti mogli upravljati svojim računom", "+vVZ/G": "Poveži", @@ -11,6 +12,7 @@ "/JE/X+": "Podrška za račun", "/PCavi": "Javno", "/RD0e2": "Nostr koristi tehnologiju digitalnog potpisa kako bi pružio bilješke o zaštiti od neovlaštenog mijenjanja koje se mogu sigurno replicirati na mnoge releje kako bi se osigurala nepotrebna pohrana vašeg sadržaja.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Olakšajte pronalazak i dijeljenje svog profila", "/n5KSF": "{n} mikrosekundi", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "Pretraga...", "0jOEtS": "Nevažeći LNURL", "0mch2Y": "ime sadrži nepodržane znakove", + "0uoY11": "Show Status", "0yO7wF": "{n} sekundi", "1A7TZk": "Što je Snort i kako funkcionira?", "1Mo59U": "Jeste li sigurni da želite ukloniti ovu bilješku iz trake s oznakama?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Oznake", "2k0Cv+": "Ne sviđa se ({n})", "2ukA4d": "{n} sati", + "3KNMbJ": "Articles", "3Rx6Qo": "Napredno", "3cc4Ct": "Svijetlo", "3gOsZq": "Prevoditelji", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Pratitelji", "3xCwbZ": "ILI", "3yk8fB": "Novčanik", + "40VR6s": "Nostr Connect", "450Fty": "Nijedan", "47FYwb": "Otkaži", "4IPzdn": "Primarni Developeri", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs je jedan od prvih NIP-05 pružatelja usluge u svemiru i nudi široku kolekciju domena po razumnim cijenama", "4Z3t5i": "Koristi imgproxy za komprimiranje slika", "4rYCjn": "Bilješka sebi", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Kreiraj Račun", "5oTnfy": "Kupi Nadimak", "5rOdPG": "Jednom kad postavite ekstenziju za upravljanje ključevima i izgenerirate svoj ključ, možete zapratiti naš predložak za nove korisnike kako biste postavili svoj profil i kako bismo Vam pomogli pronaći zanimljive ljude na Nostr-u za praćenje.", "5u6iEc": "Prebaci na Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "Pošalji zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Dobij identifikator", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Sviđa se ({n})", "6uMqL1": "Neplaćeno", "7+Domh": "Bilješke", "7BX/yC": "Zamjena Računa", "7hp70g": "NIP-05", - "7xzTiH": "{action} do {target}", "8/vBbP": "Ponovna dijeljenja ({n})", "89q5wc": "Potvrdi ponovno dijeljenje", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sat-ove", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "ime predugo", "8v1NN+": "Fraza za uparivanje", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Sljedeće", "9HU8vw": "Odgovori", "9SvQep": "Praćenja {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning račun", "ADmfQT": "Matični", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "Ovaj je autor utišan", "Adk34V": "Postavi svoj Profil", "Ai8VHU": "Neograničeno zadržavanje bilješki na releju Snort", @@ -92,6 +107,7 @@ "BOr9z/": "Snort je projekt otvorenog koda kojeg su izgradili strastveni ljudi u svoje slobodno vrijeme", "BWpuKl": "Ažuriraj", "BcGMo+": "Bilješke sadrže tekstualni sadržaj, a najpopularnija upotreba ovih bilješki je pohranjivanje poruka tipa \"tweet like\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Odjava", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "Prikaži posljednje {n} bilješke", "DcL8P+": "Podrška", "Dh3hbq": "Automatski Zap", + "Dn82AL": "Live", "DtYelJ": "Prijenos", "E8a4yq": "Pratite neke popularne račune", "ELbg9p": "Data Providers", "EPYwm7": "Vaš privatni ključ je Vaša lozinka. Ako izgubite ovaj ključ, izgubit ćete pristup Vašem računu! Kopirajte ga i sačuvajte na sigurnom mjestu. Ne postoji način za resetiranje Vašeg privatnog ključa.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Globalno", "Ebl/B2": "Prevedi na {lang}", "EcZF24": "Custom Relays", "EcglP9": "Ključ", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Kupi", "Eqjl5K": "Samo Snort i naš partner za integracije Vam pruža živopisni naziv domene, ali naravno možete koristiti i druge usluge.", "F+B3x1": "Postali smo partneri i s nostrplebs.com kako bismo Vam pružili više mogućnosti", "F3l7xL": "Dodaj Račun", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL na koji treba proslijediti zaps", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Gotovo!", "FSYL8G": "Trending Users", "FdhSU2": "Preuzmi Sad", @@ -129,28 +148,32 @@ "G1BGCg": "Odaberite novčanik", "GFOoEE": "Salt", "GL8aXW": "Oznake ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Potvrdi svoju uključenu Snort nostr adresu", "Gcn9NQ": "Magnetni Link", "GspYR7": "{n} Dislajkova", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Ključ..", "H0JBH6": "Odjava", "H6/kLh": "Narudžba plaćena!", "HAlOn1": "Ime", - "HF4YnO": "Watch Live!", "HFls6j": "ime će biti dostupno kasnije", "HOzFdo": "Utišano", "HWbkEK": "Clear cache and reload", "HbefNb": "Otvori novčanik", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Hvala što koristite Snort, molimo da razmislite o donaciji ukoliko ste u mogućnosti.", "IEwZvs": "Jeste sigurni da želite otkačiti bilješku?", "IKKHqV": "Follows", "INSqIz": "Twitter korisničko ime...", "IUZC+0": "Ovo znači da nitko ne može promijeniti bilješke koje ste kreirali te svatko jednostavno može verificirati da ste bilješke koje čitaju Vi kreirali.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Pretplate", "JCIgkj": "Korisničko ime", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Izbriši", "K7AkdL": "Prikaži", "KAhAcM": "Unesi LNDHub konfiguraciju", - "KLo3SP": "Razlog: {reason}", "KQvWvD": "Izbrisano", "KWuDfz": "Moji ključevi su spremljeni, nastavi", "KahimY": "Tip nepoznatog eventa: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Ostale Povezanosti", + "LR1XjT": "Pin too short", "LXxsbk": "Anonimno", "LgbKvU": "Komentiraj", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Izbornici za uklanjanje pogrešaka", "MBAYRO": "Prikazuje \"Kopiraj ID\" i \"Kopiraj JSON događaja\" u kontekstnom izborniku svake poruke", "MI2jkA": "Nije dostupno:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Kupnja {item}", "N2IrpM": "Potvrdi", + "NAidKb": "Notifications", "NAuFNH": "Već imate pretplatu ovog tipa, molimo obnovite pretplatu ili kupite novu", "NNSu3d": "Uvezi Pratitelje s Twittera", "NdOYJJ": "Hmm ovdje nema ničega... Baci oko na {newUsersPage} kako bi zapratio neke preporučene nostriće!", @@ -192,7 +219,6 @@ "OLEm6z": "Nepoznata pogreška kod prijave", "OQXnew": "Vaša je pretplata i dalje aktivna, još ju ne možete obnoviti", "ORGv1Q": "Kreirano", - "P04gQm": "Svi zapovi poslani na ovu bilješku bit će primljeni putem sljedećeg LNURL-a", "P61BTu": "Kopiraj JSON događaj", "P7FD0F": "Sustav (zadano)", "P7nJT9": "Ukupno danas (UTC): {amount} sati", @@ -207,7 +233,6 @@ "QxCuTo": "Umjetnik {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Pretplata", - "R1fEdZ": "Proslijedi Zap-ove", "R81upa": "People you follow", "RDZVQL": "Provjeri", "RahCRH": "Isteklo", @@ -217,19 +242,19 @@ "RoOyAh": "Releji", "Rs4kCE": "Oznaka", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Ažuriraj Lightning Adresu", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "Proxy LN adrese", "ShdEie": "Mark all read", "Sjo1P4": "Prilagođeno", "Ss0sWu": "Plati Odmah", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "Pretplate na čekanju", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Korisnici moraju prihvatiti upozorenje o sadržaju kako bi prikazali sadržaj Vaše bilješke.", "Up5U7K": "Blokiraj", @@ -239,15 +264,16 @@ "VR5eHw": "Javni ključ (npub/nprofile)", "VlJkSk": "{n} utišano", "VnXp8Z": "Avatar", - "VtPV/B": "Prijava s Proširenjem (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "Kako funkcioniraju ključevi?", "W1yoZY": "Čini se kako nemate nikakve pretplate, možete pronaći neke na {link}", "W2PiAr": "{n} Blokirano", "W9355R": "Ponovno uključi", "WONP5O": "Pronađite svoje pratitelje sa Twittera na Nostru (podaci pruženi od {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "npr. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reakcije", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Unesite frazu za uparivanje", "ZKORll": "Aktivirajte Odmah", "ZLmyG9": "Suradnici", - "ZUZedV": "Lightning Donacije:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "Financirajte developere i platforme koje pružaju NIP-05 verifikacijske usluge", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Prikaži više", "b12Goz": "Mnemonički", "b5vAk0": "Vaš nadimak će se ponašati kao lightning adresa i preusmjerit će Vas na odabrani LNURL ili Lightning adresu", + "bLZL5a": "Get Address", "bQdA2k": "Osjetljiv sadržaj", "bep9C3": "Javni Ključ", "bfvyfs": "Anon", "brAXSu": "Odaberite korisničko ime", "bxv59V": "Upravo sada", "c+oiJe": "Instaliraj proširenje", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Ako imate upit o svojoj NIP-05 narudžbi, molimo da pošaljete poruku {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "LN Adresa", "dOQCL8": "Ime za prikaz", "e61Jf3": "Dolazi uskoro", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Označi sve kao pročitano", "eHAneD": "Emotikon reakcije", "eJj8HD": "Verificiraj Se", @@ -297,6 +326,7 @@ "fWZYP5": "Prikačeno", "filwqD": "Pročitaj", "flnGvv": "Što ti je na umu?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Spremljeno", "g5pX+a": "Dodatne informacije", "g985Wp": "Glasanje nije uspjelo", @@ -310,9 +340,9 @@ "h8XMJL": "Značke", "hK5ZDk": "svijet", "hMzcSq": "Poruke", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Podržava", "hicxcO": "Pokaži odgovore", + "hmZ3Bz": "Media", "hniz8Z": "ovdje", "i/dBAR": "Zap Pool", "iCqGww": "Reakcije ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL prijevodi", "iGT1eE": "Spriječite lažne račune da vas oponašaju", "iNWbVV": "Nadimak", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Nije moguće prijaviti se pomoću privatnog ključa na nesigurnoj vezi, molimo koristite Nostr produžetak za upravljanje ključevima", "ieGrWo": "Prati", "itPgxd": "Profil", @@ -381,9 +410,11 @@ "qMx1sA": "Zadani iznos Zap-a", "qUJTsT": "Blokirano", "qdGuQo": "Vaš Privatni Ključ Je (nemojte ga dijeliti ni sa kim)", + "qfmMQh": "This note has been muted", "qkvYUb": "Dodati na Profil", "qmJ8kD": "Prijevod neuspješan", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Unesite lozinku novčanika", "rT14Ow": "Dodaj relej", @@ -392,7 +423,10 @@ "rmdsT4": "{n} dana", "rrfdTe": "Ovo je ista tehnologija koja je korištena od strane Bitcoina te je dokazana kao iznimno sigurna.", "rudscU": "Učitavanje pratitelja neuspješno, molimo pokušajte ponovno kasnije", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort je dizajniran kako bi pružio slično iskustvo kao i Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Nije dopušteno lažno predstavljanje i imitiranje. Snort i naši partneri zadržavaju pravo ukinuti vaš nadimak (ne Vaš račun - nitko Vam to ne može oduzeti) zbog kršenja ovog pravila.", "tOdNiY": "Tamno", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Značka za pruženu podršku", "u/vOPu": "Plaćeno", "u4bHcR": "Ovdje pogledajte kod: {link}", - "uD/N6c": "Zap {target} {n} sat-ove", "uSV4Ti": "Ponovna dijeljenja morate potvrditi ručno", + "uc0din": "Send sats splits to", "usAvMr": "Uredi profil", "ut+2Cd": "Dobijte identifikator partnera", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} drugi} other{& {n} drugi}}", - "vU71Ez": "Plaćanje s {wallet}", "vZ4quW": "NIP-05 je specifikacija za provjeru temeljena na DNS-u koja vam pomaže potvrditi da ste pravi korisnik.", "vhlWFg": "Mogućnosti ankete", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Uredi", "wLtRCF": "Vaš ključ", + "wSZR47": "Submit", "wWLwvh": "Anonimno", "wYSD2L": "Nostr Adresa", "wih7iJ": "ime je blokirano", + "wofVHy": "Moderation", "wqyN/i": "Saznajte više informacija o {service} na {link}", "wtLjP6": "Kopiraj ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Ova bilješka označena je kao osjetljiva, kliknite ovdje da je otkrijete", "x82IOl": "Utišaj", + "xIcAOU": "Votes by {type}", "xIoGG9": "Idi na", "xJ9n2N": "Vaš javni ključ", "xKflGN": "{username}'ova praćenja na Nostr-u", @@ -433,11 +468,13 @@ "y1Z3or": "Jezik", "yCLnBC": "LNURL ili Lightning Adresa", "yCmnnm": "Čitaj globalno sa", + "zCb8fX": "Weight", "zFegDD": "Kontakt", "zINlao": "Vlasnik", "zQvVDJ": "Sve", "zcaOTs": "Iznos Zap-a u sats-ima", "zjJZBd": "Spremni ste!", "zonsdq": "Neuspješno učitavanje LNURL usluge", - "zvCDao": "Automatski prikaži najnovije bilješke" + "zvCDao": "Automatski prikaži najnovije bilješke", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/hu_HU.json b/packages/app/src/translations/hu_HU.json index 62161ed3..2ebf7865 100644 --- a/packages/app/src/translations/hu_HU.json +++ b/packages/app/src/translations/hu_HU.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Fizetés Most", "+Vxixo": "Titkos Csoportos Chat", "+aZY2h": "Zap típusa", + "+tShPg": "követek", "+vA//S": "Bejelentkezés", "+vIQlC": "Ahhoz hogy a jövőben is hozzáférj a fiókodhoz, kérlek mindenképp győződj meg róla hogy a következő jelszót elmentetted", "+vVZ/G": "Kapcsolódás", @@ -11,6 +12,7 @@ "/JE/X+": "Segítség", "/PCavi": "Nyilvános", "/RD0e2": "A Nostr digitális aláírásokat használ, amivel megmásíthatatlan bejegyzéseket lehet létrehozni. Ezeket bárhány másolatban a különböző csomópontokra szétszórhatóak, ezzel biztosítva a tartalom redundanciáját.", + "/Xf4UW": "Anonimizált használati adatok küldése", "/d6vEc": "Legyen a fiókod könnyebben megtalálható és megosztható", "/n5KSF": "{n} ms", "00LcfG": "Továbbiak betöltése", @@ -19,6 +21,7 @@ "0BUTMv": "Keresés...", "0jOEtS": "Érvénytelen LNURL", "0mch2Y": "név nem engedélyezett karaktereket tartalmaz", + "0uoY11": "Státusz megjelenítése", "0yO7wF": "{n} másodperc", "1A7TZk": "Mi a Snort és hogyan működik?", "1Mo59U": "Biztos hogy a kedvencekből ezt a bejegyzést el akarod távolítani?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} könyvjelző", "2k0Cv+": "Nemtetszések ({n})", "2ukA4d": "{n} órák", + "3KNMbJ": "Cikkek", "3Rx6Qo": "Speciális", "3cc4Ct": "Világos", "3gOsZq": "Fordítók", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Követő", "3xCwbZ": "VAGY", "3yk8fB": "Pénztárca", + "40VR6s": "Nostr Connect", "450Fty": "Nincs", "47FYwb": "Törlés", "4IPzdn": "Elsődleges Fejlesztők", @@ -50,25 +55,34 @@ "4Vmpt4": "A Nostr Plebs-ék az egyik legelső megfelelő feltételekkel és árakkal NIP-05 azonosítást biztosító szolgáltató", "4Z3t5i": "Az imgproxy használata a képek tömörítéséhez", "4rYCjn": "Jegyzet magamnak", + "5BVs2e": "zap", + "5CB6zB": "Zap-ek megosztása", "5JcXdV": "Fiók létrehozása", "5oTnfy": "Azonosító vásárlása", "5rOdPG": "Miután beállítottad a kulcskezelő bővítményt és egy kulcsot létrehoztál, folytathatod az új felhasználói folyamatunkat, hogy profilodat beállítsd. Segítségével a Nostr-án néhány érdekes személyt megtalálhatsz, akiket ezekután egy kattintással követni is tudsz.", "5u6iEc": "Átvitel a Publikus kulcsra", "5vMmmR": "A Nostr-án a felhasználónevek nem egyediek. A nostr cím az Ön egyedi, ember által olvasható címe, amely regisztrációjától egyedi.", "5ykRmX": "Zap küldése", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Visszajátszás Megtekintése", "65BmHb": "Nem sikerült a proxyképet a(z) {host} webhelyről betölteni. Kattintson ide a közvetlen betöltéshez", + "6OSOXl": "Ok: {reason}", "6Yfvvp": "Szerezz egy azonosítót", + "6bgpn+": "Nem minden kliens támogatja ezt, ezért előfordulhat, hogy továbbra is a zap-eket úgy kapja, mintha a zap felosztás nem lett volna konfigurálva", "6ewQqw": "Lájkok ({n})", "6uMqL1": "Fizetetlen", "7+Domh": "Bejegyzések", "7BX/yC": "Fiók Váltó", "7hp70g": "NIP-05", - "7xzTiH": "{action} oda {target}", "8/vBbP": "Megosztva ({n})", "89q5wc": "Megosztás megerősítése", + "8Kboo2": "A kezdéshez az aláíró alkalmazással olvassa be ezt a QR-kódot", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Címzett", + "8Y6bZQ": "Érvénytelen zap megosztás: {input}", "8g2vyB": "név túl hosszú", "8v1NN+": "Párosító kifejezés", + "8xNnhi": "Nostr Kiegészítő", "9+Ddtu": "Következő", "9HU8vw": "Válasz", "9SvQep": "{n} követek", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning Számla", "ADmfQT": "Szülő", "AGNz71": "Zap-elni Mind a {n} sats-ot", + "AN0Z7Q": "Némított szavak", "ASRK0S": "Ez a felhasználó némítva", "Adk34V": "Profilod kitöltése", "Ai8VHU": "A Snort csomóponton korlátlan bejegyzés megőrzés", @@ -92,6 +107,7 @@ "BOr9z/": "A Snort egy nyílt forráskódú projekt, amit elhivatott emberek a szabadidejükben fejlesztettek.", "BWpuKl": "Frissítés", "BcGMo+": "A bejegyzések szöveget tartalmaznak és a legnépszerűbb felhasználási módja, hogy ”Twitter féle” üzeneteket tároljanak benne.", + "C1LjMx": "Lightning Donation", "C5xzTC": "Prémium", "C81/uG": "Kijelentkezés", "C8HhVE": "Javasolt követendő személyek", @@ -106,20 +122,23 @@ "DZzCem": "A legutóbbi {n} bejegyzés mutatása", "DcL8P+": "Támogató", "Dh3hbq": "Auto Zap", + "Dn82AL": "Élő", "DtYelJ": "Átvitel", "E8a4yq": "Kövess néhány népszerű felhasználót", "ELbg9p": "Adatszolgáltatók", "EPYwm7": "A privát kulcsod a jelszavad. Ha elhagyod, elveszíted a hozzáférést a fiókodhoz! Mentsd le egy biztonságos helyre, mert nincs lehetőség a privát kulcsaid helyreállítására!", + "EQKRE4": "Jelvények megjelenítése a profiloldalakon", "EWyQH5": "Globális", "Ebl/B2": "Fordítás erre {lang}", "EcZF24": "Egyedi Csomópontok", "EcglP9": "Kulcs", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Vásárlás", "Eqjl5K": "Csak a Snort és a mi integrációs partnerünk azonosítója ad színes domain nevet, de azonosításra szabadon használhatod más szolgáltatók szolgáltatásait is.", "F+B3x1": "A több lehetőség biztosítása érdekében, a nostrplebs.com partnerségre léptünk", "F3l7xL": "Fiók Hozzáadása", "FDguSC": "{n} Zap-ek", - "FP+D3H": "LNURL ahova a zap-eket továbbítjuk", + "FMfjrl": "Státusz üzenetek megjelenítése a profiloldalakon", "FS3b54": "Kész!", "FSYL8G": "Trendelő felhasználók", "FdhSU2": "Igényeld most", @@ -129,28 +148,32 @@ "G1BGCg": "Tárca kiválasztása", "GFOoEE": "Salt", "GL8aXW": "Könyvjelzők ({n})", + "GQPtfk": "Csatlakozz a Streamhez", "GSye7T": "Lightning Cím", "GUlSVG": "Igényeld a hozzá tartozó Snort nostr címet", "Gcn9NQ": "Mágnes Link", "GspYR7": "{n} Nem tetszik", + "Gxcr08": "Esemény közvetítése", "H+vHiz": "Hex kulcs..", "H0JBH6": "Kijelentkezés", "H6/kLh": "Rendelés fizetve!", "HAlOn1": "Név", - "HF4YnO": "Nézd élőben!", "HFls6j": "név később elérhető", "HOzFdo": "Némítottak", "HWbkEK": "Törölje a gyorsítótárat és töltse be újra", "HbefNb": "Pénztárca megnyitása", + "HhcAVH": "Ön nem követi ezt a személyt, kattintson ide a média betöltéséhez a(z) {link} webhelyről, vagy frissítse beállításait, hogy a médiát mindig mindenkitől betöltse.", "IDjHJ6": "Köszönjük, hogy a Snortot használod, ha teheted fontold meg az adományozást.", "IEwZvs": "Biztos hogy a bejegyzés kiemelését visszavonod?", "IKKHqV": "Követőim", "INSqIz": "Twitter felhasználónév...", "IUZC+0": "Ez azt jelenti, hogy a te általad létrehozott bejegyzéseket senki sem tudja módosítani és bárki ellenőrizheti hogy tényleg te írtad.", "Ig9/a1": "{n} sats {name}-nak elküldve", + "IoQq+a": "Kattintson ide a betöltéshez", "Ix8l+B": "Felkapott Bejegyzések", "J+dIsA": "Előfizetések", "JCIgkj": "Felhasználónév", + "JGrt9q": "Sat-ok küldése {name}", "JHEHCk": "Zap-ek ({n})", "JPFYIM": "Nincs Lightning cím", "JeoS4y": "Megosztás", @@ -160,16 +183,19 @@ "K3r6DQ": "Törlés", "K7AkdL": "Mutat", "KAhAcM": "Az LNDHub konfiguráció megadása", - "KLo3SP": "Indok: {reason}", "KQvWvD": "Törölve", "KWuDfz": "Lementettem a kulcsaimat, folytatás", "KahimY": "Ismeretlen esemény: {kind}", "KoFlZg": "Add meg a pénzverde URL-jét", + "KtsyO0": "Pin-kód megadása", "LF5kYT": "Egyéb Kapcsolatok", + "LR1XjT": "Pin túl rövid", "LXxsbk": "Ismeretlen", "LgbKvU": "Hozzászólás", "Lu5/Bj": "Zapstr-en megnyitása", "Lw+I+J": "{n,plural,=0{{name} zapp-elt} other{{name} és {n} mások is zapp-elték}}", + "LwYmVi": "Ezen bejegyzésben lévő zap-ek a következő felhasználók között lesznek megosztva.", + "M10zFV": "Nostr Connect", "M3Oirc": "Hibaelhárító menü", "MBAYRO": "Mutasd az \"Egyedi azonosítót\" és a \"JSON esetet\" minden üzenet szövegmezőjében", "MI2jkA": "Nem elérhető:", @@ -180,6 +206,7 @@ "MuVeKe": "Nostr cím vásárlása", "MzRYWH": "{item} megveszem", "N2IrpM": "Jóváhagy", + "NAidKb": "Értesítések", "NAuFNH": "Már van ilyen típusú előfizetésed, kérjük, újítsd meg, vagy fizess", "NNSu3d": "Twitter követések importálása", "NdOYJJ": "Hmm nincs itt semmi.. Ellenőrizd a {newUsersPage}, hogy tudj néhány javasolt Nostr felhasználót követni!", @@ -192,7 +219,6 @@ "OLEm6z": "Ismeretlen bejelentkezési hiba", "OQXnew": "Az előfizetésed még aktív, így nem tudod megújítani", "ORGv1Q": "Létrehozva", - "P04gQm": "Minden Zap amelyet ez a bejegyzés fogad, a következő LNURL-re lesz küldve", "P61BTu": "JSON eset", "P7FD0F": "Rendszer (Alapértelmezett)", "P7nJT9": "Összesen ma (UTC): {amount} sat", @@ -207,7 +233,6 @@ "QxCuTo": "Művészet: {name}", "Qxv0B2": "Jelenleg a zap-medencében {number} sats van.", "R/6nsx": "Előfizetés", - "R1fEdZ": "Zap-ek Továbbítása", "R81upa": "Általad követett személyek", "RDZVQL": "Ellenőrzés", "RahCRH": "Lejárt", @@ -217,19 +242,19 @@ "RoOyAh": "Csomópontok", "Rs4kCE": "Könyvjelző", "RwFaYs": "Rendezés", + "SMO+on": "Sat küldése {name}", "SOqbe9": "Lightning cím frissítése", "SP0+yi": "Előfizetés vásárlása", - "SX58hM": "Másolás", "SYQtZ7": "LN cím proxy", "ShdEie": "Mindet olvasottnak jelöl", "Sjo1P4": "Egyedi", "Ss0sWu": "Fizetés Most", + "StKzTE": "A szerző ezt a bejegyzést kényes témaként jelölte meg", "TDR5ge": "A bejegyzésekben található média bizonyos emberek számára automatikusan megjelenik, másoknak pedig egy link lesz helyette", - "TMfYfY": "Cashu token", + "TP/cMX": "Befejezve", "TpgeGw": "Hex Salt..", "Tpy00S": "Személyek", "UDYlxu": "Függőben lévő előfizetések", - "ULotH9": "Egyenleg: {amount} sats", "UT7Nkj": "Új Beszélgetés", "UUPFlt": "Ahhoz hogy a bejegyzés tartalma megjelenjen, a felhasználóknak a tartalomra vonatkozó figyelmeztetést el kell fogadniuk.", "Up5U7K": "Tiltás", @@ -239,15 +264,16 @@ "VR5eHw": "Publikus kulcs (npub/nprofile)", "VlJkSk": "{n} némított", "VnXp8Z": "Avatar", - "VtPV/B": "Bejelentkezés kiterjesztéssel (NIP-07)", "VvaJst": "Tárcák megtekintése", "Vx7Zm2": "Hogyan működnek a kulcsok?", "W1yoZY": "Úgy tűnik, nincs előfizetésed, itt szerezhetsz egyet: {link}", "W2PiAr": "{n} Tiltott", "W9355R": "Némítás visszavonása", "WONP5O": "A Twitter követők a Nostr hálózaton való megtalálása (Az adatokat a {provider} biztosította)", + "WvGmZT": "npub / nprofil / nostr cím", "WxthCV": "pl. Jack", "X7xU8J": "nsec, npub, nip-05, hex, helyreállító (mnemonikus)", + "XECMfW": "Használati mutatók küldése", "XICsE8": "Fájl tárhelyszolgáltatók", "XgWvGA": "Reakciók", "Xopqkl": "Az alapértelmezett zap összeg {number} sats, a példaértékek kiszámítása ebből történik.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Párosító kifejezés megadása", "ZKORll": "Aktiválás", "ZLmyG9": "Közreműködők", - "ZUZedV": "Lightning adomány:", + "ZS+jRE": "Zap felosztások küldése ide", "Zr5TMx": "Profil Beállítása", "a5UPxh": "Támogasd a fejlesztőket és a platform szolgáltatókat akik NIP-05 azonosító szolgáltatásokat biztosítanak", "a7TDNm": "A bejegyzések a globális és a bejegyzések fülek alatt valós időben jelennek meg", "aWpBzj": "Mutass többet", "b12Goz": "Emlékezeterősítő", "b5vAk0": "Az azonosító mint egy Lightning cím fog működni, és a kiválasztott LNURL- vagy Lightning-címre irányít át", + "bLZL5a": "Get Address", "bQdA2k": "Érzékeny tartalom", "bep9C3": "Publikus Kulcs", "bfvyfs": "Névtelen", "brAXSu": "Válassz felhasználónevet", "bxv59V": "Csak most", "c+oiJe": "Bővítmény Telepítése", + "c2DTVd": "A privát kulcsodnak a titkosításához adj meg egy PIN-kódot. Ezt a PIN-kódot minden alkalommal meg kell adnod, amikor a Snort-ot megnyitod.", "c35bj2": "Ha kérdése van a NIP-05 azonosító rendelésével kapcsolatban, kérjük, írjon üzenetet {link}", "c3g2hL": "Újraküldés", "cFbU1B": "Albyt használod? Nyissa meg a {link} webhelyet az NWC konfigurációjának lekéréséhez!", @@ -287,6 +315,7 @@ "d7d0/x": "LN cím", "dOQCL8": "Megjelenítendő név", "e61Jf3": "Hamarosan", + "e7VmYP": "A privát kulcs feloldásához írd be a PIN-kódot", "e7qqly": "Mind olvasottnak jelölni", "eHAneD": "Reakció emoji", "eJj8HD": "Légy Azonosítva", @@ -297,6 +326,7 @@ "fWZYP5": "Kiemelt", "filwqD": "Olvasás", "flnGvv": "Mi jár a fejedben?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Mentve", "g5pX+a": "Rólam", "g985Wp": "A szavazás elküldése sikertelen", @@ -310,9 +340,9 @@ "h8XMJL": "Kitüntetések", "hK5ZDk": "a világ", "hMzcSq": "Üzenetek", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Támogatás", "hicxcO": "Válaszok megjelenítése", + "hmZ3Bz": "Média", "hniz8Z": "itt", "i/dBAR": "Zap Medence", "iCqGww": "({n}) reakciók", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL fordító", "iGT1eE": "Akadályozd meg hogy a kamu fiókok utánozzanak", "iNWbVV": "Azonosító", - "iUsU2x": "Pénzverde: {url}", "iXPL0Z": "Nem biztonságos kapcsolaton a privát kulccsoddal NEM (!) tudsz bejelentkezni, használj helyette egy Nostr kulcskezelő bővítményt", "ieGrWo": "Követem", "itPgxd": "Profil", @@ -381,9 +410,11 @@ "qMx1sA": "Alapértelmezett Zap összeg", "qUJTsT": "Tiltva", "qdGuQo": "A te privát kulcsod (senkivel se oszd meg)", + "qfmMQh": "Ez a bejegyzés némítva", "qkvYUb": "Hozzáadás a profilhoz", "qmJ8kD": "Fordítás nem sikerült", "qtWLmt": "Lájk", + "qz9fty": "Helytelen Pin-kód", "r3C4x/": "Szoftver", "r5srDR": "Add meg a pénztárcád jelszavát", "rT14Ow": "Csomópont hozzáadása", @@ -392,7 +423,10 @@ "rmdsT4": "{n} napok", "rrfdTe": "Ez a technológia ugyanaz, mint amivel a tökéletes biztonságát a Bitcoin is sikeresen bizonyította.", "rudscU": "Hiba a követők betöltésénél, kérlek próbáld később", + "sKDn4e": "Jelvények megjelenítése", + "sUNhQE": "felhasználó", "sWnYKw": "Snort úgy lett tervezve, hogy hasonló élményt nyújtson mint a Twitter.", + "sZQzjQ": "Nem sikerült a zap felosztása: {input}", "svOoEH": "Az engedély nélküli névfoglalás vagy megszemélyesítés nem engedélyezett. A szabályok megszegéséért, a Snort és partnerei fenntartják maguknak a jogot, hogy azonnal töröljék az azonosítót (nem a fiókot, mert azt nem lehet).", "tOdNiY": "Sötét", "th5lxp": "Az írási joggal rendelkező csomópontok egy részhalmazának bejegyzés küldése", @@ -400,13 +434,12 @@ "ttxS0b": "Támogatói jelvény", "u/vOPu": "Fizetve", "u4bHcR": "Nézze meg a kódot itt: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "A megosztásokhoz manuális megerősítés szükséges", + "uc0din": "Zap felosztások küldése ide", "usAvMr": "Profil módosítása", "ut+2Cd": "Szerezz egy partner azonosítót", "v8lolG": "Csevegés indítása", "vOKedj": "{n,plural,=1{és más {n} által} other{és másik {n} által}}", - "vU71Ez": "Fizetés {wallet}", "vZ4quW": "A NIP-05 egy DNS alapú azonosítási specifikáció, ami segít a valós személyed bizonyításában.", "vhlWFg": "Szavazási opciók", "vlbWtt": "Szerezz ingyen egyet", @@ -414,14 +447,16 @@ "vxwnbh": "Az összes közzétett eseményre alkalmazandó munka mennyisége", "wEQDC6": "Módosítás", "wLtRCF": "A kulcsod", + "wSZR47": "Küldés", "wWLwvh": "Névtelen", "wYSD2L": "Nostr Cím", "wih7iJ": "név tiltva", + "wofVHy": "Moderáció", "wqyN/i": "Több információ a {service} itt {link}", "wtLjP6": "Egyedi azonosító", "x/Fx2P": "Finanszírozza az Ön által használt szolgáltatásokat úgy, hogy az összes zaps-ek egy részét egy támogatói gyűjtőbe osztja fel!", - "x/q8d5": "Ez a bejegyzés érzékenynek lett minősítve, a megjelenítéshez kattints ide", "x82IOl": "Némítás", + "xIcAOU": "Szavazatok {type} által", "xIoGG9": "Menj ide", "xJ9n2N": "A te publikus kulcsod", "xKflGN": "{username} a Nostr-án követ", @@ -433,11 +468,13 @@ "y1Z3or": "Nyelv", "yCLnBC": "LNURL vagy Lightning cím", "yCmnnm": "Globális betöltése innen", + "zCb8fX": "Súly", "zFegDD": "Kapcsolat", "zINlao": "Tulajdonos", "zQvVDJ": "Mind", "zcaOTs": "Zap összeg sats-ban", "zjJZBd": "Készen vagy!", "zonsdq": "Az LNURL szolgáltatás betöltése nem sikerült", - "zvCDao": "Automatikusan a legfrissebb bejegyzéseket mutassa" + "zvCDao": "Automatikusan a legfrissebb bejegyzéseket mutassa", + "zwb6LR": "Pénzverde: {url}" } diff --git a/packages/app/src/translations/id_ID.json b/packages/app/src/translations/id_ID.json index b3e94d55..5c5062ac 100644 --- a/packages/app/src/translations/id_ID.json +++ b/packages/app/src/translations/id_ID.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "Tipe Zap", + "+tShPg": "following", "+vA//S": "Logins", "+vIQlC": "Harap pastikan untuk menyimpan kata sandi berikut untuk mengelola pegangan Anda di masa mendatang", "+vVZ/G": "Connect", @@ -11,6 +12,7 @@ "/JE/X+": "Dukungan Akun", "/PCavi": "Publik", "/RD0e2": "Nostr menggunakan teknologi tanda tangan digital untuk menyediakan catatan anti pengerusakan yang dapat dengan aman direplikasi ke banyak relai untuk menyediakan penyimpanan konten Anda yang berulang.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Jadikan profil Anda lebih mudah untuk ditemukan dan dibagikan", "/n5KSF": "{n} ms", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "Cari...", "0jOEtS": "LNURL Tidak valid", "0mch2Y": "nama memiliki karakter yang tidak diizinkan", + "0uoY11": "Show Status", "0yO7wF": "{n} detik", "1A7TZk": "Apa itu Snort dan bagaimana cara kerjanya?", "1Mo59U": "Apa Anda yakin Anda ingin memindahkan catatan ini dari penanda buku?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Penanda Buku", "2k0Cv+": "Tidak disukai ({n})", "2ukA4d": "{n} hours", + "3KNMbJ": "Articles", "3Rx6Qo": "Advanced", "3cc4Ct": "Cahaya", "3gOsZq": "Penerjemah", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Pengikut", "3xCwbZ": "ATAU", "3yk8fB": "Wallet", + "40VR6s": "Nostr Connect", "450Fty": "Tidak ada", "47FYwb": "Membatalkan", "4IPzdn": "Pengembang Utama", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs adalah salah satu penyedia NIP-05 pertama di ruang ini dan menawarkan koleksi domain yang bagus dengan harga terjangkau", "4Z3t5i": "Gunakan imgproxy untuk mengkompresi gambar", "4rYCjn": "Catatan untuk diri sendiri", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Create Account", "5oTnfy": "Buy Handle", "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", "5u6iEc": "Transfer to Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "Kirim zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Get an identifier", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Suka ({n})", "6uMqL1": "Unpaid", "7+Domh": "Catatan", "7BX/yC": "Account Switcher", "7hp70g": "NIP-05", - "7xzTiH": "{action} ke {target}", "8/vBbP": "Posting ulang ({n})", "89q5wc": "Konfirmasi Posting Ulang", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sat", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "nama terlalu panjang", "8v1NN+": "Pairing phrase", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Berikutnya", "9HU8vw": "Membalas", "9SvQep": "Mengikuti {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Faktur Lightning", "ADmfQT": "Orang Tua", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "Penulis ini telah dibisukan", "Adk34V": "Siapkan Profil Anda", "Ai8VHU": "Unlimited note retention on Snort relay", @@ -92,6 +107,7 @@ "BOr9z/": "Snort adalah proyek open source yang dibangun oleh orang-orang yang bersemangat di waktu luang mereka", "BWpuKl": "Update", "BcGMo+": "Catatan menyimpan konten teks, penggunaan paling populer dari catatan ini adalah untuk menyimpan pesan-pesan yang \"seperti tweet\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Keluar", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "Tampilkan catatan {n} terbaru", "DcL8P+": "Supporter", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transfer", "E8a4yq": "Ikuti beberapa akun populer", "ELbg9p": "Data Providers", "EPYwm7": "Kunci pribadi Anda adalah kata sandi Anda. Jika Anda kehilangan kunci ini, Anda akan kehilangan akses ke akun Anda! Salin dan simpan di tempat yang aman. Tidak ada cara untuk mengatur ulang kunci pribadi Anda.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Terjemahkan ke {lang}", "EcZF24": "Custom Relays", "EcglP9": "Kunci", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Membeli", "Eqjl5K": "Hanya Snort dan pengidentifikasi mitra integrasi kami yang memberi Anda nama domain yang beragam, tetapi Anda juga dapat menggunakan layanan lain.", "F+B3x1": "Kami juga bermitra dengan nostrplebs.com untuk memberi Anda lebih banyak opsi", "F3l7xL": "Add Account", "FDguSC": "{n} Zap", - "FP+D3H": "LNURL to forward zaps to", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Selesai!", "FSYL8G": "Trending Users", "FdhSU2": "Claim Now", @@ -129,28 +148,32 @@ "G1BGCg": "Select Wallet", "GFOoEE": "Garam", "GL8aXW": "Penanda buku ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Claim your included Snort nostr address", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Tidak suka", + "Gxcr08": "Broadcast Event", "H+vHiz": "Kunci Hex..", "H0JBH6": "Keluar", "H6/kLh": "Pesanan Dibayar!", "HAlOn1": "Nama", - "HF4YnO": "Watch Live!", "HFls6j": "nama akan tersedia nanti", "HOzFdo": "Dibisukan", "HWbkEK": "Clear cache and reload", "HbefNb": "Buka Dompet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", "IEwZvs": "Apakah Anda yakin Anda ingin menghilangkan sematan catatan ini?", "IKKHqV": "Follows", "INSqIz": "Nama pengguna Twitter...", "IUZC+0": "Ini berarti bahwa tidak seorang pun dapat mengubah catatan yang telah Anda buat dan semua orang dapat dengan mudah memverifikasi bahwa catatan yang mereka baca dibuat oleh Anda.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Subscriptions", "JCIgkj": "Nama pengguna", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zap ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Hapus", "K7AkdL": "Tampilkan", "KAhAcM": "Enter LNDHub config", - "KLo3SP": "Reason: {reason}", "KQvWvD": "Dihapus", "KWuDfz": "Saya telah menyimpan kunci saya, lanjutkan", "KahimY": "Jenis acara tidak diketahui: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Other Connections", + "LR1XjT": "Pin too short", "LXxsbk": "Anonim", "LgbKvU": "Komentar", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Debug Menu", "MBAYRO": "Menampilkan \"Salin ID\" dan \"Salin Event JSON\" di menu konteks pada setiap pesan", "MI2jkA": "Tidak tersedia:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Membeli {item}", "N2IrpM": "Konfirmasi", + "NAidKb": "Notifications", "NAuFNH": "You already have a subscription of this type, please renew or pay", "NNSu3d": "Import Twitter Follows", "NdOYJJ": "Hmm tidak ada apa-apa di sini.. Lihat {newUsersPage} untuk mengikuti beberapa nostrich yang direkomendasikan!", @@ -192,7 +219,6 @@ "OLEm6z": "Unknown login error", "OQXnew": "You subscription is still active, you can't renew yet", "ORGv1Q": "Created", - "P04gQm": "All zaps sent to this note will be received by the following LNURL", "P61BTu": "Salin Event JSON", "P7FD0F": "Sistem (Bawaan)", "P7nJT9": "Total hari ini (UTC): {amount} sat", @@ -207,7 +233,6 @@ "QxCuTo": "Seni oleh {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Subscription", - "R1fEdZ": "Forward Zaps", "R81upa": "People you follow", "RDZVQL": "Periksa", "RahCRH": "Kedaluwarsa", @@ -217,19 +242,19 @@ "RoOyAh": "Relai-relai", "Rs4kCE": "Penanda buku", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Update Lightning Address", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "LN Address Proxy", "ShdEie": "Mark all read", "Sjo1P4": "Kustomisasi", "Ss0sWu": "Pay Now", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Garam Hex..", "Tpy00S": "People", "UDYlxu": "Pending Subscriptions", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Users must accept the content warning to show the content of your note.", "Up5U7K": "Blokir", @@ -239,15 +264,16 @@ "VR5eHw": "Public key (npub/nprofile)", "VlJkSk": "{n} dibisukan", "VnXp8Z": "Avatar", - "VtPV/B": "Masuk dengan Ekstensi (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "Bagaimana cara kerja kunci?", "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", "W2PiAr": "{n} Diblokir", "W9355R": "Berhenti membisukan", "WONP5O": "Temukan pengikut twitter Anda di nostr (Data disediakan oleh {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "misalnya, Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reaksi", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Enter pairing phrase", "ZKORll": "Aktifkan Sekarang", "ZLmyG9": "Kontributor", - "ZUZedV": "Donasi Lightning:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "Danai pengembang-pengembang dan platform-platform yang menyediakan layanan verifikasi NIP-05", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Tampilkan lebih banyak", "b12Goz": "Mnemonic", "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bLZL5a": "Get Address", "bQdA2k": "Sensitive Content", "bep9C3": "Public Key", "bfvyfs": "Anon", "brAXSu": "Pilih sebuah nama pengguna", "bxv59V": "Baru saja", "c+oiJe": "Install Extension", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Jika Anda memiliki pertanyaan tentang pesanan NIP-05 Anda, silakan DM {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "Alamat LN", "dOQCL8": "Nama tampilan", "e61Jf3": "Coming soon", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Tandai Semua Telah Dibaca", "eHAneD": "Emoji reaksi", "eJj8HD": "Dapatkan Verifikasi", @@ -297,6 +326,7 @@ "fWZYP5": "Disematkan", "filwqD": "Baca", "flnGvv": "Apa yang sedang kamu pikirkan?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Disimpan", "g5pX+a": "Tentang", "g985Wp": "Failed to send vote", @@ -310,9 +340,9 @@ "h8XMJL": "Badges", "hK5ZDk": "Dunia", "hMzcSq": "Pesan-pesan", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Dukungan", "hicxcO": "Tampilkan balasan-balasan", + "hmZ3Bz": "Media", "hniz8Z": "here", "i/dBAR": "Zap Pool", "iCqGww": "Reaksi-reaksi ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL translations", "iGT1eE": "Cegah akun palsu meniru Anda", "iNWbVV": "Menangani", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", "ieGrWo": "Ikuti", "itPgxd": "Profil", @@ -381,9 +410,11 @@ "qMx1sA": "Jumlah Zap bawaan", "qUJTsT": "Diblokir", "qdGuQo": "Kunci Pribadi Anda Adalah (jangan bagikan ini dengan siapa pun)", + "qfmMQh": "This note has been muted", "qkvYUb": "Tambahkan ke Profil", "qmJ8kD": "Terjemahan gagal", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Perangkat lunak", "r5srDR": "Enter wallet password", "rT14Ow": "Tambahkan Relay", @@ -392,7 +423,10 @@ "rmdsT4": "{n} days", "rrfdTe": "Ini adalah teknologi yang sama yang digunakan oleh Bitcoin dan telah terbukti sangat aman.", "rudscU": "Gagal memuat yang diikuti, harap coba lagi nanti", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort dirancang untuk memiliki pengalaman yang mirip dengan Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Penyerobotan nama dan peniruan identitas tidak diperbolehkan. Snort dan mitra kami berhak menghentikan pegangan Anda (bukan akun Anda - tidak ada yang dapat mengambilnya) karena melanggar peraturan ini.", "tOdNiY": "Gelap", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Supporter Badge", "u/vOPu": "Sudah dibayar", "u4bHcR": "Lihat kodenya di sini: {link}", - "uD/N6c": "Zap {target} {n} sat", "uSV4Ti": "Posting ulang perlu dikonfirmasi secara manual", + "uc0din": "Send sats splits to", "usAvMr": "Sunting profil", "ut+2Cd": "Dapatkan pengenal mitra", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} lainnya} other{& {n} lainnya}}", - "vU71Ez": "Paying with {wallet}", "vZ4quW": "NIP-05 adalah spesifikasi verifikasi berbasis DNS yang membantu memvalidasi Anda sebagai pengguna nyata.", "vhlWFg": "Poll Options", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Sunting", "wLtRCF": "Kunci Anda", + "wSZR47": "Submit", "wWLwvh": "Anon", "wYSD2L": "Nostr Adddress", "wih7iJ": "nama diblokir", + "wofVHy": "Moderation", "wqyN/i": "Cari tahu info selengkapnya tentang {service} di {link}", "wtLjP6": "Salin ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "This note has been marked as sensitive, click here to reveal", "x82IOl": "Bisukan", + "xIcAOU": "Votes by {type}", "xIoGG9": "Pergi ke", "xJ9n2N": "kunci publik Anda", "xKflGN": "Yang diikuti {username} di Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Bahasa", "yCLnBC": "LNURL or Lightning Address", "yCmnnm": "Baca global dari", + "zCb8fX": "Weight", "zFegDD": "Kontak", "zINlao": "Pemilik", "zQvVDJ": "Semua", "zcaOTs": "Jumlah zap dalam sat", "zjJZBd": "Anda siap!", "zonsdq": "Gagal memuat layanan LNURL", - "zvCDao": "Tampilkan catatan terbaru secara otomatis" + "zvCDao": "Tampilkan catatan terbaru secara otomatis", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/it_IT.json b/packages/app/src/translations/it_IT.json index f51e832e..2359581e 100644 --- a/packages/app/src/translations/it_IT.json +++ b/packages/app/src/translations/it_IT.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Payout Now", "+Vxixo": "Secret Group Chat", "+aZY2h": "Tipo di zap", + "+tShPg": "following", "+vA//S": "Accedi", "+vIQlC": "Assicurati di salvare la seguente password per gestire il tuo profilo in futuro", "+vVZ/G": "Connetti", @@ -11,6 +12,7 @@ "/JE/X+": "Servizio clienti", "/PCavi": "Pubblica", "/RD0e2": "Nostr utilizza la tecnologia della firma digitale per fornire note a prova di manomissione che possono essere replicate in modo sicuro su molti relè per fornire l'archiviazione ridondante dei tuoi contenuti.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Rendi il tuo profilo più facile da trovare e condividere", "/n5KSF": "{n} ms", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "Cerca...", "0jOEtS": "LNURL non valido", "0mch2Y": "il nome ha caratteri non ammessi", + "0uoY11": "Show Status", "0yO7wF": "{n} sec", "1A7TZk": "Che cos’è Snort e come funziona?", "1Mo59U": "Vuoi davvero rimuovere questa nota dai preferiti?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} preferiti", "2k0Cv+": "Non mi piace ({n})", "2ukA4d": "{n} ore", + "3KNMbJ": "Articles", "3Rx6Qo": "Avanzato", "3cc4Ct": "Chiaro", "3gOsZq": "Traduttori", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Seguaci", "3xCwbZ": "O", "3yk8fB": "Portafoglio", + "40VR6s": "Nostr Connect", "450Fty": "Nessuno", "47FYwb": "Annulla", "4IPzdn": "Sviluppatori principali", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs è uno dei primi provider NIP-05 e offre una grande collezione di domini a prezzi ragionevoli", "4Z3t5i": "Utilizza imgproxy per comprimere le immagini", "4rYCjn": "Note personali", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Crea Account", "5oTnfy": "Acquista Handle", "5rOdPG": "Dopo aver configurato l'estensione per la gestione delle chiavi e generata una chiave, puoi seguire il nostro percorso per nuovi utenti per configurare il tuo profilo. Ti aiuterà a trovare persone interessanti da seguire su Nostr.", "5u6iEc": "Trasferisci a Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "Invia zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Ottieni una verifica", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Mi piace ({n})", "6uMqL1": "Non pagato", "7+Domh": "Note", "7BX/yC": "Commutatore di Account", "7hp70g": "NIP-05", - "7xzTiH": "{action} a {target}", "8/vBbP": "Riposta ({n})", "89q5wc": "Conferma Riposta", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "Nome troppo lungo", "8v1NN+": "Frase di accoppiamento", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Avanti", "9HU8vw": "Rispondi", "9SvQep": "Segue {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Fattura Lightning", "ADmfQT": "Parente", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "Questo autore è stato silenziato", "Adk34V": "Configura il tuo profilo", "Ai8VHU": "Ritenzione illimitata della nota sul relè Snort", @@ -92,6 +107,7 @@ "BOr9z/": "Snort è un progetto open source creato da appassionati nel loro tempo libero", "BWpuKl": "Aggiorna", "BcGMo+": "Le note contengono contenuto di testo, l'uso più popolare di queste note è quello di memorizzare i messaggi \"come tweet\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Disconnettiti", "C8HhVE": "Suggested Follows", @@ -106,20 +122,23 @@ "DZzCem": "Visualizza le ultime {n} note", "DcL8P+": "Sostenitore", "Dh3hbq": "Zap automatico", + "Dn82AL": "Live", "DtYelJ": "Trasferisci", "E8a4yq": "Segui alcuni account popolari", "ELbg9p": "Data Providers", "EPYwm7": "La tua chiave privata è la password. Se la perdi, perderai anche l'accesso al tuo account! Copiala e conservala in un posto sicuro. Non c'è modo di ripristinare la chiave privata.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Globale", "Ebl/B2": "Traduci in {lang}", "EcZF24": "Custom Relays", "EcglP9": "Chiave", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Acquista", "Eqjl5K": "Solo Snort e il nostro identificatore partner d'integrazione ti dà un nome di dominio colorato, ma puoi utilizzare anche altri servizi.", "F+B3x1": "Abbiamo anche collaborato con nostrplebs.com per darti più opzioni", "F3l7xL": "Aggiungi Account", "FDguSC": "{n} Zap", - "FP+D3H": "LNURL per inoltrare gli zap a", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Fatto!", "FSYL8G": "Trending Users", "FdhSU2": "Rivendica Ora", @@ -129,28 +148,32 @@ "G1BGCg": "Seleziona portafoglio", "GFOoEE": "Salt", "GL8aXW": "Preferiti ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "Rivendica il tuo indirizzo nostr Snort incluso", "Gcn9NQ": "Link magnetico", "GspYR7": "{n} Non mi piace", + "Gxcr08": "Broadcast Event", "H+vHiz": "Chiave esadecimale..", "H0JBH6": "Disconnettiti", "H6/kLh": "Ordine pagato!", "HAlOn1": "Nome", - "HF4YnO": "Watch Live!", "HFls6j": "il nome sarà disponibile in seguito", "HOzFdo": "Silenziato", "HWbkEK": "Clear cache and reload", "HbefNb": "Apri portafoglio", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Grazie per aver usato Snort, si prega di considerare la donazione se possibile.", "IEwZvs": "Sei sicuro di voler staccare questa nota?", "IKKHqV": "Follows", "INSqIz": "Nome utente Twitter...", "IUZC+0": "Questo significa che nessuno può modificare le note che hai creato e tutti possono facilmente verificare che le note che stanno leggendo sono create da te.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "Abbonamenti", "JCIgkj": "Nome utente", + "JGrt9q": "Send sats to {name}", "JHEHCk": "({n}) Zap", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Elimina", "K7AkdL": "Visualizza", "KAhAcM": "Inserisci la configurazione LNDHub", - "KLo3SP": "Motivo: {reason}", "KQvWvD": "Eliminato", "KWuDfz": "Ho salvato le mie chiavi, continua", "KahimY": "Tipo di evento sconosciuto: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "Altre connessioni", + "LR1XjT": "Pin too short", "LXxsbk": "Anonimo", "LgbKvU": "Commenta", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Menu debug", "MBAYRO": "Mostra \"Copia ID\" e \"Copia evento JSON\" nel menu contestuale di ogni messaggio", "MI2jkA": "Non disponibile:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Acquista {item}", "N2IrpM": "Conferma", + "NAidKb": "Notifications", "NAuFNH": "Hai già un abbonamento di questo tipo, si prega di rinnovare o pagare", "NNSu3d": "Importa Seguaci Twitter", "NdOYJJ": "Mmh, non c'è nulla qui... Vai alla {newUsersPage} per seguire alcuni utenti Nostr consigliati!", @@ -192,7 +219,6 @@ "OLEm6z": "Errore di login sconosciuto", "OQXnew": "L'abbonamento è ancora attivo, non puoi ancora rinnovare", "ORGv1Q": "Creato", - "P04gQm": "Tutti gli zap inviati a questa nota verranno ricevuti dal LNURL seguente", "P61BTu": "Copia evento JSON", "P7FD0F": "Sistema (Predefinito)", "P7nJT9": "Totale oggi (UTC): {amount} sat", @@ -207,7 +233,6 @@ "QxCuTo": "Arte di {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Abbonamento", - "R1fEdZ": "Inoltra gli Zap", "R81upa": "People you follow", "RDZVQL": "Verifica", "RahCRH": "Scaduto", @@ -217,19 +242,19 @@ "RoOyAh": "Relay", "Rs4kCE": "Preferito", "RwFaYs": "Sort", + "SMO+on": "Send zap to {name}", "SOqbe9": "Aggiorna indirizzo Lightning", "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", "SYQtZ7": "Proxy Indirizzo LN", "ShdEie": "Mark all read", "Sjo1P4": "Personalizzato", "Ss0sWu": "Paga ora", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "Registrazione in sospeso", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "Gli utenti devono accettare l'avviso di contenuto per mostrare il contenuto della nota.", "Up5U7K": "Blocca", @@ -239,15 +264,16 @@ "VR5eHw": "Chiave pubblica (npub/nprofile)", "VlJkSk": "{n} silenziato", "VnXp8Z": "Avatar", - "VtPV/B": "Accedi con estensione (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "Come funzionano le chiavi?", "W1yoZY": "Sembra che tu non abbia alcun abbonamento, puoi ottenere un {link}", "W2PiAr": "{n} Bloccato", "W9355R": "Riattiva", "WONP5O": "Trova i tuoi seguaci di Twitter su nostr (Dati forniti da {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "ad es. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Reazioni", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Inserisci la frase di accoppiamento", "ZKORll": "Attiva adesso", "ZLmyG9": "Contributori", - "ZUZedV": "Donazione Lightning:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "Sviluppatori di fondi e piattaforme che forniscono servizi di verifica NIP-05", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Mostra altro", "b12Goz": "Mnemonico", "b5vAk0": "Il tuo handle si comporterà come un indirizzo lightning e reindirizzerà al LNURL o all'indirizzo Lightning selezionato", + "bLZL5a": "Get Address", "bQdA2k": "Contenuto sensibile", "bep9C3": "Chiave Pubblica", "bfvyfs": "Anon", "brAXSu": "Scegli un nome utente", "bxv59V": "Proprio adesso", "c+oiJe": "Installa estensione", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Se hai una richiesta sul tuo ordine NIP-05, scrivimi in privato {link}", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "Indirizzo LN", "dOQCL8": "Nome visualizzato", "e61Jf3": "Prossimamente", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Segna tutto come letto", "eHAneD": "Emoji di reazione", "eJj8HD": "Verifica il tuo account", @@ -297,6 +326,7 @@ "fWZYP5": "Attaccato", "filwqD": "Leggi", "flnGvv": "A cosa pensi?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Salvato", "g5pX+a": "Info", "g985Wp": "Impossibile inviare il voto", @@ -310,9 +340,9 @@ "h8XMJL": "Distintivi", "hK5ZDk": "mondo", "hMzcSq": "Messaggi", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Supporto", "hicxcO": "Mostra le risposte", + "hmZ3Bz": "Media", "hniz8Z": "qui", "i/dBAR": "Zap Pool", "iCqGww": "Reazioni ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL traduzioni", "iGT1eE": "Impedisci agli account falsi di imitarti", "iNWbVV": "Handle", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Impossibile effettuare il login con la chiave privata su una connessione insicura. Utilizza un'estensione Nostr key manager", "ieGrWo": "Segui", "itPgxd": "Profilo", @@ -381,9 +410,11 @@ "qMx1sA": "Quantità Zap predefinita", "qUJTsT": "Bloccato", "qdGuQo": "La tua chiave privata è (non condividerla con nessuno)", + "qfmMQh": "This note has been muted", "qkvYUb": "Aggiungi al Profilo", "qmJ8kD": "Traduzione fallita", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Inserire la password del portafogli", "rT14Ow": "Aggiungi Relays", @@ -392,7 +423,10 @@ "rmdsT4": "{n} giorni", "rrfdTe": "Questa è la stessa tecnologia che viene utilizzata dai Bitcoin, ed è stata dimostrata essere estremamente sicura.", "rudscU": "Salvataggio non riuscito. Riprova più tardi", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort è stato progettato per avere un'esperienza simile a Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Non è consentito lo squatting e l'impersonazione. Snort e i nostri partner si riservano il diritto di terminare il tuo handle (non il tuo account - nessuno può toccarlo) per aver violato questa regola.", "tOdNiY": "Scuro", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "Distintivo del sostenitore", "u/vOPu": "Pagato", "u4bHcR": "Controlla il codice qui: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "I reposts devono essere confermati manualmente", + "uc0din": "Send sats splits to", "usAvMr": "Modifica profilo", "ut+2Cd": "Ottieni un partner identificatore", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} altro} other{& {n} altri}}", - "vU71Ez": "Paga con {wallet}", "vZ4quW": "NIP-05 è una specifica di verifica basata su DNS che ti aiuta a convalidare come un utente reale.", "vhlWFg": "Opzioni del sondaggio", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Modifica", "wLtRCF": "La tua chiave", + "wSZR47": "Submit", "wWLwvh": "Anonimo", "wYSD2L": "Indirizzo Nostr", "wih7iJ": "il nome è bloccato", + "wofVHy": "Moderation", "wqyN/i": "Per saperne di più su {service} clicca {link}", "wtLjP6": "Copia ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Questa nota è stata contrassegnata come sensibile, clicca qui per rivelarla", "x82IOl": "Silenzia", + "xIcAOU": "Votes by {type}", "xIoGG9": "Vai a", "xJ9n2N": "La tua chiave pubblica", "xKflGN": "Seguito da {username} su Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Lingua", "yCLnBC": "LNURL o indirizzo Lightning", "yCmnnm": "Lettura globale da", + "zCb8fX": "Weight", "zFegDD": "Contatta", "zINlao": "Proprietario", "zQvVDJ": "Tutto", "zcaOTs": "Quantità di Zap in sats", "zjJZBd": "Tutto fatto!", "zonsdq": "Caricamento del servizio LNURL fallito", - "zvCDao": "Visualizza automaticamente le ultime note" + "zvCDao": "Visualizza automaticamente le ultime note", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/ja_JP.json b/packages/app/src/translations/ja_JP.json index 45714a8b..18dcc7b4 100644 --- a/packages/app/src/translations/ja_JP.json +++ b/packages/app/src/translations/ja_JP.json @@ -1,24 +1,27 @@ { "+D82kt": "{id}をリポストしますか?", "+PzQ9Y": "今すぐ支払う", - "+Vxixo": "Secret Group Chat", + "+Vxixo": "秘密のグループチャット", "+aZY2h": "ザップの種類", + "+tShPg": "following", "+vA//S": "ログイン", "+vIQlC": "ハンドルを管理し続けるために、必ず、以下のパスワードを保存してください", "+vVZ/G": "接続", - "+xliwN": "{name} reposted", + "+xliwN": "{name} がリポスト", "/4tOwT": "スキップ", "/JE/X+": "アカウントサポート", "/PCavi": "公開", "/RD0e2": "Nostrはデジタル署名技術を使って投稿の改竄防止を図り、多数のリレーに安全に複製してコンテンツの冗長ストレージを提供しています。", + "/Xf4UW": "匿名で利用状況を送信します", "/d6vEc": "プロフィールを見つけやすく、共有しやすくなる", "/n5KSF": "{n}ミリ秒", - "00LcfG": "Load more", + "00LcfG": "さらに読み込む", "08zn6O": "鍵をエクスポート", "0Azlrb": "管理", "0BUTMv": "検索する", "0jOEtS": "無効なLNURL", "0mch2Y": "名前に使用できない文字が含まれています", + "0uoY11": "Show Status", "0yO7wF": "{n}秒", "1A7TZk": "Snortって何? どういう仕組み?", "1Mo59U": "本当にこの投稿をブックマークから削除しますか?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} ブックマーク", "2k0Cv+": "イヤ ({n})", "2ukA4d": "{n}時間", + "3KNMbJ": "Articles", "3Rx6Qo": "詳細オプション", "3cc4Ct": "ライト", "3gOsZq": "翻訳者", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} フォロワー", "3xCwbZ": "もしくは", "3yk8fB": "ウォレット", + "40VR6s": "Nostr Connect", "450Fty": "なし", "47FYwb": "キャンセル", "4IPzdn": "主要な開発者", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebsは宇宙で最初のNIP-05プロバイダのひとつで、豊富なドメインをリーズナブルな価格で提供します", "4Z3t5i": "imgproxyを使って画像を圧縮します", "4rYCjn": "自分用メモ", + "5BVs2e": "ザップ", + "5CB6zB": "Zap Splits", "5JcXdV": "アカウントを作成する", "5oTnfy": "ハンドルを購入する", "5rOdPG": "キーマネージャー拡張機能をセットアップして鍵を生成したら、新しいユーザーフローに従ってプロフィールを設定し、Nostrにいる興味深い人を見つけることができます。", "5u6iEc": "以下の公開鍵に転送する", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "Nostrのユーザー名は誰でも同じものを取得可能です。Nostrアドレスは登録時にあなただけに寄与されたオリジナルアドレスです。", "5ykRmX": "ザップする", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "{host}から画像のプロキシに失敗しました。ここをクリックして直接読み込みます", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "IDを入手する", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "スキ ({n})", "6uMqL1": "未払い", "7+Domh": "投稿", "7BX/yC": "アカウント切り替え", "7hp70g": "NIP-05", - "7xzTiH": "{target}に{action}", "8/vBbP": "リポスト ({n})", "89q5wc": "リポストの確認", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "{n} satsをザップする", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "名前が長すぎます", "8v1NN+": "ペアリングフレーズ", + "8xNnhi": "Nostr Extension", "9+Ddtu": "次へ", "9HU8vw": "返信", "9SvQep": "{n} フォロー", @@ -78,6 +92,7 @@ "9wO4wJ": "ライトニング インボイス", "ADmfQT": "返信元", "AGNz71": "{n} satsを全てザップ", + "AN0Z7Q": "Muted Words", "ASRK0S": "投稿者はミュートされています", "Adk34V": "プロフィールを設定", "Ai8VHU": "Snortリレーにおける無期限のノート保持", @@ -92,6 +107,7 @@ "BOr9z/": "Snortは、情熱的な人々が自由時間を使って作ったオープンソースプロジェクトです", "BWpuKl": "更新", "BcGMo+": "投稿はテキストのコンテンツを持ち、その主な用途は「ツイートのような」メッセージを保持することです。", + "C1LjMx": "Lightning Donation", "C5xzTC": "プレミアム", "C81/uG": "ログアウト", "C8HhVE": "おすすめのフォロー", @@ -106,22 +122,25 @@ "DZzCem": "最新の投稿{n}件を表示", "DcL8P+": "サポーター", "Dh3hbq": "自動ザップ", + "Dn82AL": "Live", "DtYelJ": "転送", "E8a4yq": "人気のアカウントをフォロー", "ELbg9p": "データプロバイダ", "EPYwm7": "秘密鍵はパスワードです。この鍵を失うと、アカウントにアクセスできなくなります!コピーして安全な場所に保管してください。秘密鍵をリセットする方法はありません。", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "グローバル", "Ebl/B2": "{lang}に翻訳", "EcZF24": "カスタムリレー", "EcglP9": "鍵", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "購入", "Eqjl5K": "カラフルなドメイン名を使えるのはSnortまたはパートナーのIDだけですが、他のサービスの使用も歓迎します。", "F+B3x1": "私たちはより多くのオプションを提供するため nostrplebs.com とも提携しています", "F3l7xL": "アカウントを追加する", "FDguSC": "{n} ザップ", - "FP+D3H": "ザップを転送するLNURL", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "完了!", - "FSYL8G": "Trending Users", + "FSYL8G": "話題のユーザー", "FdhSU2": "今すぐ要求する", "FfYsOb": "エラーが発生しました!", "FmXUJg": "フォローされています", @@ -129,47 +148,54 @@ "G1BGCg": "ウォレットを選択する", "GFOoEE": "ソルト", "GL8aXW": "ブックマーク ({n})", - "GSye7T": "Lightning Address", + "GQPtfk": "Join Stream", + "GSye7T": "ライトニングアドレス", "GUlSVG": "特典に含まれるSnort Nostrアドレスを要求する", "Gcn9NQ": "マグネットリンク", "GspYR7": "{n} イヤ", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Keyを入力", "H0JBH6": "ログアウト", "H6/kLh": "支払いが完了しました!", "HAlOn1": "名前", - "HF4YnO": "Watch Live!", "HFls6j": "名前は後で使用できるようになります", "HOzFdo": "ミュート中", "HWbkEK": "キャッシュをクリアして再読み込み", "HbefNb": "ウォレットを開く", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Snortをご利用いただき、どうもありがとうございます。できましたら寄付をご検討ください。", "IEwZvs": "本当にこの投稿のピン留めを解除しますか?", - "IKKHqV": "Follows", + "IKKHqV": " フォロー", "INSqIz": "Twitterユーザー名を入力", "IUZC+0": "このため、あなたが作成した投稿を誰も変更できず、誰でも簡単にあなたが作成した投稿であることを確認できます。", "Ig9/a1": "{n} satsを{name}に送りました", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "話題の投稿", "J+dIsA": "サブスクリプション", "JCIgkj": "ユーザー名", + "JGrt9q": "satsを{name}送る", "JHEHCk": "ザップ ({n})", "JPFYIM": "ライトニングアドレスがありません", - "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JeoS4y": "リポスト", + "JjGgXI": "ユーザーを検索", "JkLHGw": "ウェブサイト", "JymXbw": "秘密鍵", "K3r6DQ": "削除", "K7AkdL": "表示", "KAhAcM": "LNDHubの設定値を入力", - "KLo3SP": "理由: {reason}", "KQvWvD": "削除済み", "KWuDfz": "鍵を保存したので次へ", "KahimY": "不明なイベント種別: {kind}", "KoFlZg": "Mint URLを入力", + "KtsyO0": "Enter Pin", "LF5kYT": "その他の接続", + "LR1XjT": "Pin too short", "LXxsbk": "匿名", "LgbKvU": "コメント", "Lu5/Bj": "Zapstrで開く", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "デバッグメニュー", "MBAYRO": "「IDをコピー」と「イベントJSONをコピー」を全てのメッセージのコンテキストメニューに表示します", "MI2jkA": "利用不可:", @@ -177,9 +203,10 @@ "MRp6Ly": "Twitterユーザー名", "MWTx65": "既定のページ", "Mrpkot": "サブスクリプションに支払う", - "MuVeKe": "Buy nostr address", + "MuVeKe": "Nostrアドレスを購入", "MzRYWH": "{item}の購入", "N2IrpM": "確認", + "NAidKb": "Notifications", "NAuFNH": "このタイプのサブスクリプションを既にお持ちです。更新もしくは支払いをお願いいたします", "NNSu3d": "Twitterのフォローをインポートする", "NdOYJJ": "うーん、何もない…… {newUsersPage}をチェックしておすすめのNostrichをフォローしよう!", @@ -192,7 +219,6 @@ "OLEm6z": "未知のログインエラー", "OQXnew": "サブスクリプションがまだ有効なため、更新することができません", "ORGv1Q": "登録日", - "P04gQm": "このノートに送られた全てのザップは、以下のLNURLで受信されます", "P61BTu": "イベントJSONをコピー", "P7FD0F": "システム (デフォルト)", "P7nJT9": "本日の合計額 (UTC): {amount} sats", @@ -207,8 +233,7 @@ "QxCuTo": "Art by {name}", "Qxv0B2": "現在、Zap Poolには{number} satsあります。", "R/6nsx": "サブスクリプション", - "R1fEdZ": "ザップの転送", - "R81upa": "People you follow", + "R81upa": "あなたがフォローしている人", "RDZVQL": "チェック", "RahCRH": "失効", "RfhLwC": "制作者: {author}", @@ -217,20 +242,20 @@ "RoOyAh": "リレー", "Rs4kCE": "ブックマーク", "RwFaYs": "並び替え", + "SMO+on": "ザップを{name}に送る", "SOqbe9": "ライトニングアドレスの更新", "SP0+yi": "サブスクリプションの購入", - "SX58hM": "コピー", "SYQtZ7": "LNアドレス プロキシ", - "ShdEie": "Mark all read", + "ShdEie": "すべて既読", "Sjo1P4": "カスタム", "Ss0sWu": "今すぐ支払う", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "選択したユーザーの投稿ではメディアが自動的に表示され、それ以外はリンクのみが表示されます", - "TMfYfY": "Cashuトークン", + "TP/cMX": "Ended", "TpgeGw": "Hex Saltを入力", "Tpy00S": "ユーザー", "UDYlxu": "保留中のサブスクリプション", - "ULotH9": "金額: {amount} sats", - "UT7Nkj": "New Chat", + "UT7Nkj": "新規チャット", "UUPFlt": "ユーザーは、投稿内容の表示のためにコンテンツ警告へ同意する必要があります。", "Up5U7K": "ブロック", "VBadwB": "どうやらキーマネージャー拡張機能が見つけられません。ページを再読み込みしてください。", @@ -239,15 +264,16 @@ "VR5eHw": "公開鍵 (npub/nprofile)", "VlJkSk": "{n} ミュート", "VnXp8Z": "アバター", - "VtPV/B": "ブラウザ拡張機能でログイン (NIP-07)", - "VvaJst": "View Wallets", + "VvaJst": "ウォレットを表示", "Vx7Zm2": "鍵の役割は?", "W1yoZY": "サブスクリプションをお持ちでないようです。{link}から入手することができます", "W2PiAr": "{n} ブロック", "W9355R": "ミュート解除", "WONP5O": "Twitterでフォローしている人をNostrで見つける (データ提供: {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "例: ジャック", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "利用状況データを送信する", "XICsE8": "ファイルホスト", "XgWvGA": "リアクション", "Xopqkl": "デフォルトのザップ量は{number} satsで、例示された値はここから算出されます。", @@ -260,19 +286,21 @@ "Z4BMCZ": "ペアリングフレーズを入力", "ZKORll": "今すぐ有効化", "ZLmyG9": "貢献者", - "ZUZedV": "ライトニング寄付:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "プロフィールを設定", "a5UPxh": "NIP-05認証サービスを提供するプラットフォームや開発者に資金援助する", "a7TDNm": "グローバルタブと投稿タブで投稿がリアルタイムに流れるようになります", "aWpBzj": "もっと見る", "b12Goz": "ニーモニック", "b5vAk0": "あなたのハンドルはライトニングアドレスのように動作し、選択したLNURLまたはライトニングアドレスに転送されます", + "bLZL5a": "Get Address", "bQdA2k": "センシティブなコンテンツ", "bep9C3": "公開鍵", - "bfvyfs": "Anon", + "bfvyfs": "匿名", "brAXSu": "ユーザー名を決める", "bxv59V": "たった今", "c+oiJe": "拡張機能のインストール", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "NIP-05の注文に関するお問い合わせは、DMにて{link}までお願いします。", "c3g2hL": "再送信", "cFbU1B": "Albyをお使いですか?{link}にアクセスすると、NWCの設定が取得できます!", @@ -287,16 +315,18 @@ "d7d0/x": "ライトニング アドレス", "dOQCL8": "表示名", "e61Jf3": "まもなく開始", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "全て既読にする", "eHAneD": "リアクションの絵文字", "eJj8HD": "認証を得る", "eSzf2G": "{nIn} satsを1回ザップすると、{nOut} satsがZap Poolに割り当てられます。", - "eXT2QQ": "Group Chat", - "fBI91o": "Zap", + "eXT2QQ": "グループチャット", + "fBI91o": "ザップ", "fOksnD": "LNURLサービスがザップに対応していないため投票できません", "fWZYP5": "ピン留めされた投稿", "filwqD": "読み取り", "flnGvv": "思いつくことは?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "保存済み", "g5pX+a": "自己紹介", "g985Wp": "投票を送信できませんでした", @@ -306,13 +336,13 @@ "gXgY3+": "まだすべてのクライアントがこれをサポートしているわけではありません。", "gczcC5": "購読", "gjBiyj": "読込中…", - "grQ+mI": "Proof of Work", + "grQ+mI": "プルーフオブワーク", "h8XMJL": "バッジ", "hK5ZDk": "世界", "hMzcSq": "メッセージ", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "対応", "hicxcO": "返信を見る", + "hmZ3Bz": "Media", "hniz8Z": "こちら", "i/dBAR": "Zap Pool", "iCqGww": "リアクション ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL翻訳", "iGT1eE": "なりすましを防ぎます", "iNWbVV": "ハンドル", - "iUsU2x": "Mint: {url}", "iXPL0Z": "安全でない接続では秘密鍵でログインすることはできません。代わりにNostrキーマネージャー拡張機能を使用してください。", "ieGrWo": "フォロー", "itPgxd": "プロフィール", @@ -368,10 +397,10 @@ "oJ+JJN": "何も見つかりません (´・ω・`)", "odFwjL": "フォローのみ", "odhABf": "ログイン", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "ojzbwv": "まだNostrアドレスを持ってないようだね!以下のリンクをチェックして、ぜひ手に入れよう:{link}", "osUr8O": "これらの拡張機能を使用して、ほとんどのNostrサイトにログインできます。", "oxCa4R": "IDを入手すると、あなたを知っている人があなたを確認するのに役立ちます。@jackというユーザー名の人はたくさんいても、jack@cash.appは一人だけです。", - "p4N05H": "Upload", + "p4N05H": "アップロード", "p85Uwy": "有効なサブスクリプション", "pI+77w": "Snortリレーからダウンロードできるバックアップ", "puLNUJ": "ピン留め", @@ -381,9 +410,11 @@ "qMx1sA": "既定のザップ額", "qUJTsT": "ブロック", "qdGuQo": "秘密鍵 (誰とも共有しないこと)", + "qfmMQh": "This note has been muted", "qkvYUb": "プロフィールに追加", "qmJ8kD": "翻訳できませんでした", - "qtWLmt": "Like", + "qtWLmt": "スキ", + "qz9fty": "Incorrect pin", "r3C4x/": "ソフトウェア", "r5srDR": "ウォレットのパスワードを入力", "rT14Ow": "リレーを追加する", @@ -392,7 +423,10 @@ "rmdsT4": "{n}日", "rrfdTe": "これはビットコインで使われているのと同じ技術で、非常に安全であることが証明されています。", "rudscU": "フォローの読み込みに失敗しました。後でやり直してください", + "sKDn4e": "Show Badges", + "sUNhQE": "ユーザー", "sWnYKw": "SnortはTwitterに似た体験ができるようにデザインされています。", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "サイバースクワッティングとなりすましは禁止されています。ルールに抵触した場合、Snortとパートナーはハンドルを停止する権利を保有します。(アカウントではありません。それは誰にも取り上げることはできません。)", "tOdNiY": "ダーク", "th5lxp": "書き込みリレーの一部に投稿を送信する", @@ -400,28 +434,29 @@ "ttxS0b": "サポーターバッジ", "u/vOPu": "支払済", "u4bHcR": "コードはこちらでご確認ください: {link}", - "uD/N6c": "{target}に{n} satsをザップする", "uSV4Ti": "リポスト前に確認を表示します", + "uc0din": "Send sats splits to", "usAvMr": "プロフィールを編集", "ut+2Cd": "パートナーIDを入手", - "v8lolG": "Start chat", + "v8lolG": "チャットを開始", "vOKedj": "{n,plural,=1{と他{n}人} other{と他{n}人}}", - "vU71Ez": "{wallet}で支払う", "vZ4quW": "NIP-05は、ユーザが本物であることを検証するDNSベースの仕組みです。", "vhlWFg": "投票オプション", - "vlbWtt": "Get a free one", + "vlbWtt": "無料版を入手", "vrTOHJ": "{amount} sats", - "vxwnbh": "Amount of work to apply to all published events", + "vxwnbh": "公開イベントに適用する作業量", "wEQDC6": "編集", "wLtRCF": "自分の鍵", + "wSZR47": "Submit", "wWLwvh": "匿名", "wYSD2L": "Nostrアドレス", "wih7iJ": "名前がブロックされています", + "wofVHy": "Moderation", "wqyN/i": "{service}の詳細を{link}で見る", "wtLjP6": "IDをコピー", "x/Fx2P": "利用しているサービスへ支援するため、自分のザップの一部をプールに集めましょう!", - "x/q8d5": "この投稿はセンシティブとしてマークされています。ここをクリックして表示します", "x82IOl": "ミュート", + "xIcAOU": "Votes by {type}", "xIoGG9": "開く:", "xJ9n2N": "公開鍵", "xKflGN": "{username}のNostrでのフォロー", @@ -433,11 +468,13 @@ "y1Z3or": "言語", "yCLnBC": "LNURLまたはライトニングアドレス", "yCmnnm": "グローバル読み取り元", + "zCb8fX": "Weight", "zFegDD": "連絡先", "zINlao": "運営者", "zQvVDJ": "すべて", "zcaOTs": "ザップ額 (satoshi)", "zjJZBd": "準備完了!", "zonsdq": "LNURLサービスの読み込みに失敗しました", - "zvCDao": "最新の記事を自動で表示する" + "zvCDao": "最新の記事を自動で表示する", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/ko_KR.json b/packages/app/src/translations/ko_KR.json index 6850017d..e1b96dd4 100644 --- a/packages/app/src/translations/ko_KR.json +++ b/packages/app/src/translations/ko_KR.json @@ -50,6 +50,7 @@ "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", "4Z3t5i": "Use imgproxy to compress images", "4rYCjn": "Note to Self", + "5BVs2e": "zap", "5JcXdV": "Create Account", "5oTnfy": "Buy Handle", "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", @@ -392,6 +393,7 @@ "rmdsT4": "{n} days", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", "sWnYKw": "Snort is designed to have a similar experience to Twitter.", "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", "tOdNiY": "Dark", @@ -422,6 +424,7 @@ "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", "x/q8d5": "This note has been marked as sensitive, click here to reveal", "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", "xIoGG9": "Go to", "xJ9n2N": "Your public key", "xKflGN": "{username}''s Follows on Nostr", diff --git a/packages/app/src/translations/nl_NL.json b/packages/app/src/translations/nl_NL.json index 6fc4788c..47aebbb1 100644 --- a/packages/app/src/translations/nl_NL.json +++ b/packages/app/src/translations/nl_NL.json @@ -1,443 +1,480 @@ { - "+D82kt": "Are you sure you want to repost: {id}", - "+PzQ9Y": "Payout Now", - "+Vxixo": "Secret Group Chat", + "+D82kt": "Weet je het zeker dat je dit wil herplaatsen: {id}", + "+PzQ9Y": "Nu uitbetalen", + "+Vxixo": "Geheime Groep Chat", "+aZY2h": "Zap Type", - "+vA//S": "Logins", - "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", - "+vVZ/G": "Connect", - "+xliwN": "{name} reposted", - "/4tOwT": "Skip", - "/JE/X+": "Account Support", - "/PCavi": "Public", - "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", - "/d6vEc": "Make your profile easier to find and share", + "+tShPg": "following", + "+vA//S": "Aanmeldingen", + "+vIQlC": "Zorg ervoor dat u het volgende wachtwoord opslaat om uw account in de toekomst te beheren", + "+vVZ/G": "Verbind", + "+xliwN": "{name} Heeft herplaatst", + "/4tOwT": "Overslaan", + "/JE/X+": "Account hulp", + "/PCavi": "Openbaar", + "/RD0e2": "Nostr maakt gebruik van digitale handtekeningentechnologie om manipulatiebeveiligde notes te realiseren. Zo kunnen de notes consistent naar veel relays tegelijk worden verzonden en wordt overbodige opslag voorkomen.", + "/Xf4UW": "Send anonymous usage metrics", + "/d6vEc": "Maak je profiel gemakkelijker om te vinden en te delen", "/n5KSF": "{n} ms", - "00LcfG": "Load more", - "08zn6O": "Export Keys", - "0Azlrb": "Manage", + "00LcfG": "Meer laden", + "08zn6O": "Sleutel exporteren", + "0Azlrb": "Beheren", "0BUTMv": "Zoeken naar...", - "0jOEtS": "Invalid LNURL", - "0mch2Y": "name has disallowed characters", - "0yO7wF": "{n} secs", - "1A7TZk": "What is Snort and how does it work?", - "1Mo59U": "Are you sure you want to remove this note from bookmarks?", - "1R43+L": "Enter Nostr Wallet Connect config", - "1c4YST": "Connected to: {node} 🎉", - "1iQ8GN": "Toggle Preview", - "1nYUGC": "{n} Following", - "1udzha": "Conversations", - "2/2yg+": "Add", - "25V4l1": "Banner", - "2IFGap": "Donate", - "2LbrkB": "Enter password", - "2a2YiP": "{n} Bookmarks", - "2k0Cv+": "Dislikes ({n})", + "0jOEtS": "Ongeldig LNURL", + "0mch2Y": "naam heeft verboden tekens", + "0uoY11": "Show Status", + "0yO7wF": "{n} seconden", + "1A7TZk": "Wat is Snort en hoe werkt het?", + "1Mo59U": "Weet u zeker dat u deze notitie uit bladwijzers wilt verwijderen?", + "1R43+L": "Voer Nostr Wallet Connect configuratie in", + "1c4YST": "Verbonden met: {node}🎉", + "1iQ8GN": "Preview tonen/verbergen", + "1nYUGC": "{n} Volgend", + "1udzha": "Gesprekken", + "2/2yg+": "Toevoegen", + "25V4l1": "Omslagfoto", + "2IFGap": "Doneer", + "2LbrkB": "Wachtwoord invoeren", + "2a2YiP": "{n} Bladwijzers", + "2k0Cv+": "Niet leuk ({n})", "2ukA4d": "{n} uur geleden", - "3Rx6Qo": "Advanced", - "3cc4Ct": "Light", + "3KNMbJ": "Articles", + "3Rx6Qo": "Geavanceerd", + "3cc4Ct": "Licht", "3gOsZq": "Vertalers", - "3qnJlS": "You are voting with {amount} sats", - "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", - "3tVy+Z": "{n} Followers", + "3qnJlS": "Je stemt met {amount} sats", + "3t3kok": "{n,plural,one {}=1{{n} nieuwe note} other{{n} nieuwe notes}}", + "3tVy+Z": "{n} Volgers", "3xCwbZ": "Of", "3yk8fB": "Wallet", - "450Fty": "None", + "40VR6s": "Nostr Connect", + "450Fty": "Geen", "47FYwb": "Annuleer", - "4IPzdn": "Primary Developers", - "4L2vUY": "Your new NIP-05 handle is:", - "4OB335": "Dislike", - "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", - "4Z3t5i": "Use imgproxy to compress images", - "4rYCjn": "Note to Self", - "5JcXdV": "Create Account", - "5oTnfy": "Buy Handle", - "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", - "5u6iEc": "Transfer to Pubkey", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", - "5ykRmX": "Send zap", - "65BmHb": "Failed to proxy image from {host}, click here to load directly", - "6Yfvvp": "Get an identifier", + "4IPzdn": "Primaire ontwikkelaars", + "4L2vUY": "Uw nieuwe NIP-05 handvat is:", + "4OB335": "Vind-ik-niet-leuks", + "4Vmpt4": "NostrPlebs is een van de eerste Nostr NIP-05 providers en biedt een goede verzameling domeinen aan tegen redelijke prijzen", + "4Z3t5i": "Gebruik imgproxy om foto's te comprimeren", + "4rYCjn": "Notitie aan mijzelf", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", + "5JcXdV": "Account aanmaken", + "5oTnfy": "Koop Account-Naam", + "5rOdPG": "Zodra je de key manager extensie hebt geïnstalleerd en een key hebt gegenereerd kunt u onze Nieuwe Account Flow gebruiken om uw profiel in te stellen en interessante Nostr accounts vinden om te volgen.", + "5u6iEc": "Overzetten naar Pubkey", + "5vMmmR": "Gebruikersnamen zijn niet uniek op Nostr. Het Nostr adres(!) is uw unieke account label (e.g. name@nostrplebs.com).", + "5ykRmX": "Zend zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", + "65BmHb": "Proxy afbeelding laden van {host} is mislukt, klik hier om direct te laden", + "6OSOXl": "Reason: {reason}", + "6Yfvvp": "Krijg een identifier", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Likes ({n})", - "6uMqL1": "Unpaid", + "6uMqL1": "Niet betaald", "7+Domh": "Notes", - "7BX/yC": "Account Switcher", + "7BX/yC": "Account wisselaar", "7hp70g": "NIP-05", - "7xzTiH": "{action} to {target}", "8/vBbP": "Reposts ({n})", - "89q5wc": "Confirm Reposts", + "89q5wc": "Bevestig Reposts", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", - "8g2vyB": "name too long", - "8v1NN+": "Pairing phrase", - "9+Ddtu": "Next", - "9HU8vw": "Reply", - "9SvQep": "Follows {n}", - "9WRlF4": "Send", - "9gqH2W": "Login", - "9pMqYs": "Nostr Address", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", + "8g2vyB": "naam is te lang", + "8v1NN+": "Paring-zin", + "8xNnhi": "Nostr Extension", + "9+Ddtu": "Volgende", + "9HU8vw": "Reageren", + "9SvQep": "Volgend {n}", + "9WRlF4": "Verzend", + "9gqH2W": "Inloggen", + "9pMqYs": "Nostr Adres", "9wO4wJ": "Lightning Invoice", "ADmfQT": "Parent", - "AGNz71": "Zap All {n} sats", - "ASRK0S": "This author has been muted", - "Adk34V": "Setup your Profile", - "Ai8VHU": "Unlimited note retention on Snort relay", - "AkCxS/": "Reason", + "AGNz71": "Zap Iedereen {n} sats", + "AN0Z7Q": "Muted Words", + "ASRK0S": "Deze auteur is gedempt", + "Adk34V": "Profiel instellen", + "Ai8VHU": "Onbeperkt note-behoud op Snort relay", + "AkCxS/": "Reden", "AnLrRC": "Non-Zap", - "AyGauy": "Login", - "B4C47Y": "name too short", + "AyGauy": "Inloggen", + "B4C47Y": "naam is te kort", "B6+XJy": "zapped", "B6H7eJ": "nsec, npub, nip-05, hex", - "BGCM48": "Write access to Snort relay, with 1 year of event retention", - "BOUMjw": "No nostr users found for {twitterUsername}", - "BOr9z/": "Snort is an open source project built by passionate people in their free time", - "BWpuKl": "Update", - "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "BGCM48": "Schrijf toegang tot Snort relays en 1 jaar note-opslag", + "BOUMjw": "Geen Nostr account gevonden voor {twitterUsername}", + "BOr9z/": "Snort is een open-source project gebouwd door gepassioneerde mensen in hun vrije tijd", + "BWpuKl": "Updaten", + "BcGMo+": "Notes bevatten tekst; het populairste gebruik van deze notities is het posten van \"Tweet-achtige\" berichten.", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Uitloggen", - "C8HhVE": "Suggested Follows", - "CHTbO3": "Failed to load invoice", - "CVWeJ6": "Trending People", - "CmZ9ls": "{n} Muted", + "C8HhVE": "Voorgestelde Accounts", + "CHTbO3": "Invoice laden mislukt", + "CVWeJ6": "Trending Accounts", + "CmZ9ls": "{n} Gedempt", "CsCUYo": "{n} sats", - "Cu/K85": "Translated from {lang}", - "D+KzKd": "Automatically zap every note when loaded", - "D3idYv": "Settings", - "DKnriN": "Send sats", - "DZzCem": "Show latest {n} notes", + "Cu/K85": "Vertaald vanuit {lang}", + "D+KzKd": "Zap elke note automatisch wanneer geladen", + "D3idYv": "Instellingen", + "DKnriN": "Sats verzenden", + "DZzCem": "Toon laatste {n} notes", "DcL8P+": "Supporter", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Transfer", - "E8a4yq": "Follow some popular accounts", - "ELbg9p": "Data Providers", - "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "E8a4yq": "Volg wat populaire accounts", + "ELbg9p": "Data aanbieders", + "EPYwm7": "Uw privésleutel is uw wachtwoord. Als u deze sleutel verliest, verliest u de toegang tot uw account! Kopieer het en bewaar het goed. Er is geen manier om je privésleutel te resetten.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", - "Ebl/B2": "Translate to {lang}", + "Ebl/B2": "Vertalen naar {lang}", "EcZF24": "Custom Relays", - "EcglP9": "Key", - "EnCOBJ": "Buy", - "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", - "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", - "F3l7xL": "Add Account", + "EcglP9": "Sleutel", + "EjFyoR": "On-chain Donation Address", + "EnCOBJ": "Kopen", + "Eqjl5K": "Alleen Snort en onze partner-identifiers geven u een kleurrijke domeinnaam, maar u bent ook welkom om andere diensten te gebruiken.", + "F+B3x1": "We werken ook samen met Nostrplebs.com om je meer opties te geven", + "F3l7xL": "Account toevoegen", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL to forward zaps to", - "FS3b54": "Done!", - "FSYL8G": "Trending Users", - "FdhSU2": "Claim Now", - "FfYsOb": "An error has occured!", - "FmXUJg": "follows you", - "G/yZLu": "Remove", - "G1BGCg": "Select Wallet", - "GFOoEE": "Salt", - "GL8aXW": "Bookmarks ({n})", - "GSye7T": "Lightning Address", - "GUlSVG": "Claim your included Snort nostr address", + "FMfjrl": "Show status messages on profile pages", + "FS3b54": "Klaar!", + "FSYL8G": "Trending Accounts", + "FdhSU2": "Claim nu", + "FfYsOb": "Er is een fout opgetreden!", + "FmXUJg": "volgt u", + "G/yZLu": "Verwijderen", + "G1BGCg": "Selecteer Wallet", + "GFOoEE": "Zout", + "GL8aXW": "Bladwijzers ({n})", + "GQPtfk": "Join Stream", + "GSye7T": "Lightning adres", + "GUlSVG": "Claim je inbegrepen Snort Nostr adres", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Dislike", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Key..", - "H0JBH6": "Log Out", - "H6/kLh": "Order Paid!", - "HAlOn1": "Name", - "HF4YnO": "Watch Live!", - "HFls6j": "name will be available later", - "HOzFdo": "Muted", - "HWbkEK": "Clear cache and reload", - "HbefNb": "Open Wallet", - "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", - "IEwZvs": "Are you sure you want to unpin this note?", - "IKKHqV": "Follows", - "INSqIz": "Twitter username...", - "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", - "Ig9/a1": "Sent {n} sats to {name}", + "H0JBH6": "Uitloggen", + "H6/kLh": "Bestelling betaald!", + "HAlOn1": "Naam", + "HFls6j": "naam zal later beschikbaar zijn", + "HOzFdo": "Gedempt", + "HWbkEK": "Cache wissen en herladen", + "HbefNb": "Open wallet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", + "IDjHJ6": "Bedankt voor het gebruiken van Snort, overweeg alstublieft om te doneren als je kunt.", + "IEwZvs": "Weet u zeker dat u deze note wilt losmaken?", + "IKKHqV": "Volgers", + "INSqIz": "Twitter-gebruikersnaam...", + "IUZC+0": "Dit betekent dat niemand notes kan wijzigen die je hebt gemaakt en dat iedereen kan verifiëren dat de notes die ze lezen door jou worden gemaakt.", + "Ig9/a1": "{n} sats verzonden naar {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", - "J+dIsA": "Subscriptions", - "JCIgkj": "Username", + "J+dIsA": "Abonnementen", + "JCIgkj": "Gebruikersnaam", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", - "JPFYIM": "No lightning address", + "JPFYIM": "Geen lightning adres", "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JjGgXI": "Zoek Account", "JkLHGw": "Website", - "JymXbw": "Private Key", - "K3r6DQ": "Delete", - "K7AkdL": "Show", - "KAhAcM": "Enter LNDHub config", - "KLo3SP": "Reason: {reason}", - "KQvWvD": "Deleted", - "KWuDfz": "I have saved my keys, continue", - "KahimY": "Unknown event kind: {kind}", - "KoFlZg": "Enter mint URL", - "LF5kYT": "Other Connections", - "LXxsbk": "Anonymous", - "LgbKvU": "Comment", - "Lu5/Bj": "Open on Zapstr", - "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", - "M3Oirc": "Debug Menus", - "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", - "MI2jkA": "Not available:", - "MP54GY": "Wallet password", - "MRp6Ly": "Twitter username", - "MWTx65": "Default Page", - "Mrpkot": "Pay for subscription", - "MuVeKe": "Buy nostr address", - "MzRYWH": "Buying {item}", - "N2IrpM": "Confirm", - "NAuFNH": "You already have a subscription of this type, please renew or pay", - "NNSu3d": "Import Twitter Follows", - "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", - "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", - "NfNk2V": "Your private key", - "NndBJE": "New users page", - "O9GTIc": "Profile picture", + "JymXbw": "Privésleutel", + "K3r6DQ": "Wis", + "K7AkdL": "Weergeven", + "KAhAcM": "Voer LNDHub config in", + "KQvWvD": "Verwijderd", + "KWuDfz": "Ik heb mijn sleutels opgeslagen, doorgaan", + "KahimY": "Onbekende event soort: {kind}", + "KoFlZg": "Voer mint URL in", + "KtsyO0": "Enter Pin", + "LF5kYT": "Andere Connecties", + "LR1XjT": "Pin too short", + "LXxsbk": "Anoniem", + "LgbKvU": "Opmerking", + "Lu5/Bj": "Open op Zapstr", + "Lw+I+J": "{n,plural,one {}=0{{name} gezapped} other{{name} & {n} anderen}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", + "M3Oirc": "Debug-menu", + "MBAYRO": "Toont \"Kopieer ID\" en \"Kopieer Event JSON\" in het context menu bij elk bericht", + "MI2jkA": "Niet beschikbaar:", + "MP54GY": "Wachtwoord Wallet", + "MRp6Ly": "Twitter-gebruikersnaam", + "MWTx65": "Standaardpagina", + "Mrpkot": "Betaal abonnement", + "MuVeKe": "Koop Nostr adres", + "MzRYWH": "{item} kopen", + "N2IrpM": "Bevestig", + "NAidKb": "Notifications", + "NAuFNH": "U heeft al een abonnement van dit type, gelieve te vernieuwen of te betalen", + "NNSu3d": "Importeer Twitter Volglijst", + "NdOYJJ": "Hmm niets hier... Bekijk {newUsersPage} om aanbevolen Nostriches te volgen!", + "NepkXH": "Kan niet stemmen met {amount} sats, stel een ander standaard zap bedrag in", + "NfNk2V": "Uw privésleutel", + "NndBJE": "Nieuwe gebruikerspagina", + "O9GTIc": "Profiel foto", "OEW7yJ": "Zaps", - "OKhRC6": "Share", - "OLEm6z": "Unknown login error", - "OQXnew": "You subscription is still active, you can't renew yet", - "ORGv1Q": "Created", - "P04gQm": "All zaps sent to this note will be received by the following LNURL", - "P61BTu": "Copy Event JSON", - "P7FD0F": "System (Default)", - "P7nJT9": "Total today (UTC): {amount} sats", - "PCSt5T": "Preferences", - "PLSbmL": "Your mnemonic phrase", - "PamNxw": "Unknown file header: {name}", - "Pe0ogR": "Theme", - "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "OKhRC6": "Deel", + "OLEm6z": "Onbekende inlogfout", + "OQXnew": "Uw abonnement is nog actief, u kunt nog niet vernieuwen", + "ORGv1Q": "Aangemaakt", + "P61BTu": "Kopieer JSON event", + "P7FD0F": "Systeem (Standaard)", + "P7nJT9": "Totaal vandaag (UTC): {amount} sats", + "PCSt5T": "Voorkeuren", + "PLSbmL": "Uw wachtwoord om te onthouden", + "PamNxw": "Onbekende bestandsheader: {name}", + "Pe0ogR": "Thema", + "PrsIg7": "Reacties worden op elke pagina getoond, indien uitgeschakeld worden er geen reacties getoond", "QDFTjG": "{n} Relays", - "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", - "QawghE": "You can change your username at any point.", - "QxCuTo": "Art by {name}", - "Qxv0B2": "You currently have {number} sats in your zap pool.", - "R/6nsx": "Subscription", - "R1fEdZ": "Forward Zaps", - "R81upa": "People you follow", - "RDZVQL": "Check", - "RahCRH": "Expired", - "RfhLwC": "By: {author}", - "RhDAoS": "Are you sure you want to delete {id}", + "QWhotP": "Zap Pool werkt alleen als u een van de ondersteunde wallet connecties gebruikt (WebLN, LNDHub of Nostr Wallet Connect)", + "QawghE": "Je kan je gebruikersnaam op elk moment veranderen.", + "QxCuTo": "Kunst door {name}", + "Qxv0B2": "Op dit moment heb je {number} sats in je Zap-Pool.", + "R/6nsx": "Abonnement", + "R81upa": "Mensen die je volgt", + "RDZVQL": "Controleer", + "RahCRH": "Verlopen", + "RfhLwC": "Door: {author}", + "RhDAoS": "Weet u zeker dat u dit wilt verwijderen {id}", "RjpoYG": "Recent", "RoOyAh": "Relays", - "Rs4kCE": "Bookmark", - "RwFaYs": "Sort", - "SOqbe9": "Update Lightning Address", - "SP0+yi": "Buy Subscription", - "SX58hM": "Copy", - "SYQtZ7": "LN Address Proxy", - "ShdEie": "Mark all read", + "Rs4kCE": "Bladwijzers", + "RwFaYs": "Sorteer", + "SMO+on": "Send zap to {name}", + "SOqbe9": "Update Lightning Adres", + "SP0+yi": "Abonnement kopen", + "SYQtZ7": "LN adres Proxy", + "ShdEie": "Markeer alle als gelezen", "Sjo1P4": "Custom", - "Ss0sWu": "Pay Now", - "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "Ss0sWu": "Nu betalen", + "StKzTE": "The author has marked this note as a sensitive topic", + "TDR5ge": "Media in notities wordt automatisch getoond voor de geselecteerde personen, anders wordt alleen de link getoond", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", - "Tpy00S": "People", - "UDYlxu": "Pending Subscriptions", - "ULotH9": "Amount: {amount} sats", - "UT7Nkj": "New Chat", - "UUPFlt": "Users must accept the content warning to show the content of your note.", - "Up5U7K": "Block", - "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", - "VN0+Fz": "Balance: {amount} sats", - "VOjC1i": "Pick which upload service you want to upload attachments to", - "VR5eHw": "Public key (npub/nprofile)", - "VlJkSk": "{n} muted", - "VnXp8Z": "Avatar", - "VtPV/B": "Login with Extension (NIP-07)", - "VvaJst": "View Wallets", - "Vx7Zm2": "How do keys work?", - "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", - "W2PiAr": "{n} Blocked", - "W9355R": "Unmute", - "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "Tpy00S": "Personen", + "UDYlxu": "Abonnementen in afwachting", + "UT7Nkj": "Nieuwe Chat", + "UUPFlt": "Gebruikers moeten akkoord gaan met de waarschuwing van de inhoud om de inhoud van uw notitie te tonen.", + "Up5U7K": "Blokkeren", + "VBadwB": "Hmm, kan de extentie van een sleutelbeheerder niet vinden.. probeer de pagina opnieuw te laden.", + "VN0+Fz": "Saldo: {amount} sats", + "VOjC1i": "Kies naar welke service u bijlagen wilt uploaden", + "VR5eHw": "Publieke sleutel (npub/nprofile)", + "VlJkSk": "{n} gedempt", + "VnXp8Z": "Profielfoto", + "VvaJst": "Bekijk wallets", + "Vx7Zm2": "Hoe werken ze?", + "W1yoZY": "Het lijkt erop dat je geen abonnement hebt, je kan er één {link} krijgen", + "W2PiAr": "{n} Geblokkeerd", + "W9355R": "Niet langer negeren", + "WONP5O": "Vind je Twitter volglijst op nostr (Gegevens verstrekt door {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "e.g. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", - "XgWvGA": "Reactions", - "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", - "XrSk2j": "Redeem", - "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", - "Y31HTH": "Help fund the development of Snort", - "YDURw6": "Service URL", - "YXA3AH": "Enable reactions", - "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", - "Z4BMCZ": "Enter pairing phrase", - "ZKORll": "Activate Now", - "ZLmyG9": "Contributors", - "ZUZedV": "Lightning Donation:", - "Zr5TMx": "Setup profile", - "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", - "a7TDNm": "Notes will stream in real time into global and notes tab", - "aWpBzj": "Show more", + "XgWvGA": "Reacties", + "Xopqkl": "Uw standaard zap bedrag is {number} sats, voorbeeldwaarden worden hiermee berekend.", + "XrSk2j": "Inwisselen", + "XzF0aC": "Sleutelbeheer extensies zijn veiliger en maken het mogelijk om eenvoudig in te loggen op een Nostr client, hier zijn enkele bekende extensies:", + "Y31HTH": "Help met de ontwikkeling van Snort", + "YDURw6": "Service-URL", + "YXA3AH": "Reacties inschakelen", + "Z0FDj+": "Abonneer je op Snort {plan} voor {price} en ontvang de volgende beloningen", + "Z4BMCZ": "Voer koppelingszin in", + "ZKORll": "Activeer Nu", + "ZLmyG9": "Bijdragers", + "ZS+jRE": "Send zap splits to", + "Zr5TMx": "Profiel instellen", + "a5UPxh": "Support ontwikkelaars en platforms die NIP-05 identificatie-diensten aanbieden", + "a7TDNm": "Notes zullen in real-time worden gestreamt naar het Global en Notes tabblad", + "aWpBzj": "Toon meer", "b12Goz": "Mnemonic", - "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", - "bQdA2k": "Sensitive Content", - "bep9C3": "Public Key", + "b5vAk0": "Uw Nostr adres gedraagt zich als een Lightning adres en stuurt zaps door naar uw gekozen LNURL/Lightning adres", + "bLZL5a": "Get Address", + "bQdA2k": "Gevoelige inhoud", + "bep9C3": "Publieke sleutel", "bfvyfs": "Anon", - "brAXSu": "Pick a username", - "bxv59V": "Just now", - "c+oiJe": "Install Extension", - "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", - "c3g2hL": "Broadcast Again", - "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", - "cPIKU2": "Following", + "brAXSu": "Kies een gebruikersnaam", + "bxv59V": "Zojuist", + "c+oiJe": "Extensie Installeren", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", + "c35bj2": "Voor vragen over je NIP-05 bestelling stuur een DM: {link}", + "c3g2hL": "Opnieuw uitzenden", + "cFbU1B": "Gebruik je Alby? Ga naar {link} om je Nostr Wallet Connect in te stellen!", + "cPIKU2": "Volgt", "cQfLWb": "URL..", - "cWx9t8": "Mute all", - "cg1VJ2": "Connect Wallet", - "cuP16y": "Multi account support", - "cuV2gK": "name is registered", - "cyR7Kh": "Back", - "d6CyG5": "History", + "cWx9t8": "Demp allen", + "cg1VJ2": "Wallet verbinden", + "cuP16y": "Ondersteuning voor meerdere accounts", + "cuV2gK": "naam is geregistreerd", + "cyR7Kh": "Terug", + "d6CyG5": "Geschiedenis", "d7d0/x": "LN Address", - "dOQCL8": "Display name", - "e61Jf3": "Coming soon", - "e7qqly": "Mark All Read", - "eHAneD": "Reaction emoji", - "eJj8HD": "Get Verified", - "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", - "eXT2QQ": "Group Chat", + "dOQCL8": "Weergavenaam", + "e61Jf3": "Binnenkort beschikbaar", + "e7VmYP": "Enter pin to unlock your private key", + "e7qqly": "Alles markeren als gelezen", + "eHAneD": "Emoji reactie", + "eJj8HD": "Word geverifieerd", + "eSzf2G": "Een enkele zap van {nIn} sats zal {nOut} sats toewijzen aan de zap pool.", + "eXT2QQ": "Groepschat", "fBI91o": "Zap", - "fOksnD": "Can't vote because LNURL service does not support zaps", - "fWZYP5": "Pinned", - "filwqD": "Read", - "flnGvv": "What's on your mind?", - "fsB/4p": "Saved", - "g5pX+a": "About", - "g985Wp": "Failed to send vote", - "gBdUXk": "Save your keys!", - "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", - "gDzDRs": "Emoji to send when reactiong to a note", - "gXgY3+": "Not all clients support this yet", - "gczcC5": "Subscribe", - "gjBiyj": "Loading...", + "fOksnD": "Kan niet stemmen omdat LNURL-service geen zaps ondersteunt", + "fWZYP5": "Vastgezet", + "filwqD": "Lezen", + "flnGvv": "Waar denk je aan?", + "fqwcJ1": "On-chain Donation", + "fsB/4p": "Opgeslagen", + "g5pX+a": "Over ons", + "g985Wp": "Kan code niet verzenden", + "gBdUXk": "Sla je sleutels op!", + "gDZkld": "Snort is een Nostr UI, Nostr is een gedecentraliseerd protocol voor opslaan en verspreiden van \"notes\".", + "gDzDRs": "Emoji die wordt verstuurd wanneer u reageert op een note", + "gXgY3+": "Nog niet alle Nostr Apps ondersteunen dit", + "gczcC5": "Abonneer", + "gjBiyj": "Laden...", "grQ+mI": "Proof of Work", "h8XMJL": "Badges", - "hK5ZDk": "the world", - "hMzcSq": "Messages", - "hWSp+B": "Nostr Connect (NIP-46)", - "hY4lzx": "Supports", - "hicxcO": "Show replies", - "hniz8Z": "here", + "hK5ZDk": "de wereld", + "hMzcSq": "Berichten", + "hY4lzx": "Ondersteuningen", + "hicxcO": "Reacties tonen", + "hmZ3Bz": "Media", + "hniz8Z": "hier", "i/dBAR": "Zap Pool", - "iCqGww": "Reactions ({n})", - "iDGAbc": "Get a Snort identifier", - "iEoXYx": "DeepL translations", - "iGT1eE": "Prevent fake accounts from imitating you", - "iNWbVV": "Handle", - "iUsU2x": "Mint: {url}", - "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", - "ieGrWo": "Follow", - "itPgxd": "Profile", - "izWS4J": "Unfollow", - "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", - "jCA7Cw": "Preview on snort", - "jMzO1S": "Internal error: {msg}", - "jfV8Wr": "Back", - "juhqvW": "Improve login security with browser extensions", - "jvo0vs": "Save", - "jzgQ2z": "{n} Reactions", - "k2veDA": "Write", - "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", - "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", - "kaaf1E": "now", - "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", - "l+ikU1": "Everything in {plan}", - "lBboHo": "If you want to try out some others, check out {link} for more!", - "lCILNz": "Buy Now", - "lD3+8a": "Pay", - "lPWASz": "Snort nostr address", - "lTbT3s": "Wallet password", + "iCqGww": "Reacties ({n})", + "iDGAbc": "Krijg een identifier", + "iEoXYx": "DeepL vertalingen", + "iGT1eE": "Voorkom dat nepaccounts je imiteren", + "iNWbVV": "Hendel", + "iXPL0Z": "Kan niet inloggen met een privésleutel in een onbeveiligde omgeving, gebruik in plaats daarvan een Nostr key manager extensie", + "ieGrWo": "Volgen", + "itPgxd": "Profiel", + "izWS4J": "Ontvolgen", + "jA3OE/": "{n,plural,one {}=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Voorvertoning op snort", + "jMzO1S": "Interne fout: {msg}", + "jfV8Wr": "Terug", + "juhqvW": "Verbeter inlogbeveiliging met browserextensies", + "jvo0vs": "Opslaan", + "jzgQ2z": "{n} Reacties", + "k2veDA": "Schrijven", + "k7sKNy": "Onze eigen NIP-05 verificatieservice ondersteunt de ontwikkeling van deze site en geeft u een speciale badge op onze site!", + "kJYo0u": "{n,plural,one {}=0{{name} gerepost} other{{name} & {n} anderen gerepost}}", + "kaaf1E": "nu", + "kuPHYE": "{n,plural,one {}=0{{name} geliked} other{{name} & {n} anderen geliked}}", + "l+ikU1": "Alles in {plan}", + "lBboHo": "Als je wat anderen wilt uitproberen, bekijk dan {link} voor meer!", + "lCILNz": "Koop nu", + "lD3+8a": "Betaal", + "lPWASz": "Koop Nostr adres", + "lTbT3s": "Wachtwoord Wallet", "lgg1KN": "accountoverzicht", - "ll3xBp": "Image proxy service", - "lnaT9F": "Following {n}", - "lsNFM1": "Click to load content from {link}", - "lvlPhZ": "Pay Invoice", - "mErPop": "It looks like you dont have any, check {link} to buy one!", - "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", - "mKAr6h": "Follow all", - "mKh2HS": "File upload service", - "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", - "mTJFgF": "Popular", - "mfe8RW": "Option: {n}", - "n1Whvj": "Switch", - "nDejmx": "Unblock", - "nGBrvw": "Bookmarks", - "nN9XTz": "Share your thoughts with {link}", - "nOaArs": "Setup Profile", - "nWQFic": "Renew", - "nn1qb3": "Your donations are greatly appreciated", - "nwZXeh": "{n} blocked", - "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", - "o7e+nJ": "{n} followers", - "oJ+JJN": "Nothing found :/", - "odFwjL": "Follows only", - "odhABf": "Login", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", - "osUr8O": "You can also use these extensions to login to most Nostr sites.", - "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", - "p4N05H": "Upload", - "p85Uwy": "Active Subscriptions", - "pI+77w": "Downloadable backups from Snort relay", - "puLNUJ": "Pin", - "pzTOmv": "Followers", - "qD9EUF": "Email <> DM bridge for your Snort nostr address", - "qDwvZ4": "Unknown error", - "qMx1sA": "Default Zap amount", - "qUJTsT": "Blocked", - "qdGuQo": "Your Private Key Is (do not share this with anyone)", - "qkvYUb": "Add to Profile", - "qmJ8kD": "Translation failed", - "qtWLmt": "Like", + "ll3xBp": "Proxyservice voor afbeeldingen", + "lnaT9F": "Volgend {n}", + "lsNFM1": "Klik om inhoud van {link} te laden", + "lvlPhZ": "Betaal invoice", + "mErPop": "Het lijkt erop dat je er geen hebt, check {link} om er een te kopen!", + "mH91FY": "Elke medewerker krijgt een percentage van alle donaties en NIP-05 orders, je kunt de splitsingen hieronder zien", + "mKAr6h": "Volg allen", + "mKh2HS": "File Upload service", + "mKhgP9": "{n,plural,one {}=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Populair", + "mfe8RW": "Optie: {n}", + "n1Whvj": "Wissel", + "nDejmx": "Deblokkeer", + "nGBrvw": "Bladwijzers", + "nN9XTz": "Deel je gedachten met {link}", + "nOaArs": "Profiel instellen", + "nWQFic": "Vernieuwen", + "nn1qb3": "Uw donaties worden zeer gewaardeerd", + "nwZXeh": "{n} geblokkeerd", + "o6Uy3d": "Alleen de privésleutel kan worden gebruikt om te posten, al het andere logt u in alleen-lezen modus.", + "o7e+nJ": "{n} volgers", + "oJ+JJN": "Niets gevonden :/", + "odFwjL": "Alleen volgend", + "odhABf": "Inloggen", + "ojzbwv": "Hey, het lijkt erop dat je nog geen Nostr Adres hebt. Overweeg om er een te nemen! Bekijk {link}", + "osUr8O": "U kunt deze extensie ook gebruiken om in te loggen op andere Nostr apps and websites.", + "oxCa4R": "Het krijgen van een Nostr adres identifier helpt mensen die u kennen te bevestigen dat dit account werkelijk van u is. Veel mensen kunnen een @jack gebruikersnaam hebben, maar er is slechts één jack@cash.app.", + "p4N05H": "Uploaden", + "p85Uwy": "Actieve Abonnementen", + "pI+77w": "Downloadbare back-ups van Snort relay", + "puLNUJ": "Vastmaken aan profielpagina", + "pzTOmv": "Volgers", + "qD9EUF": "E-mail <> DM bridge voor je Snort Nostr adres", + "qDwvZ4": "Onbekende fout", + "qMx1sA": "Standaard Zap-bedrag", + "qUJTsT": "Geblokkeerd", + "qdGuQo": "Uw privésleutel is (deel dit met niemand)", + "qfmMQh": "This note has been muted", + "qkvYUb": "Toevoegen aan Profiel", + "qmJ8kD": "Vertaling mislukt", + "qtWLmt": "Vind ik leuk", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", - "r5srDR": "Enter wallet password", - "rT14Ow": "Add Relays", - "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", - "rfuMjE": "(Default)", - "rmdsT4": "{n} days", - "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", - "rudscU": "Failed to load follows, please try again later", - "sWnYKw": "Snort is designed to have a similar experience to Twitter.", - "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", - "tOdNiY": "Dark", - "th5lxp": "Send note to a subset of your write relays", - "thnRpU": "Getting NIP-05 verified can help:", + "r5srDR": "Voer wallet wachtwoord in", + "rT14Ow": "Relays toevoegen", + "reJ6SM": "Het wordt aanbevolen om een van de volgende browserextensies te gebruiken als u op een computer bent om uw sleutel te beveiligen:", + "rfuMjE": "(Standaard)", + "rmdsT4": "{n} dagen", + "rrfdTe": "Dit is dezelfde technologie die door het Bitcoin netwerk wordt gebruikt en waarvan veiligheid is bewezen.", + "rudscU": "Fout bij het laden van de volgers, probeer het later opnieuw", + "sKDn4e": "Show Badges", + "sUNhQE": "user", + "sWnYKw": "Snort is ontworpen om een soortgelijke ervaring als Twitter te hebben.", + "sZQzjQ": "Failed to parse zap split: {input}", + "svOoEH": "Naam-squatting en imitatie is niet aangeraden. Snort en onze partners behouden zich het recht om uw Nostr adres identifier te beëindigen (lees: enkel de geldigheid van uw identifier, dus niet uw account - niemand kan deze wegnemen) voor het schenden van deze regel.", + "tOdNiY": "Donker thema", + "th5lxp": "Stuur notitie naar een subset van schrijfrelays", + "thnRpU": "NIP-05 geverifieerd worden kan helpen:", "ttxS0b": "Supporter Badge", - "u/vOPu": "Paid", - "u4bHcR": "Check out the code here: {link}", - "uD/N6c": "Zap {target} {n} sats", - "uSV4Ti": "Reposts need to be manually confirmed", - "usAvMr": "Edit Profile", - "ut+2Cd": "Get a partner identifier", + "u/vOPu": "Betaald", + "u4bHcR": "Bekijk de code hier: {link}", + "uSV4Ti": "Reposts moeten handmatig worden bevestigd", + "uc0din": "Send sats splits to", + "usAvMr": "Bewerk Profiel", + "ut+2Cd": "Krijg een identifier", "v8lolG": "Start chat", - "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", - "vU71Ez": "Paying with {wallet}", - "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", - "vhlWFg": "Poll Options", - "vlbWtt": "Get a free one", + "vOKedj": "{n,plural,one {}=1{& {n} anderen} other{& {n} anderen}}", + "vZ4quW": "NIP-05 is een DNS-gebaseerde identificatie die helpt u te valideren als een bepaalde gebruiker.", + "vhlWFg": "Poll Opties", + "vlbWtt": "Verkrijg er één gratis", "vrTOHJ": "{amount} sats", - "vxwnbh": "Amount of work to apply to all published events", - "wEQDC6": "Edit", - "wLtRCF": "Your key", + "vxwnbh": "Hoeveelheid werk van toepassing op alle gepubliceerde events", + "wEQDC6": "Bewerk", + "wLtRCF": "Uw sleutel", + "wSZR47": "Submit", "wWLwvh": "Anon", - "wYSD2L": "Nostr Adddress", - "wih7iJ": "name is blocked", - "wqyN/i": "Find out more info about {service} at {link}", - "wtLjP6": "Copy ID", - "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "This note has been marked as sensitive, click here to reveal", - "x82IOl": "Mute", - "xIoGG9": "Go to", - "xJ9n2N": "Your public key", - "xKflGN": "{username}''s Follows on Nostr", - "xQtL3v": "Unlock", - "xaj9Ba": "Provider", - "xbVgIm": "Automatically load media", - "xhQMeQ": "Expires", - "xmcVZ0": "Search", - "y1Z3or": "Language", - "yCLnBC": "LNURL or Lightning Address", - "yCmnnm": "Read global from", + "wYSD2L": "Nostr Adres", + "wih7iJ": "naam is geblokkeerd", + "wofVHy": "Moderation", + "wqyN/i": "Meer informatie over {service} bij {link}", + "wtLjP6": "Kopieer ID", + "x/Fx2P": "Ondersteun diensten die je gebruikt door een deel van je zaps in een pot geld te splitsen!", + "x82IOl": "Dempen", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Ga naar", + "xJ9n2N": "Uw publieke sleutel", + "xKflGN": "{username}'s Volglijst op Nostr", + "xQtL3v": "Ontgrendel", + "xaj9Ba": "Aanbieder", + "xbVgIm": "Automatisch video's laden", + "xhQMeQ": "Verloopt op", + "xmcVZ0": "Zoeken", + "y1Z3or": "Taal", + "yCLnBC": "LNURL of Lightning Address", + "yCmnnm": "Lees Global van", + "zCb8fX": "Weight", "zFegDD": "Contact", - "zINlao": "Owner", - "zQvVDJ": "All", - "zcaOTs": "Zap amount in sats", - "zjJZBd": "You're ready!", - "zonsdq": "Failed to load LNURL service", - "zvCDao": "Automatically show latest notes" + "zINlao": "Eigenaar", + "zQvVDJ": "Alle", + "zcaOTs": "Zap hoeveelheid in sats", + "zjJZBd": "Je bent er klaar voor!", + "zonsdq": "Laden van LNURL service mislukt", + "zvCDao": "Recente notities automatisch weergeven", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/no_NO.json b/packages/app/src/translations/no_NO.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/no_NO.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/pa_IN.json b/packages/app/src/translations/pa_IN.json index 6850017d..e1b96dd4 100644 --- a/packages/app/src/translations/pa_IN.json +++ b/packages/app/src/translations/pa_IN.json @@ -50,6 +50,7 @@ "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", "4Z3t5i": "Use imgproxy to compress images", "4rYCjn": "Note to Self", + "5BVs2e": "zap", "5JcXdV": "Create Account", "5oTnfy": "Buy Handle", "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", @@ -392,6 +393,7 @@ "rmdsT4": "{n} days", "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", "sWnYKw": "Snort is designed to have a similar experience to Twitter.", "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", "tOdNiY": "Dark", @@ -422,6 +424,7 @@ "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", "x/q8d5": "This note has been marked as sensitive, click here to reveal", "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", "xIoGG9": "Go to", "xJ9n2N": "Your public key", "xKflGN": "{username}''s Follows on Nostr", diff --git a/packages/app/src/translations/pl_PL.json b/packages/app/src/translations/pl_PL.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/pl_PL.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/pt_BR.json b/packages/app/src/translations/pt_BR.json index 2f14e3d5..efc6ab26 100644 --- a/packages/app/src/translations/pt_BR.json +++ b/packages/app/src/translations/pt_BR.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Pagar agora", "+Vxixo": "Conversa em grupo secreta", "+aZY2h": "Tipo de Zap", + "+tShPg": "following", "+vA//S": "Logins", "+vIQlC": "Por favor, salve sua senha em um lugar seguro para poder gerenciar seu perfil no futuro", "+vVZ/G": "Conectar", @@ -11,6 +12,7 @@ "/JE/X+": "Suporte da conta", "/PCavi": "Público", "/RD0e2": "Nostr usa tecnologia de assinatura digital para prover notas à prova de adulteração que podem ser repostadas em diferentes relays para que haja armazenamento redundante de seu conteúdo.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Faça seu perfil seja mais fácil de achar e compartilhar", "/n5KSF": "{n} ms", "00LcfG": "Carregar mais", @@ -19,6 +21,7 @@ "0BUTMv": "Pesquisar...", "0jOEtS": "LNURL inválida", "0mch2Y": "o nome possui caracteres não permitidos", + "0uoY11": "Show Status", "0yO7wF": "{n} segundos", "1A7TZk": "O que é e como funciona o Snort?", "1Mo59U": "Tem certeza que deseja remover esta nota dos favoritos?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Favoritos", "2k0Cv+": "Descurtidas ({n})", "2ukA4d": "{n} horas", + "3KNMbJ": "Articles", "3Rx6Qo": "Avançado", "3cc4Ct": "Claro", "3gOsZq": "Tradutores", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Seguidores", "3xCwbZ": "Ou", "3yk8fB": "Carteira", + "40VR6s": "Nostr Connect", "450Fty": "Nenhum", "47FYwb": "Cancelar", "4IPzdn": "Desenvolvedores principais", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs é um dos primeiros provedores NIP-05 e oferece uma boa coleção de domínios a preços razoáveis", "4Z3t5i": "Usar imgproxy para comprimir imagens", "4rYCjn": "Nota pessoal", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Criar conta", "5oTnfy": "Comprar Identificador", "5rOdPG": "Após configurar sua extensão para gerenciamento de chaves e gerar uma chave, você poderá acompanhar o nosso novo fluxo de usuários para configurar seu perfil e encontrar algumas pessoas interessantes no Nostr para seguir.", "5u6iEc": "Transferir para Pubkey", "5vMmmR": "Nomes de usuário não são únicos no Nostr. O endereço do nostr é seu único endereço legível para um humano que é exclusivo para você no momento do registro.", "5ykRmX": "Enviar Zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Falha ao fazer proxy da imagem de {host}, clique aqui para carregar diretamente", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Obtenha um identificador", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Curtidas ({n})", "6uMqL1": "Não pago", "7+Domh": "Notas", "7BX/yC": "Alternador de conta", "7hp70g": "NIP-05", - "7xzTiH": "{action} para {target}", "8/vBbP": "Repostagens ({n})", "89q5wc": "Confirmar Republicações", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Enviar Zap de {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "nome longo demais", "8v1NN+": "Frase de pareamento", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Próximo", "9HU8vw": "Responder", "9SvQep": "Seguindo {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Fatura da Lightning", "ADmfQT": "Antecessor", "AGNz71": "Enviar Zap de {n} sats para todos", + "AN0Z7Q": "Muted Words", "ASRK0S": "Este autor foi silenciado", "Adk34V": "Configure seu perfil", "Ai8VHU": "Retenção de notas ilimitada no relé Snort", @@ -92,6 +107,7 @@ "BOr9z/": "Snort é um projeto de código aberto construído por pessoas apaixonadas em seu tempo livre", "BWpuKl": "Atualizar", "BcGMo+": "Notas possuem conteúdo de texto, o uso mais popular dessas notas é armazenar mensagens(similar à um Tweet).", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Sair", "C8HhVE": "Usuários sugeridos", @@ -106,20 +122,23 @@ "DZzCem": "Mostrar últimas {n} notas", "DcL8P+": "Colaborador", "Dh3hbq": "Zap automático", + "Dn82AL": "Live", "DtYelJ": "Transferir", "E8a4yq": "Siga algumas contas populares", "ELbg9p": "Provedores de dados", "EPYwm7": "A sua chave privada é a sua senha. Se você perder essa chave, perderá o acesso à sua conta! Copie-a e mantenha-a em um lugar seguro. Não há como redefinir sua chave privada.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "Traduzir para {lang}", "EcZF24": "Relays customizados", "EcglP9": "Chave", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Comprar", "Eqjl5K": "Somente o Snort e o nosso identificador de parceiro de integração lhe dá um nome de domínio colorido, mas você é bem-vindo para usar outros serviços também.", "F+B3x1": "Também fizemos parcerias com o nostrplebs.com para oferecer mais opções", "F3l7xL": "Adicionar conta", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL para onde os zaps devem ser enviados", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Pronto!", "FSYL8G": "Usuários em alta", "FdhSU2": "Solicitar agora", @@ -129,28 +148,32 @@ "G1BGCg": "Selecionar carteira", "GFOoEE": "Salt", "GL8aXW": "Favoritos ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Endereço Lightning", "GUlSVG": "Reivindicar seu endereço do Snort no Nostr", "Gcn9NQ": "Link Magnet", "GspYR7": "{n} não curtiram", + "Gxcr08": "Broadcast Event", "H+vHiz": "Chave Hex..", "H0JBH6": "Sair", "H6/kLh": "Pedido pago!", "HAlOn1": "Nome", - "HF4YnO": "Assista ao vivo!", "HFls6j": "nome vai estar disponível mais tarde", "HOzFdo": "Silenciado", "HWbkEK": "Apagar cache e recarregar", "HbefNb": "Abrir Carteira", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Obrigado por usar o Snort, por favor considere fazer uma doação se puder.", "IEwZvs": "Tem certeza de que deseja desafixar essa nota?", "IKKHqV": "Seguindo", "INSqIz": "Nome de usuário do Twitter...", "IUZC+0": "Isto significa que ninguém pode modificar notas que você criou e todos podem facilmente verificar se as notas que estão a ler são criadas por você.", "Ig9/a1": "Enviou {n} sats para {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Notas em alta", "J+dIsA": "Subscrições", "JCIgkj": "Nome de usuário", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "Nenhum endereço lightning", "JeoS4y": "Repostar", @@ -160,16 +183,19 @@ "K3r6DQ": "Deletar", "K7AkdL": "Mostrar", "KAhAcM": "Insira a configuração LNDHub", - "KLo3SP": "Motivo: {reason}", "KQvWvD": "Excluído", "KWuDfz": "Eu salvei as minhas chaves, continue", "KahimY": "Tipo de evento desconhecido: {kind}", "KoFlZg": "Insira URL mint", + "KtsyO0": "Enter Pin", "LF5kYT": "Outras conexões", + "LR1XjT": "Pin too short", "LXxsbk": "Anônimo", "LgbKvU": "Comentar", "Lu5/Bj": "Abrir no Zapstr", "Lw+I+J": "{n,plural,=0{{name} enviou um Zap} other{{name} e {n} outro(s) enviaram Zaps}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Menu de depuração", "MBAYRO": "Mostra \"Copiar ID\" e \"Copiar JSON de eventos\" no menu de contexto em cada mensagem", "MI2jkA": "Não disponível:", @@ -180,6 +206,7 @@ "MuVeKe": "Comprar endereço Nostr", "MzRYWH": "Comprando {item}", "N2IrpM": "Confirmar", + "NAidKb": "Notifications", "NAuFNH": "Você já tem uma assinatura deste tipo, por favor, renove ou pague", "NNSu3d": "Importar quem você segue no Twitter", "NdOYJJ": "Hmm não há nada aqui... Verifique {newUsersPage} para seguir alguns usuários recomendados!", @@ -192,7 +219,6 @@ "OLEm6z": "Erro de login desconhecido", "OQXnew": "Sua assinatura ainda está ativa, você não pode renovar ainda", "ORGv1Q": "Criado", - "P04gQm": "Todos os zaps enviados para esta nota serão recebidos pela seguinte LNURL", "P61BTu": "Copiar JSON do Evento", "P7FD0F": "Sistema (Padrão)", "P7nJT9": "Total hoje (UTC): {amount} sats", @@ -207,7 +233,6 @@ "QxCuTo": "Arte por {name}", "Qxv0B2": "Você tem atualmente {number} sats no seu grupo de zap.", "R/6nsx": "Assinatura", - "R1fEdZ": "Encaminhar Zaps", "R81upa": "Pessoas que você segue", "RDZVQL": "Verificar", "RahCRH": "Expirado", @@ -217,19 +242,19 @@ "RoOyAh": "Relés", "Rs4kCE": "Favoritar", "RwFaYs": "Ordenar", + "SMO+on": "Send zap to {name}", "SOqbe9": "Atualizar endereço Lightning", "SP0+yi": "Comprar assinatura", - "SX58hM": "Copiar", "SYQtZ7": "Proxy de endereço LN", "ShdEie": "Marcar todos como lidos", "Sjo1P4": "Customizar", "Ss0sWu": "Pagar agora", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "A mídia nas notas será mostrada automaticamente para os usuários selecionados, caso contrário apenas o link será mostrado", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "Pessoas", "UDYlxu": "Assinaturas pendentes", - "ULotH9": "Quantidade: {amount} sats", "UT7Nkj": "Novo chat", "UUPFlt": "Os usuários devem aceitar o aviso de conteúdo para mostrar o conteúdo da sua nota.", "Up5U7K": "Bloquear", @@ -239,15 +264,16 @@ "VR5eHw": "Chave pública (npub/nprofile)", "VlJkSk": "{n} silenciado(s)", "VnXp8Z": "Avatar", - "VtPV/B": "Login com extensão (NIP-07)", "VvaJst": "Visualizar carteiras", "Vx7Zm2": "Como as chaves funcionam?", "W1yoZY": "Parece que você não tem nenhuma assinatura, você pode obter uma aqui {link}", "W2PiAr": "{n} bloqueado", "W9355R": "Desmutar", "WONP5O": "Encontre no Nostr os perfis que você segue no Twitter (Dados fornecidos por {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "ex: Jack", "X7xU8J": "nseg, npub, nip-05, hex, mnemônico", + "XECMfW": "Send usage metrics", "XICsE8": "Servidores de arquivos", "XgWvGA": "Reações", "Xopqkl": "Sua quantidade padrão de zap é de {number} sats, valores de exemplo são calculados a partir disso.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Inserir frase de pareamento", "ZKORll": "Ativar agora", "ZLmyG9": "Colaboradores", - "ZUZedV": "Doação Lightning:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Configurar perfil", "a5UPxh": "Financie desenvolvedores e plataformas que fornecem serviços de verificação NIP-05", "a7TDNm": "As notas serão transmitidas em tempo real na guia global e de notas", "aWpBzj": "Mostrar mais", "b12Goz": "Mnemônica", "b5vAk0": "Seu identificador atuará como um endereço lightning e redirecionará para a LNURL escolhida ou endereço Lightning", + "bLZL5a": "Get Address", "bQdA2k": "Conteúdo sensível", "bep9C3": "Chave pública", "bfvyfs": "Anônimo", "brAXSu": "Escolha um nome de usuário", "bxv59V": "Agora há pouco", "c+oiJe": "Instalar extensão", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Se você tiver uma pergunta sobre o seu pedido NIP-05, por favor envie uma mensagem privada {link}", "c3g2hL": "Transmitir novamente", "cFbU1B": "Usando o Alby? Vá para {link} para obter a sua configuração de NWC!", @@ -287,6 +315,7 @@ "d7d0/x": "Endereço LN", "dOQCL8": "Exibir nome", "e61Jf3": "Em breve", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Marcar todas como lidas", "eHAneD": "Emojis de reação", "eJj8HD": "Obter verificação", @@ -297,6 +326,7 @@ "fWZYP5": "Fixado", "filwqD": "Lida", "flnGvv": "No que você está pensando?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Salvo", "g5pX+a": "Sobre", "g985Wp": "Falha ao enviar o voto", @@ -310,9 +340,9 @@ "h8XMJL": "Emblemas", "hK5ZDk": "o mundo", "hMzcSq": "Mensagens", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Suporta", "hicxcO": "Exibir respostas", + "hmZ3Bz": "Media", "hniz8Z": "aqui", "i/dBAR": "Zap Pool", "iCqGww": "Reações ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "Traduções DeepL", "iGT1eE": "Impedir que contas falsas imitem você", "iNWbVV": "Identificador", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Não é possível fazer login com a chave privada em uma conexão insegura, por favor, use uma extensão para gerenciar chaves do Nostr", "ieGrWo": "Seguir", "itPgxd": "Perfil", @@ -381,9 +410,11 @@ "qMx1sA": "Valor padrão de Zap", "qUJTsT": "Bloqueado", "qdGuQo": "Sua chave privada é (não compartilhe isso com ninguém)", + "qfmMQh": "This note has been muted", "qkvYUb": "Adicionar ao perfil", "qmJ8kD": "Falha na tradução", "qtWLmt": "Curtir", + "qz9fty": "Incorrect pin", "r3C4x/": "Software", "r5srDR": "Digite a senha da carteira", "rT14Ow": "Adicionar Relés", @@ -392,7 +423,10 @@ "rmdsT4": "{n} dia(s)", "rrfdTe": "Essa é a mesma tecnologia que é usada pelo Bitcoin e provou ser extremamente segura.", "rudscU": "Falha ao carregar, por favor, tente novamente mais tarde", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "O Snort foi projetado para ter uma experiência parecida com o Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Práticas como Name-Squatting e assunção de identidade alheia não são permitidas. O Snort e nossos parceiros reservam-se o direito de encerrar seu identificador por violar tais regras.", "tOdNiY": "Noturno", "th5lxp": "Enviar nota a um subconjunto dos seus relés de escrita", @@ -400,13 +434,12 @@ "ttxS0b": "Emblema de apoiador", "u/vOPu": "Pago", "u4bHcR": "Veja o código aqui: {link}", - "uD/N6c": "Enviar {n} sats para {target}", "uSV4Ti": "Repostagens precisam ser confirmadas manualmente", + "uc0din": "Send sats splits to", "usAvMr": "Editar perfil", "ut+2Cd": "Obter um identificador de parceiro", "v8lolG": "Iniciar chat", "vOKedj": "{n,plural,=1{& {n} outro} other{& {n} outros}}", - "vU71Ez": "Pagando com {wallet}", "vZ4quW": "NIP-05 é uma especificação de verificação baseada em DNS que ajuda a validar você como um usuário real.", "vhlWFg": "Poll Options", "vlbWtt": "Obtenha um gratuitamente", @@ -414,14 +447,16 @@ "vxwnbh": "Quantidade de trabalho para aplicar a todos os eventos publicados", "wEQDC6": "Editar", "wLtRCF": "Sua chave", + "wSZR47": "Submit", "wWLwvh": "Anônimo", "wYSD2L": "Endereço do Nostr", "wih7iJ": "o nome está bloqueado", + "wofVHy": "Moderation", "wqyN/i": "Descubra mais informações sobre {service} em {link}", "wtLjP6": "Copiar ID", "x/Fx2P": "Financie os serviços que você utiliza dividindo uma parte de todos os seus zaps em uma reserva de fundos!", - "x/q8d5": "Essa nota foi marcada como sensível, clique aqui para vê-la", "x82IOl": "Silenciar", + "xIcAOU": "Votes by {type}", "xIoGG9": "Ir para", "xJ9n2N": "Sua chave pública", "xKflGN": "{username} segue no Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Idioma", "yCLnBC": "LNURL ou endereço Lightning", "yCmnnm": "Ler global de", + "zCb8fX": "Weight", "zFegDD": "Contato", "zINlao": "Dono", "zQvVDJ": "Todos", "zcaOTs": "Quantidade de Zap em sats", "zjJZBd": "Você está pronto!", "zonsdq": "Falha ao carregar o serviço LNURL", - "zvCDao": "Mostrar automaticamente as últimas notas" + "zvCDao": "Mostrar automaticamente as últimas notas", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/pt_PT.json b/packages/app/src/translations/pt_PT.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/pt_PT.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/ro_RO.json b/packages/app/src/translations/ro_RO.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/ro_RO.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/ru_RU.json b/packages/app/src/translations/ru_RU.json index 6a4cadad..6c8363c0 100644 --- a/packages/app/src/translations/ru_RU.json +++ b/packages/app/src/translations/ru_RU.json @@ -1,24 +1,27 @@ { "+D82kt": "Вы уверены, что хотите сделать репост: {id}", - "+PzQ9Y": "Payout Now", - "+Vxixo": "Secret Group Chat", + "+PzQ9Y": "Выплатить сейчас", + "+Vxixo": "Секретный групповой чат", "+aZY2h": "Тип запа", + "+tShPg": "подписки", "+vA//S": "Логины", "+vIQlC": "Пожалуйста, обязательно сохраните следующий пароль, чтобы в будущем управлять своим хэндлом", "+vVZ/G": "Подключиться", - "+xliwN": "{name} reposted", + "+xliwN": "{name} сделал репост", "/4tOwT": "Пропустить", "/JE/X+": "Поддержка аккаунта", "/PCavi": "Публичный", "/RD0e2": "Nostr использует технологию цифровой подписи для создания защищенных от несанкционированного доступа заметок, которые можно безопасно копировать на множество релеев, обеспечивая резервное хранение Вашего контента.", + "/Xf4UW": "Отправлять показатели анонимного использования", "/d6vEc": "Упростить поиск и распространение вашего профиля в Nostr", "/n5KSF": "{n} мс", - "00LcfG": "Load more", + "00LcfG": "Загрузить больше", "08zn6O": "Экспортировать ключи", "0Azlrb": "Управление", "0BUTMv": "Поиск...", "0jOEtS": "Неверный LNURL", "0mch2Y": "имя содержит запрещенные символы", + "0uoY11": "Показать статус", "0yO7wF": "{n} секунд", "1A7TZk": "Что такое Snort и как он работает?", "1Mo59U": "Вы уверены, что хотите удалить эту заметку из закладок?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Закладок", "2k0Cv+": "Дизлайки ({n})", "2ukA4d": "{n} часов", + "3KNMbJ": "Статьи", "3Rx6Qo": "Продвинутые", "3cc4Ct": "Светлый", "3gOsZq": "Переводчики", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Подписчиков", "3xCwbZ": "ИЛИ", "3yk8fB": "Кошелек", + "40VR6s": "Подключение Nostr", "450Fty": "Ничьи", "47FYwb": "Отменить", "4IPzdn": "Основные разработчики", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs является одним из первых сервисов NIP-05 и предлагает хорошую коллекцию доменов по приемлемым ценам", "4Z3t5i": "Использовать imgproxy для сжатия изображений", "4rYCjn": "Заметка самому себе", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Создать аккаунт", "5oTnfy": "Приобрести хэндл", "5rOdPG": "После того как Вы настроили расширение для управления ключами и сгенерировали ключ, Вы можете последовать нашему гиду для новичков, чтобы настроить свой профиль и найти интересных людей, заслуживающих подписки на Nostr.", "5u6iEc": "Перевести на Pubkey", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "Имя пользователя не является уникальным на Nostr. Nostr адрес - это ваш уникальный человеко читаемый адрес, который уникален для вас при регистрации.", "5ykRmX": "Отправить зап", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Смотреть повтор", "65BmHb": "Не удалось загрузить изображение через {host}, нажмите здесь, чтобы загрузить напрямую", + "6OSOXl": "Причина: {reason}", "6Yfvvp": "Получите адрес", + "6bgpn+": "Не все клиенты поддерживают это, вы все равно можете получить некоторые запы, как если бы запределение не было настроено", "6ewQqw": "Лайки ({n})", "6uMqL1": "Не оплачено", "7+Domh": "Заметки", "7BX/yC": "Переключение аккаунтов", "7hp70g": "NIP-05", - "7xzTiH": "{action} в {target}", "8/vBbP": "Репосты ({n})", "89q5wc": "Подтверждать репосты", + "8Kboo2": "Отсканируйте этот QR код с вашим приложением для регистрации, чтобы начать", "8QDesP": "Запнуть {n} сат", + "8Rkoyb": "Получатель", + "8Y6bZQ": "Неверный zap сплит: {input}", "8g2vyB": "имя слишком длинное", "8v1NN+": "Фраза для сопряжения", + "8xNnhi": "Расширение Nostr", "9+Ddtu": "Далее", "9HU8vw": "Ответить", "9SvQep": "Подписан(а) на {n}", @@ -77,7 +91,8 @@ "9pMqYs": "Nostr адрес", "9wO4wJ": "Лайтнинг-инвойс", "ADmfQT": "Ветка", - "AGNz71": "Zap All {n} sats", + "AGNz71": "Зап Все {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "Этот автор был заглушен", "Adk34V": "Настройте свой профиль", "Ai8VHU": "Снятие ограничения срока хранения ваших заметок на релее Snort", @@ -92,9 +107,10 @@ "BOr9z/": "Snort - это проект с открытым исходным кодом, созданный энтузиастами", "BWpuKl": "Обновить", "BcGMo+": "Заметки содержат текст; наиболее популярное применение этих заметок - передача сообщений \"подобно твиттеру\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premium", "C81/uG": "Выход", - "C8HhVE": "Suggested Follows", + "C8HhVE": "Предложенные подписки", "CHTbO3": "Не удалось загрузить инвойс", "CVWeJ6": "Популярные профили", "CmZ9ls": "{n} заглушен", @@ -106,22 +122,25 @@ "DZzCem": "Показать последние {n} заметки", "DcL8P+": "Саппортер", "Dh3hbq": "Авто Зап", + "Dn82AL": "Прямой эфир", "DtYelJ": "Перевод", "E8a4yq": "Подпишитесь на популярные аккаунты", - "ELbg9p": "Data Providers", + "ELbg9p": "Данные провайдера", "EPYwm7": "Ваш приватный ключ - это Ваш пароль. Если Вы потеряете этот ключ, вы потеряете доступ к своей учетной записи! Скопируйте и сохраните его в надежном месте. Восстановить приватный ключ невозможно.", + "EQKRE4": "Показывать значки на страницах профиля", "EWyQH5": "Глобальная лента", "Ebl/B2": "Перевести на {lang}", "EcZF24": "Кастомные релеи", "EcglP9": "Ключ", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Купить", "Eqjl5K": "Snort предлагает запоминающиеся адреса, но Вы вольны воспользоваться и другими сервисами.", "F+B3x1": "Мы также сотрудничаем с nostrplebs.com, чтобы предоставить Вам больше возможностей", "F3l7xL": "Добавить аккаунт", "FDguSC": "{n} Запов", - "FP+D3H": "LNURL для получения запов", + "FMfjrl": "Показывать статусные сообщения на страницах профиля", "FS3b54": "Готово!", - "FSYL8G": "Trending Users", + "FSYL8G": "Трендовые пользователи", "FdhSU2": "Получить сейчас", "FfYsOb": "Произошла ошибка!", "FmXUJg": "подписан(а) на Вас", @@ -129,28 +148,32 @@ "G1BGCg": "Выберите кошелек", "GFOoEE": "Соль", "GL8aXW": "Закладки ({n})", - "GSye7T": "Lightning Address", + "GQPtfk": "Присоединиться к стриму", + "GSye7T": "Lightning Адрес", "GUlSVG": "Получите Nostr адрес от Snort", "Gcn9NQ": "Magnet-ссылка", "GspYR7": "{n} Дизлайк", + "Gxcr08": "Трансляция события", "H+vHiz": "Hex ключа..", "H0JBH6": "Выйти", "H6/kLh": "Заказ оплачен!", "HAlOn1": "Имя", - "HF4YnO": "Watch Live!", "HFls6j": "имя будет доступно позже", "HOzFdo": "Заглушен", - "HWbkEK": "Clear cache and reload", + "HWbkEK": "Очистить кэш и перезагрузить", "HbefNb": "Открыть кошелек", + "HhcAVH": "Вы не подписаны на этого человека, нажмите здесь, чтобы загрузить медиа из {link}, или обновите ваши предпочтения чтобы всегда загружать медиа от всех.", "IDjHJ6": "Спасибо, что выбрали Snort, пожалуйста, поддержите проект, если это возможно.", "IEwZvs": "Вы уверены, что хотите открепить эту заметку?", - "IKKHqV": "Follows", + "IKKHqV": "Подписки", "INSqIz": "Имя пользователя в Twitter...", "IUZC+0": "Это означает, что никто не может изменить созданные Вами заметки, и каждый может легко убедиться, что увиденные ими заметки созданы Вами.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Популярные заметки", "J+dIsA": "Подписки", "JCIgkj": "Имя пользователя", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Запы ({n})", "JPFYIM": "No lightning address", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "Удалить", "K7AkdL": "Показать", "KAhAcM": "Введите конфигурацию LNDHub", - "KLo3SP": "Причина: {reason}", "KQvWvD": "Удалено", "KWuDfz": "Я сохранил свои ключи; продолжить", "KahimY": "Неизвестный тип события: {kind}", "KoFlZg": "Введите URL монетного двора", + "KtsyO0": "Enter Pin", "LF5kYT": "Другие подключения", + "LR1XjT": "Pin too short", "LXxsbk": "Аноним", "LgbKvU": "Комментарий", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Меню отладки", "MBAYRO": "Показывает \"Копировать ID\" и \"Копировать JSON\" в контекстном меню каждой заметки", "MI2jkA": "Недоступно:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "Покупка {item}", "N2IrpM": "Подтвердить", + "NAidKb": "Notifications", "NAuFNH": "У вас уже есть подписка этого типа, пожалуйста, обновите или оплатите ее.", "NNSu3d": "Импортировать подписки из Твиттера", "NdOYJJ": "Хм, здесь ничего нет. Перейди на {newUsersPage}, чтобы подписаться на рекомендуемых пользователей!", @@ -192,7 +219,6 @@ "OLEm6z": "Неизвестная ошибка при логине", "OQXnew": "Ваша подписка все еще активна, продлить ее пока что невозможно", "ORGv1Q": "Создано", - "P04gQm": "Все запы этой заметки будут отправлены на следующий LNURL", "P61BTu": "Копировать JSON события", "P7FD0F": "Системный (по умолчанию)", "P7nJT9": "Всего сегодня (UTC): {amount} сат", @@ -207,7 +233,6 @@ "QxCuTo": "Произведение {name}", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "Подписка", - "R1fEdZ": "Перенаправить Запы", "R81upa": "People you follow", "RDZVQL": "Проверить", "RahCRH": "Срок действия истёк", @@ -217,19 +242,19 @@ "RoOyAh": "Релеи", "Rs4kCE": "Закладка", "RwFaYs": "Сортировать", + "SMO+on": "Send zap to {name}", "SOqbe9": "Обновить лайтнинг-адрес", "SP0+yi": "Приобрести подписку", - "SX58hM": "Копировать", "SYQtZ7": "Прокси лайтнинг-адреса", "ShdEie": "Mark all read", "Sjo1P4": "Настраиваемые", "Ss0sWu": "Оплатить сейчас", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Токен Cashu", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "Пользователи", "UDYlxu": "Подписки в ожидании", - "ULotH9": "Сумма: {amount} сат", "UT7Nkj": "New Chat", "UUPFlt": "Пользователи должны принять предупреждение, чтобы увидеть содержание вашей заметки.", "Up5U7K": "Блок", @@ -239,15 +264,16 @@ "VR5eHw": "Публичный ключ (npub/nprofile)", "VlJkSk": "{n} заглушено", "VnXp8Z": "Аватар", - "VtPV/B": "Войти с помощью расширения (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "Как работают ключи?", "W1yoZY": "Похоже, у вас нет подписок, вы можете оформить подписку {link}", "W2PiAr": "{n} Заблокировано", "W9355R": "Разблокировать", "WONP5O": "Найдите тех, на кого вы подписаны в Twitter на Nostr (данные предоставлены {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "напр. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "Реакции", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Введи фразу для сопряжения", "ZKORll": "Активировать", "ZLmyG9": "Сделали вклад", - "ZUZedV": "Поддержка через Лайтнинг:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Настроить профиль", "a5UPxh": "Поддержать разработчиков и платформы, предоставляющие услуги NIP-05", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "Развернуть", "b12Goz": "Мнемоническая фраза", "b5vAk0": "Ваш хэндл будет выступать в качестве лайтнинг-адреса и перенаправлять запы на выбранный вами LNURL или LN адрес", + "bLZL5a": "Get Address", "bQdA2k": "Контент 18+", "bep9C3": "Публичный ключ", "bfvyfs": "Anon", "brAXSu": "Выберите имя пользователя", "bxv59V": "Только что", "c+oiJe": "Установить расширение", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Если у Вас есть вопросы по поводу NIP-05, отправьте сообщение {link}", "c3g2hL": "Транслировать снова", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "Лайтнинг-адрес", "dOQCL8": "Отображаемое имя", "e61Jf3": "В разработке", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Отметить все как прочитанные", "eHAneD": "Эмодзи реакции", "eJj8HD": "Получить верификацию", @@ -297,6 +326,7 @@ "fWZYP5": "Закреплённые", "filwqD": "Прочитано", "flnGvv": "Поделитесь мнением", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Сохранено", "g5pX+a": "О себе", "g985Wp": "Не удалось проголосовать", @@ -310,9 +340,9 @@ "h8XMJL": "Бейджи", "hK5ZDk": "мир", "hMzcSq": "Сообщения", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Поддержка", "hicxcO": "Показать ответы", + "hmZ3Bz": "Media", "hniz8Z": "здесь", "i/dBAR": "Zap Pool", "iCqGww": "Реакции ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "Переводы от DeepL", "iGT1eE": "Бороться с имитирующими Вас поддельными аккаунтами", "iNWbVV": "Хэндл", - "iUsU2x": "Минт: {url}", "iXPL0Z": "Не удается войти с помощью приватного ключа - ваше соединение небезопасно, пожалуйста, используйте расширение-менеджер ключей", "ieGrWo": "Подписаться", "itPgxd": "Профиль", @@ -381,9 +410,11 @@ "qMx1sA": "Размер Запа по умолчанию", "qUJTsT": "Заблокировано", "qdGuQo": "Ваш приватный ключ (не делитесь им ни с кем)", + "qfmMQh": "This note has been muted", "qkvYUb": "Добавить в профиль", "qmJ8kD": "Перевод не удался", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "Программное обеспечение", "r5srDR": "Введите пароль кошелька", "rT14Ow": "Добавить реле", @@ -392,7 +423,10 @@ "rmdsT4": "{n} дней", "rrfdTe": "Это та же технология, которая используется Биткоином и доказала свою исключительную безопасность.", "rudscU": "Не удалось загрузить подписки, повторите попытку позже", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort разработан таким образом, чтобы работать аналогично Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Присвоение имен и выдача себя за других пользователей не допускается. Snort и наши партнеры оставляют за собой право удалить Вашу учетную запись (не аккаунт - его никто не может отнять) за нарушение этого правила.", "tOdNiY": "Тёмная", "th5lxp": "Отправить заметку на подмножество Ваших пишущих релеев", @@ -400,13 +434,12 @@ "ttxS0b": "Бейдж Саппортера", "u/vOPu": "Оплачено", "u4bHcR": "Проверить код можно здесь: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "Репосты должны быть подтверждены вручную", + "uc0din": "Send sats splits to", "usAvMr": "Редактировать профиль", "ut+2Cd": "Получить адрес от нашего партнера", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} другой} other{& {n} других}}", - "vU71Ez": "Оплата через {wallet}", "vZ4quW": "NIP-05 - это спецификация проверки на основе DNS, которая помогает подтвердить тебя как реального пользователя.", "vhlWFg": "Настройки опроса", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "Редактировать", "wLtRCF": "Ваш ключ", + "wSZR47": "Submit", "wWLwvh": "Аноним", "wYSD2L": "Nostr-адрес", "wih7iJ": "имя заблокировано", + "wofVHy": "Moderation", "wqyN/i": "Узнай больше о {service} в {link}", "wtLjP6": "Скопировать ID", "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", - "x/q8d5": "Эта заметка была помечена как деликатная, нажмите здесь, чтобы раскрыть ее", "x82IOl": "Заглушить", + "xIcAOU": "Votes by {type}", "xIoGG9": "Перейти", "xJ9n2N": "Ваш публичный ключ", "xKflGN": "Подписки {username} на Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Язык", "yCLnBC": "LNURL или Лайтнинг-адрес", "yCmnnm": "Читать глобальную ленту с", + "zCb8fX": "Weight", "zFegDD": "Связь", "zINlao": "Владелец", "zQvVDJ": "Все", "zcaOTs": "Сумма Запа в сатах", "zjJZBd": "Готово!", "zonsdq": "Не удалось загрузить службу LNURL", - "zvCDao": "Автоматически показывать новые заметки" + "zvCDao": "Автоматически показывать новые заметки", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/sr_SP.json b/packages/app/src/translations/sr_SP.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/sr_SP.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/sv_SE.json b/packages/app/src/translations/sv_SE.json index 03828217..33f5677b 100644 --- a/packages/app/src/translations/sv_SE.json +++ b/packages/app/src/translations/sv_SE.json @@ -1,24 +1,27 @@ { "+D82kt": "Är du säker på att du vill dela vidare:{id}", "+PzQ9Y": "Utbetalning nu", - "+Vxixo": "Secret Group Chat", + "+Vxixo": "Hemlig grupp chatt", "+aZY2h": "Zap Typ", + "+tShPg": "följer", "+vA//S": "Inloggningar", "+vIQlC": "Se till att spara följande lösenord för att hantera din profil i framtiden", "+vVZ/G": "Anslut", - "+xliwN": "{name} reposted", + "+xliwN": "{name} delade", "/4tOwT": "Hoppa över", "/JE/X+": "Konto Support", "/PCavi": "Publik", "/RD0e2": "Nostr använder digital signatur teknik för att ge manipuleringssäkra anteckningar som säkert kan replikeras till många reläer för att ge redundant lagring av ditt innehåll.", + "/Xf4UW": "Skicka anonyma användarvärden", "/d6vEc": "Gör din profil enklare att hitta och dela", "/n5KSF": "{n} ms", - "00LcfG": "Load more", + "00LcfG": "Ladda fler", "08zn6O": "Exportera nycklar", "0Azlrb": "Hantera", "0BUTMv": "Sök...", "0jOEtS": "Ogiltig LNURL", "0mch2Y": "namnet har otillåtna tecken", + "0uoY11": "Visa status", "0yO7wF": "{n} secs", "1A7TZk": "Vad är Snort och hur fungerar det?", "1Mo59U": "Är du säker på att du vill ta bort den här anteckningen från bokmärken?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Bokmärken", "2k0Cv+": "Gillar inte ({n})", "2ukA4d": "{n} timmar", + "3KNMbJ": "Artiklar", "3Rx6Qo": "Avancerad", "3cc4Ct": "Ljust", "3gOsZq": "Översättare", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Följare", "3xCwbZ": "ELLER", "3yk8fB": "Plånbok", + "40VR6s": "Nostr Connect", "450Fty": "Ingen", "47FYwb": "Avbryt", "4IPzdn": "Primära Utvecklare", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs är en av de första NIP-05 leverantörerna och erbjuder en bra samling domäner till rimliga priser", "4Z3t5i": "Använd imgproxy för att komprimera bilder", "4rYCjn": "Anteckning till mig själv", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Skapa konto", "5oTnfy": "Köp Namn", "5rOdPG": "När du har konfigurerat din nyckelmanagers tillägg och genererat en nyckel, du kan följa vårat nya användarflöde för att ställa in din profil och hjälpa dig att hitta några intressanta personer på Nostr att följa.", "5u6iEc": "Överför till Pubkey", - "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5vMmmR": "Användarnamn är inte unika på Nostr. Nostr-adressen är din unika människoläsbara adress som är unik för dig vid registrering.", "5ykRmX": "Skicka zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Titta på repris", "65BmHb": "Det gick inte att proxybilden från {host}, klicka här för att ladda direkt", + "6OSOXl": "Anledning: {reason}", "6Yfvvp": "Skaffa en identifierare", + "6bgpn+": "Inte alla klienter stöder detta, kan du fortfarande få några zaps som om zap delning inte konfigurerades", "6ewQqw": "Gillar ({n})", "6uMqL1": "Obetald", "7+Domh": "Anteckningar", "7BX/yC": "Konto växlare", "7hp70g": "NIP-05", - "7xzTiH": "{action} till {target}", "8/vBbP": "Delningar ({n})", "89q5wc": "Bekräfta Dela vidare", + "8Kboo2": "Skanna den här QR-koden med din signeringsapp för att komma igång", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Mottagare", + "8Y6bZQ": "Ogiltig zapsplit: {input}", "8g2vyB": "namnet är för långt", "8v1NN+": "Parnings fras", + "8xNnhi": "Nostr Tillägg", "9+Ddtu": "Nästa", "9HU8vw": "Svara", "9SvQep": "Följer {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning-faktura", "ADmfQT": "Förälder", "AGNz71": "Zappa Alla {n} sats", + "AN0Z7Q": "Tystade ord", "ASRK0S": "Denna författare har tystats", "Adk34V": "Ställ in din profil", "Ai8VHU": "Obegränsat antal anteckningar på Snort-relä", @@ -92,6 +107,7 @@ "BOr9z/": "Snort är ett projekt med öppen källkod byggt av passionerade människor på deras fritid", "BWpuKl": "Uppdatera", "BcGMo+": "Anteckningar håller textinnehåll, den mest populära användningen av dessa anteckningar är att lagra \"tweet like\" meddelanden.", + "C1LjMx": "Lightning donation", "C5xzTC": "Premium", "C81/uG": "Logga ut", "C8HhVE": "Föreslagna personer att följa", @@ -106,22 +122,25 @@ "DZzCem": "Visa senaste {n} anteckningar", "DcL8P+": "Supporter", "Dh3hbq": "Auto Zap", + "Dn82AL": "Live", "DtYelJ": "Överför", "E8a4yq": "Följ några populära konton", "ELbg9p": "Dataleverantörer", "EPYwm7": "Din privata nyckel är ditt lösenord. Om du förlorar denna nyckel, kommer du att förlora tillgång till ditt konto! Kopiera den och hålla den på en säker plats. Det finns inget sätt att återställa din privata nyckel.", + "EQKRE4": "Visa emblem på profilsidor", "EWyQH5": "Global", "Ebl/B2": "Översätt till {lang}", "EcZF24": "Anpassade reläer", "EcglP9": "Nyckel", + "EjFyoR": "Donationsadress för On-chain", "EnCOBJ": "Köpa", "Eqjl5K": "Endast Snort och vår integrationspartner identifierare ger dig ett färgglatt domännamn, men du är välkommen att använda andra tjänster också.", "F+B3x1": "Vi har också samarbete med nostrplebs.com för att ge dig fler alternativ", "F3l7xL": "Lägg till konto", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL för att vidarebefordra zaps till", + "FMfjrl": "Visa statusmeddelanden på profilsidor", "FS3b54": "Klart!", - "FSYL8G": "Trending Users", + "FSYL8G": "Trendande användare", "FdhSU2": "Hämta nu", "FfYsOb": "Ett fel har uppstått!", "FmXUJg": "följer dig", @@ -129,47 +148,54 @@ "G1BGCg": "Välj plånbok", "GFOoEE": "Salt", "GL8aXW": "Bokmärken ({n})", - "GSye7T": "Lightning Address", + "GQPtfk": "Anslut till Stream", + "GSye7T": "Lightning-adress", "GUlSVG": "Hämta din inkluderade Snort nostr adress", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} Ogillar", + "Gxcr08": "Sänd händelse", "H+vHiz": "Hex Key..", "H0JBH6": "Logga Ut", "H6/kLh": "Beställningen är betald!", "HAlOn1": "Namn", - "HF4YnO": "Watch Live!", "HFls6j": "namnet kommer att vara tillgängligt senare", "HOzFdo": "Tystad", "HWbkEK": "Rensa cache och ladda om", "HbefNb": "Öppna plånbok", + "HhcAVH": "Du följer inte denna person, klicka här för att ladda media från {link}, eller uppdatera dina inställningar för att alltid ladda media från alla.", "IDjHJ6": "Tack för att du använder Snort, vänligen överväg att donera om du kan.", "IEwZvs": "Är du säker på att du vill ta bort denna anteckningen?", - "IKKHqV": "Follows", + "IKKHqV": "Följer", "INSqIz": "Twitter-användarnamn...", "IUZC+0": "Detta innebär att ingen kan ändra anteckningar som du har skapat och alla kan enkelt kontrollera att de anteckningar de läser har skapats av dig.", "Ig9/a1": "Skickade {n} sats till {name}", + "IoQq+a": "Klicka här för att ladda ändå", "Ix8l+B": "Trendande anteckningar", "J+dIsA": "Prenumerationer", "JCIgkj": "Användarnamn", + "JGrt9q": "Skicka sats till {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "Ingen lightning adress", - "JeoS4y": "Repost", - "JjGgXI": "Search users", + "JeoS4y": "Dela", + "JjGgXI": "Sök användare", "JkLHGw": "Webbsida", "JymXbw": "Privat nyckel", "K3r6DQ": "Radera", "K7AkdL": "Visa", "KAhAcM": "Ange LNDHub-konfiguration", - "KLo3SP": "Anledning: {reason}", "KQvWvD": "Raderad", "KWuDfz": "Jag har sparat mina nycklar, fortsätt", "KahimY": "Okänd händelsetyp: {kind}", "KoFlZg": "Ange mint URL", + "KtsyO0": "Ange PIN-kod", "LF5kYT": "Andra anslutningar", + "LR1XjT": "PIN-koden är för kort", "LXxsbk": "Anonymt", "LgbKvU": "Kommentar", "Lu5/Bj": "Öppna på Zapstr", - "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "Lw+I+J": "{n,plural,one {}=0{{name} zappade} other{{name} & {n} andra zappade}}", + "LwYmVi": "Zaps på denna anteckning kommer att delas upp till följande användare.", + "M10zFV": "Nostr Connect", "M3Oirc": "Debug Menus", "MBAYRO": "Visar \"Kopiera ID\" och \"Kopiera händelse JSON\" i kontextmenyn på varje meddelande", "MI2jkA": "Inte tillgänglig:", @@ -177,9 +203,10 @@ "MRp6Ly": "Twitter-användarnamn", "MWTx65": "Förvald Sida", "Mrpkot": "Betala för prenumeration", - "MuVeKe": "Buy nostr address", + "MuVeKe": "Köp nostr adress", "MzRYWH": "Köper {item}", "N2IrpM": "Bekräfta", + "NAidKb": "Notifikationer", "NAuFNH": "Du har redan en prenumeration av den här typen, vänligen förnya eller betala", "NNSu3d": "Importera personer som du följer på Twitter", "NdOYJJ": "Hmm inget här.. Kolla in {newUsersPage} för att följa några rekommenderade nostrich's!", @@ -192,7 +219,6 @@ "OLEm6z": "Okänt inloggningsfel", "OQXnew": "Din prenumeration är fortfarande aktiv, du kan inte förnya ännu", "ORGv1Q": "Skapad", - "P04gQm": "Alla zaps som skickas till denna anteckning kommer att tas emot av följande LNURL", "P61BTu": "Kopiera händelse JSON", "P7FD0F": "System (standard)", "P7nJT9": "Totalt idag (UTC): {amount} sats", @@ -207,8 +233,7 @@ "QxCuTo": "Konst av {name}", "Qxv0B2": "Du har för närvarande {number} sats i din zap pool.", "R/6nsx": "Prenumeration", - "R1fEdZ": "Vidarebefordra Zaps", - "R81upa": "People you follow", + "R81upa": "Personer du följer", "RDZVQL": "Kontrollera", "RahCRH": "Förfallen", "RfhLwC": "Av: {author}", @@ -217,20 +242,20 @@ "RoOyAh": "Reläer", "Rs4kCE": "Bokmärke", "RwFaYs": "Sortera", + "SMO+on": "Skicka zap till {name}", "SOqbe9": "Uppdatera Lightning-adress", "SP0+yi": "Köp Prenumeration", - "SX58hM": "Kopiera", "SYQtZ7": "LN Adress Proxy", - "ShdEie": "Mark all read", + "ShdEie": "Markera alla som lästa", "Sjo1P4": "Anpassat", "Ss0sWu": "Betala nu", + "StKzTE": "Författaren har markerat denna anteckning som ett känsligt ämne", "TDR5ge": "Media i anteckningar visas automatiskt för valda personer, annars visas endast länken", - "TMfYfY": "Cashu token", + "TP/cMX": "Avslutade", "TpgeGw": "Hex Salt..", "Tpy00S": "Personer", "UDYlxu": "Väntande prenumerationer", - "ULotH9": "Belopp: {amount} sats", - "UT7Nkj": "New Chat", + "UT7Nkj": "Ny chatt", "UUPFlt": "Användare måste acceptera innehållsvarningen för att visa innehållet i din anteckning.", "Up5U7K": "Blockera", "VBadwB": "Hmm, kan inte hitta ett Key Manager tillägg.. prova att ladda om sidan.", @@ -239,15 +264,16 @@ "VR5eHw": "Publik nyckel (npub/nprofile)", "VlJkSk": "{n} tystad", "VnXp8Z": "Avatar", - "VtPV/B": "Logga in med tillägg (NIP-07)", - "VvaJst": "View Wallets", + "VvaJst": "Visa plånböcker", "Vx7Zm2": "Hur fungerar nycklar?", "W1yoZY": "Det verkar som om du inte har några prenumerationer, du kan skaffa en {link}", "W2PiAr": "{n} Blockerad", "W9355R": "Avtysta", "WONP5O": "Hitta personer du följde på twitter så du kan följa dem på nostr (Data som tillhandahålls av {provider})", + "WvGmZT": "npub / nprofile / nostr adress", "WxthCV": "t.ex. Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Skicka användningsstatistik", "XICsE8": "Filvärdar", "XgWvGA": "Reaktioner", "Xopqkl": "Ditt förvalda zap-belopp är {number} sats, exempelvärden beräknas utifrån detta.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Ange parningsfras", "ZKORll": "Aktivera nu", "ZLmyG9": "Bidragsgivare", - "ZUZedV": "Lightning Donation:", + "ZS+jRE": "Skicka zap-delning till", "Zr5TMx": "Profilinställning", "a5UPxh": "Finansiera utvecklare och plattformar som tillhandahåller NIP-05 verifieringstjänster", "a7TDNm": "Anteckningar kommer att strömmas i realtid in i global och antecknings fliken", "aWpBzj": "Visa mer", "b12Goz": "Mnemonic", "b5vAk0": "Ditt namn kommer att fungera som en Lightning adress och kommer att omdirigeras till din valda LNURL eller Lightning adress", + "bLZL5a": "Skaffa adress", "bQdA2k": "Känsligt innehåll", "bep9C3": "Publik nyckel", "bfvyfs": "Anon", "brAXSu": "Välj ett användarnamn", "bxv59V": "Just nu", "c+oiJe": "Installera Tillägg", + "c2DTVd": "Ange en pin för att kryptera din privata nyckel, du måste ange denna pin varje gång du öppnar Snort.", "c35bj2": "Om du har en förfrågan om din NIP-05 beställning, vänligen DM {link}", "c3g2hL": "Sänd igen", "cFbU1B": "Använder du Alby? Gå till {link} för att få din NWC konfiguration!", @@ -287,16 +315,18 @@ "d7d0/x": "LN Adress", "dOQCL8": "Visnings namn", "e61Jf3": "Kommer snart", + "e7VmYP": "Ange pin för att låsa upp din privata nyckel", "e7qqly": "Markera alla som lästa", "eHAneD": "Reaktion emoji", "eJj8HD": "Bli Verifierad", "eSzf2G": "En enda zap med {nIn} sats kommer att fördela {nOut} sats till zappoolen.", - "eXT2QQ": "Group Chat", + "eXT2QQ": "Gruppchatt", "fBI91o": "Zap", "fOksnD": "Kan inte rösta eftersom LNURL-tjänsten inte stöder zaps", "fWZYP5": "Fastnålad", "filwqD": "Läs", "flnGvv": "Vad tänker du på?", + "fqwcJ1": "On-chain donation", "fsB/4p": "Sparad", "g5pX+a": "Om", "g985Wp": "Misslyckades att skicka röst", @@ -310,9 +340,9 @@ "h8XMJL": "Emblem", "hK5ZDk": "världen", "hMzcSq": "Meddelanden", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Support", "hicxcO": "Visa svar", + "hmZ3Bz": "Media", "hniz8Z": "här", "i/dBAR": "Zap Pool", "iCqGww": "Reaktioner ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL översättningar", "iGT1eE": "Förhindra falska konton från att imitera dig", "iNWbVV": "Namn", - "iUsU2x": "Mint: {url}", "iXPL0Z": "Kan inte logga in med privat nyckel på en osäker anslutning, använd ett tillägg för Nostr key manager istället", "ieGrWo": "Följ", "itPgxd": "Profil", @@ -334,9 +363,9 @@ "jzgQ2z": "{n} Reaktioner", "k2veDA": "Skriv", "k7sKNy": "Vår alldeles egna NIP-05 verifieringstjänst, hjälpa till att stödja utvecklingen av denna webbplats och få ett glänsande speciellt märke på vår webbplats!", - "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kJYo0u": "{n,plural,one {}=0{{name} delade} other{{name} & {n} andra delade}}", "kaaf1E": "nu", - "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "kuPHYE": "{n,plural,one {}=0{{name} gillade} other{{name} & {n} andra gillade}}", "l+ikU1": "Allting i {plan}", "lBboHo": "Om du vill prova några andra, kolla in {link} för mer!", "lCILNz": "Köp Nu", @@ -368,10 +397,10 @@ "oJ+JJN": "Inget hittat :/", "odFwjL": "Följer endast", "odhABf": "Logga in", - "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "ojzbwv": "Hej, det ser ut som du inte har en Nostr adress ännu, du bör skaffa en! Kolla in {link}", "osUr8O": "Du kan också använda dessa tillägg för att logga in på de flesta Nostr webbplatser.", "oxCa4R": "Att få en identifierare hjälper till att bekräfta den verkliga du för människor som känner dig. Många kan ha ett användarnamn @jack, men det finns bara en jack@cash.app.", - "p4N05H": "Upload", + "p4N05H": "Ladda upp", "p85Uwy": "Aktiva prenumerationer", "pI+77w": "Nedladdningsbara säkerhetskopior från Snort relä", "puLNUJ": "Fäst", @@ -381,9 +410,11 @@ "qMx1sA": "Förvalt Zap-belopp", "qUJTsT": "Blockerad", "qdGuQo": "Din privata nyckel är (dela inte detta med någon)", + "qfmMQh": "Den här anteckningen har tystats", "qkvYUb": "Lägg till i min profil", "qmJ8kD": "Översättning misslyckades", - "qtWLmt": "Like", + "qtWLmt": "Gilla", + "qz9fty": "Felaktig pin", "r3C4x/": "Mjukvara", "r5srDR": "Ange lösenord för plånboken", "rT14Ow": "Lägg till reläer", @@ -392,7 +423,10 @@ "rmdsT4": "{n} dagar", "rrfdTe": "Detta är samma teknik som används av Bitcoin och har visat sig vara extremt säker.", "rudscU": "Det gick inte att ladda följande, försök igen senare", + "sKDn4e": "Visa emblem", + "sUNhQE": "användare", "sWnYKw": "Snort är utformat för att ha en liknande upplevelse som Twitter.", + "sZQzjQ": "Misslyckades att tolka zapsplit: {input}", "svOoEH": "Namnslåss och personifiering är inte tillåtet. Snort och våra partners förbehåller sig rätten att avsluta ditt handle (inte ditt konto - ingen kan ta bort det) för att du bryter mot denna regel.", "tOdNiY": "Mörkt", "th5lxp": "Skicka anteckning till en delmängd av dina skrivreläer", @@ -400,28 +434,29 @@ "ttxS0b": "Supporter Emblem", "u/vOPu": "Betald", "u4bHcR": "Kolla in koden här: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "Dela vidare måste bekräftas manuellt", + "uc0din": "Skicka sats-delningar till", "usAvMr": "Redigera profil", "ut+2Cd": "Skaffa en partner identifierare", - "v8lolG": "Start chat", + "v8lolG": "Starta chatt", "vOKedj": "{n,plural,=1{& {n} andra} other{& {n} andras}}", - "vU71Ez": "Betalar med {wallet}", "vZ4quW": "NIP-05 är en DNS-baserad verifieringsspec som hjälper till att validera dig som en riktig användare.", "vhlWFg": "Alternativ för omröstning", - "vlbWtt": "Get a free one", + "vlbWtt": "Få en kostnadsfri", "vrTOHJ": "{amount} sats", - "vxwnbh": "Amount of work to apply to all published events", + "vxwnbh": "Mängd arbete att tillämpas för alla publicerade händelser", "wEQDC6": "Ändra", "wLtRCF": "Din nyckel", + "wSZR47": "Skicka", "wWLwvh": "Anon", "wYSD2L": "Nostr Adress", "wih7iJ": "namnet är blockerat", + "wofVHy": "Moderering", "wqyN/i": "Ta reda på mer om {service} på {link}", "wtLjP6": "Kopiera ID", "x/Fx2P": "Finansiera de tjänster som du använder genom att dela upp en del av alla dina zaps i en pool med pengar!", - "x/q8d5": "Denna anteckning har markerats som känslig, klicka här för att visa", "x82IOl": "Tysta", + "xIcAOU": "Röster från {type}", "xIoGG9": "Gå till", "xJ9n2N": "Din publika nyckel", "xKflGN": "{username}'s Följer på Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Språk", "yCLnBC": "LNURL or Lightning Adress", "yCmnnm": "Läs global från", + "zCb8fX": "Vikt", "zFegDD": "Kontakt", "zINlao": "Ägare", "zQvVDJ": "Alla", "zcaOTs": "Zap belopp i sats", "zjJZBd": "Du är redo!", "zonsdq": "Det gick inte att ladda LNURL-tjänsten", - "zvCDao": "Visa automatiskt de senaste anteckningarna" + "zvCDao": "Visa automatiskt de senaste anteckningarna", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/sw_KE.json b/packages/app/src/translations/sw_KE.json index fb22ad2d..5f308eed 100644 --- a/packages/app/src/translations/sw_KE.json +++ b/packages/app/src/translations/sw_KE.json @@ -3,6 +3,7 @@ "+PzQ9Y": "Malipo sasa", "+Vxixo": "Gumzo la Siri la Kikundi", "+aZY2h": "Aina ya Zap", + "+tShPg": "following", "+vA//S": "Ingizo", "+vIQlC": "Tafadhali hakikisha kuwa umehifadhi nenosiri lifuatalo ili kudhibiti mpini wako katika siku zijazo", "+vVZ/G": "Unganisha", @@ -11,6 +12,7 @@ "/JE/X+": "Usaidizi wa Akaunti", "/PCavi": "Umma", "/RD0e2": "Nostr hutumia teknolojia ya sahihi ya dijiti kutoa madokezo ya uthibitisho ambayo yanaweza kuigwa kwa usalama kwenye relay nyingi ili kutoa uhifadhi mwingi wa maudhui yako.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "Rahisisha wasifu wako kupata na kushiriki", "/n5KSF": "{n} ms", "00LcfG": "Pakia zaidi", @@ -19,6 +21,7 @@ "0BUTMv": "Tafuta...", "0jOEtS": "LNURL Batili", "0mch2Y": "jina limekataza herufi", + "0uoY11": "Show Status", "0yO7wF": "{n} sekundi", "1A7TZk": "Snort ni nini, na inafanya kazi vipi?", "1Mo59U": "Je, una uhakika unataka kuondoa dokezo hili kutoka kwa vialamisho?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} Alamisho", "2k0Cv+": "Haipendwi ({n})", "2ukA4d": "{n} masaa", + "3KNMbJ": "Articles", "3Rx6Qo": "Hali ya juu", "3cc4Ct": "Mwangaza", "3gOsZq": "Wafasiri", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} Wafuasi", "3xCwbZ": "AU", "3yk8fB": "Pochi", + "40VR6s": "Nostr Connect", "450Fty": "Hakuna", "47FYwb": "Ghairi", "4IPzdn": "Wasanidi wa Msingi", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs ni mmoja wa watoa huduma wa kwanza wa NIP-05 kwenye nafasi na inatoa mkusanyiko mzuri wa vikoa kwa bei nzuri", "4Z3t5i": "Tumia imgproxy kubana picha", "4rYCjn": "Kumbuka mwenyewe", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "Tengeneza akaunti", "5oTnfy": "Nunua Tambulisho", "5rOdPG": "Mara tu unapoweka kiendelezi cha kidhibiti chako cha ufunguo na kuunda ufunguo, unaweza kufuata mtiririko wa watumiaji wetu wapya ili kusanidi wasifu wako na kukusaidia kupata watu wanaovutia kwenye Nostr wa kufuata.", "5u6iEc": "Hamisha kwa Pubkey", "5vMmmR": "Majina ya watumiaji sio ya kipekee kwenye Nostr. Anwani ya nostr ni anwani yako ya kipekee inayoweza kusomeka na binadamu ambayo ni ya kipekee kwako unapojiandikisha.", "5ykRmX": "Tuma zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Imeshindwa kuweka picha ya seva mbadala kutoka kwa {host}, bofya hapa ili kupakia moja kwa moja", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "Pata kitambulisho", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "Vipendwa ({n})", "6uMqL1": "Haijalipwa", "7+Domh": "Noti", "7BX/yC": "Kibadilisha Akaunti", "7hp70g": "NIP-05", - "7xzTiH": "{action} kwa {target}", "8/vBbP": "Machapisho upya ({n})", "89q5wc": "Thibitisha Reposts", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "jina refu sana", "8v1NN+": "Maneno ya kuoanisha", + "8xNnhi": "Nostr Extension", "9+Ddtu": "Inayofuata", "9HU8vw": "Jibu", "9SvQep": "Anafuata {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Ankara ya umeme", "ADmfQT": "Mzazi", "AGNz71": "Zap {n} sats zote", + "AN0Z7Q": "Muted Words", "ASRK0S": "Mwandishi huyu amenyamazishwa", "Adk34V": "Sanidi Wasifu wako", "Ai8VHU": "Uhifadhi wa noti bila kikomo kwenye relay ya Snort", @@ -92,6 +107,7 @@ "BOr9z/": "Snort ni mradi wa chanzo huria uliojengwa na watu wenye shauku katika wakati wao wa bure", "BWpuKl": "Sasisha", "BcGMo+": "Vidokezo vinashikilia maudhui ya maandishi, matumizi maarufu zaidi ya madokezo haya ni kuhifadhi ujumbe wa \"tweet like\".", + "C1LjMx": "Lightning Donation", "C5xzTC": "Premiumu", "C81/uG": "Toka nje", "C8HhVE": "Inayopendekezwa Inafuata", @@ -106,20 +122,23 @@ "DZzCem": "Onyesha vidokezo vya {n} hivi karibuni", "DcL8P+": "Msaidizi", "Dh3hbq": "Zap ya kiotomatiki", + "Dn82AL": "Live", "DtYelJ": "Uhamisho", "E8a4yq": "Fuata baadhi ya akaunti maarufu", "ELbg9p": "Watoa Data", "EPYwm7": "Ufunguo wako wa faragha ni nenosiri lako. Ukipoteza ufunguo huu, utapoteza ufikiaji wa akaunti yako! Nakili na uiweke mahali salama. Hakuna njia ya kuweka upya ufunguo wako wa faragha.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Ulimwenguni", "Ebl/B2": "Tafsiri hadi {lang}", "EcZF24": "Reli maalum", "EcglP9": "Funguo", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "Nunua", "Eqjl5K": "Snort pekee na kitambulisho chetu cha mshirika wa ushirikiano kinakupa jina la kikoa zuri, lakini unakaribishwa kutumia huduma zingine pia.", "F+B3x1": "Pia tumeshirikiana na nostrplebs.com ili kukupa chaguo zaidi", "F3l7xL": "Ongeza Akaunti", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL ya kusambaza zaps kwa", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "Imekamilika!", "FSYL8G": "Watumiaji wanaovuma", "FdhSU2": "Dai sasa", @@ -129,28 +148,32 @@ "G1BGCg": "Chagua Pochi", "GFOoEE": "Chumvi", "GL8aXW": "Alamisho ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Anwani ya Umeme", "GUlSVG": "Dai anwani yako ya Nostr iliyojumuishwa", "Gcn9NQ": "Kiungo cha Sumaku", "GspYR7": "{n} Kutopenda", + "Gxcr08": "Broadcast Event", "H+vHiz": "Ufunguo wa Hex..", "H0JBH6": "Toka nje", "H6/kLh": "Agizo limelipwa!", "HAlOn1": "Jina", - "HF4YnO": "Tazama Moja kwa Moja!", "HFls6j": "jina litapatikana baadae", "HOzFdo": "Imenyamazishwa", "HWbkEK": "Futa akiba na upakie upya", "HbefNb": "Fungua Pochi", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "Asante kwa kutumia Snort, tafadhali zingatia kuchangia ukiweza.", "IEwZvs": "Je, una uhakika unataka kubandua kidokezo hiki?", "IKKHqV": "Anafuata", "INSqIz": "Jina la mtumiaji la Twitter...", "IUZC+0": "Hii inamaanisha kuwa hakuna mtu anayeweza kurekebisha madokezo ambayo umeunda na kila mtu anaweza kuthibitisha kwa urahisi kuwa madokezo anayosoma yameundwa na wewe.", "Ig9/a1": "Imetumwa sats {n} kwa {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Vidokezo vinavyovuma", "J+dIsA": "Usajili", "JCIgkj": "Jina la mtumiaji", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "Hakuna anwani ya umeme", "JeoS4y": "Reposti", @@ -160,16 +183,19 @@ "K3r6DQ": "Futa", "K7AkdL": "Onyesha", "KAhAcM": "Ingiza usanidi wa LNDHub", - "KLo3SP": "Sababu: {reason}", "KQvWvD": "Imefutwa", "KWuDfz": "Nimehifadhi funguo zangu, endelea", "KahimY": "Aina ya tukio lisilojulikana: {kind}", "KoFlZg": "Weka URL ndogo", + "KtsyO0": "Enter Pin", "LF5kYT": "Viunganisho Vingine", + "LR1XjT": "Pin too short", "LXxsbk": "Asiyejulikana", "LgbKvU": "Toa maoni", "Lu5/Bj": "Fungua kwenye Zapstr", "Lw+I+J": "{n,plural,one {}=0{{name} zapped} other{{name} & {n} wengine walizap}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "Menyu ya Utatuzi", "MBAYRO": "Inaonyesha \"Nakili Kitambulisho\" na \"Nakili Tukio JSON\" katika menyu ya muktadha kwenye kila ujumbe", "MI2jkA": "Haipatikani:", @@ -180,6 +206,7 @@ "MuVeKe": "Nunua anwani ya nostr", "MzRYWH": "Unanunua {item}", "N2IrpM": "Thibitisha", + "NAidKb": "Notifications", "NAuFNH": "Tayari una usajili wa aina hii, tafadhali usasishe au ulipe", "NNSu3d": "Ingiza Ifuatayo Twitter", "NdOYJJ": "Hmm hakuna kitu hapa.. Angalia {newUsersPage} ili kufuata nostrich's wanaopendekezwa!", @@ -192,7 +219,6 @@ "OLEm6z": "Hitilafu isiyojulikana ya kuingia", "OQXnew": "Usajili wako bado unatumika, bado huwezi kusasisha", "ORGv1Q": "Imeundwa", - "P04gQm": "Zaps zote zilizotumwa kwa noti hii zitapokelewa na LNURL ifuatayo", "P61BTu": "Nakili Tukio JSON", "P7FD0F": "Mfumo (Chaguo-msingi)", "P7nJT9": "Jumla ya leo (UTC): {amount} sats", @@ -207,7 +233,6 @@ "QxCuTo": "Sanaa kwa {name}", "Qxv0B2": "Kwa sasa una viti {number} kwenye zap pool yako.", "R/6nsx": "Usajili", - "R1fEdZ": "Mbele Zaps", "R81upa": "Watu unaowafuata", "RDZVQL": "Angalia", "RahCRH": "Muda wake umeisha", @@ -217,19 +242,19 @@ "RoOyAh": "Relays", "Rs4kCE": "Alamisho", "RwFaYs": "Panga", + "SMO+on": "Send zap to {name}", "SOqbe9": "Sasisha Anwani ya Umeme", "SP0+yi": "Nunua Usajili", - "SX58hM": "Nakili", "SYQtZ7": "Wakala wa Anwani ya LN", "ShdEie": "Weka alama kuwa zote zimesomwa", "Sjo1P4": "Desturi", "Ss0sWu": "Lipa Sasa", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Maudhui katika madokezo yataonyeshwa kiotomatiki kwa watu waliochaguliwa, vinginevyo kiungo pekee ndicho kitakachoonyeshwa", - "TMfYfY": "Ishara ya Cashu", + "TP/cMX": "Ended", "TpgeGw": "Hex chumvi..", "Tpy00S": "Watu", "UDYlxu": "Usajili Unaosubiri", - "ULotH9": "Kiasi: {amount} sats", "UT7Nkj": "Gumzo Mpya", "UUPFlt": "Watumiaji lazima wakubali onyo la maudhui ili kuonyesha maudhui ya dokezo lako.", "Up5U7K": "Zuia", @@ -239,15 +264,16 @@ "VR5eHw": "Ufunguo wa umma (npub/nprofile)", "VlJkSk": "{n} imenyamazishwa", "VnXp8Z": "Picha", - "VtPV/B": "Ingia kwa kutumia Kiendelezi (NIP-07)", "VvaJst": "Tazama Pochi", "Vx7Zm2": "Vifunguo hufanyaje kazi?", "W1yoZY": "Inaonekana huna usajili wowote, unaweza kupata moja {link}", "W2PiAr": "{n} Imezuiwa", "W9355R": "Rejesha sauti", "WONP5O": "Tafuta twitter yako inakufuata kwenye nostr (Data imetolewa na {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "kwa mfano Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "Wapangishi wa faili", "XgWvGA": "Miitikio", "Xopqkl": "Kiasi chako chaguomsingi cha zap ni {number} sats, thamani za mfano zinakokotolewa kutoka hii.", @@ -260,19 +286,21 @@ "Z4BMCZ": "Weka maneno ya kuoanisha", "ZKORll": "Washa Sasa", "ZLmyG9": "Wachangiaji", - "ZUZedV": "Mchango wa umeme:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Sanidi wasifu", "a5UPxh": "Wasanidi wa mfuko na mifumo inayotoa huduma za uthibitishaji wa NIP-05", "a7TDNm": "Vidokezo vitatiririka katika muda halisi hadi kwenye kichupo cha madokezo cha kimataifa", "aWpBzj": "Onyesha zaidi", "b12Goz": "Mnemonic", "b5vAk0": "Nchi yako itafanya kama anwani ya umeme na itaelekeza kwenye LNURL uliyochagua au anwani ya Umeme", + "bLZL5a": "Get Address", "bQdA2k": "Maudhui Nyeti", "bep9C3": "Ufunguo wa Umma", "bfvyfs": "Hajulikan", "brAXSu": "Chagua jina la mtumiaji", "bxv59V": "Sasa hivi", "c+oiJe": "Sakinisha Kiendelezi", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "Ikiwa una swali kuhusu agizo lako la NIP-05 tafadhali DM {link}", "c3g2hL": "Tangaza Tena", "cFbU1B": "Unatumia Alby? Nenda kwenye {link} ili upate usanidi wako wa NWC!", @@ -287,6 +315,7 @@ "d7d0/x": "Anwani ya LN", "dOQCL8": "Jina la kuonyesha", "e61Jf3": "Inakuja hivi karibuni", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "Weka Alama Yote Yamesomwa", "eHAneD": "Emoji ya majibu", "eJj8HD": "Pata Kuthibitishwa", @@ -297,6 +326,7 @@ "fWZYP5": "Imebandikwa", "filwqD": "Soma", "flnGvv": "Unafikiria nini?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "Imehifadhiwa", "g5pX+a": "Kuhusu", "g985Wp": "Imeshindwa kutuma kura", @@ -310,9 +340,9 @@ "h8XMJL": "Beji", "hK5ZDk": "dunia", "hMzcSq": "Ujumbe", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "Inasaidia", "hicxcO": "Onyesha majibu", + "hmZ3Bz": "Media", "hniz8Z": "hapa", "i/dBAR": "Zap Pool", "iCqGww": "Maoni ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "Tafsiri za DeepL", "iGT1eE": "Zuia akaunti ghushi zisikuige", "iNWbVV": "Mpini", - "iUsU2x": "Minti: {url}", "iXPL0Z": "Haiwezi kuingia kwa kutumia ufunguo wa faragha kwenye muunganisho usio salama, tafadhali tumia kiendelezi cha kidhibiti cha ufunguo wa Nostr badala yake", "ieGrWo": "Fuata", "itPgxd": "Wasifu", @@ -381,9 +410,11 @@ "qMx1sA": "Kiasi chaguo-msingi cha Zap", "qUJTsT": "Imezuiwa", "qdGuQo": "Ufunguo wako wa Faragha Ni (usishiriki hii na mtu yeyote)", + "qfmMQh": "This note has been muted", "qkvYUb": "Ongeza kwa Wasifu", "qmJ8kD": "Tafsiri imeshindwa", "qtWLmt": "Penda", + "qz9fty": "Incorrect pin", "r3C4x/": "Programu", "r5srDR": "Ingiza nenosiri la pochi", "rT14Ow": "Ongeza Relay", @@ -392,7 +423,10 @@ "rmdsT4": "siku {n}", "rrfdTe": "Hii ni teknolojia sawa ambayo hutumiwa na Bitcoin na imethibitishwa kuwa salama sana.", "rudscU": "Imeshindwa kupakia wafuasi, tafadhali jaribu tena baadaye", + "sKDn4e": "Show Badges", + "sUNhQE": "mtumiaji", "sWnYKw": "Snort imeundwa ili kuwa na matumizi sawa na Twitter.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "Kuchuchumaa majina na uigaji hakuruhusiwi. Koroma na washirika wetu wana haki ya kusitisha mpini wako (sio akaunti yako - hakuna mtu anayeweza kuuondoa) kwa kukiuka sheria hii.", "tOdNiY": "Giza", "th5lxp": "Tuma dokezo kwa kikundi kidogo cha relay zako za uandishi", @@ -400,13 +434,12 @@ "ttxS0b": "Beji ya Msaidizi", "u/vOPu": "Imelipwa", "u4bHcR": "Angalia msimbo hapa: {link}", - "uD/N6c": "Zap {target} sats {n}", "uSV4Ti": "Machapisho mapya yanahitaji kuthibitishwa mwenyewe", + "uc0din": "Send sats splits to", "usAvMr": "Badilisha Wasifu", "ut+2Cd": "Pata kitambulisho cha mshirika", "v8lolG": "Anzisha gumzo", "vOKedj": "{n,plural,one {}=1{& {n} mwingine} other{& {n} wengine}}", - "vU71Ez": "Kulipa kwa {wallet}", "vZ4quW": "NIP-05 ni uthibitishaji wa msingi wa DNS ambao husaidia kukuthibitisha kama mtumiaji halisi.", "vhlWFg": "Chaguo za Kura", "vlbWtt": "Pata ya bure", @@ -414,14 +447,16 @@ "vxwnbh": "Kiasi cha kazi ya kuomba kwa matukio yote yaliyochapishwa", "wEQDC6": "Hariri", "wLtRCF": "Ufunguo wako", + "wSZR47": "Submit", "wWLwvh": "Hajulikan", "wYSD2L": "Anwani ya Nostr", "wih7iJ": "jina limezuiwa", + "wofVHy": "Moderation", "wqyN/i": "Pata maelezo zaidi kuhusu {service} kwenye {link}", "wtLjP6": "Nakili ID", "x/Fx2P": "Fundisha huduma unazotumia kwa kugawanya sehemu ya zap zako zote kwenye kundi la fedha!", - "x/q8d5": "Dokezo hili limetiwa alama kuwa nyeti, bofya hapa ili kufichua", "x82IOl": "Nyamazisha", + "xIcAOU": "Kura za {type}", "xIoGG9": "Enda kwa", "xJ9n2N": "Ufunguo wako wa umma", "xKflGN": "{username}''s Inafuata kwenye Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "Lugha", "yCLnBC": "LNURL au Anwani ya Umeme", "yCmnnm": "Soma kimataifa kutoka", + "zCb8fX": "Weight", "zFegDD": "Wasiliana", "zINlao": "Mmiliki", "zQvVDJ": "Yote", "zcaOTs": "Kiasi cha Zap katika seti", "zjJZBd": "Uko tayari!", "zonsdq": "Imeshindwa kupakia huduma ya LNURL", - "zvCDao": "Onyesha madokezo mapya kiotomatiki" + "zvCDao": "Onyesha madokezo mapya kiotomatiki", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/ta_IN.json b/packages/app/src/translations/ta_IN.json index cdfbfa53..dff86c0d 100644 --- a/packages/app/src/translations/ta_IN.json +++ b/packages/app/src/translations/ta_IN.json @@ -3,6 +3,7 @@ "+PzQ9Y": "தொகையைச் செலுத்துக", "+Vxixo": "Secret Group Chat", "+aZY2h": "ஜாப் வகை", + "+tShPg": "following", "+vA//S": "உள்நுழைவுகள்", "+vIQlC": "பிற்காலத்தில் உங்களது கணக்கை நிர்வகிக்க, கீழே உள்ள கடவுச்சொல்லைத் தவறாமல் சேமிக்கவும்", "+vVZ/G": "இணை", @@ -11,6 +12,7 @@ "/JE/X+": "கணக்கு உதவி", "/PCavi": "பொது", "/RD0e2": "சேதப்படுத்த முடியாத குறிப்புகளை வழங்க, நாஸ்டர் டிஜிட்டல் கையொப்ப தொழில் நுட்பத்தைப் பயன் படுத்துகிறது. இதனால் பல ரிலேகளில் குறிப்புகள் பிரதியெடுக்கப் பட்டு, குறிப்பின் உள்ளடக்கம் கூடுதல் சேமிப்பு அடைகிறது.", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "உங்கள் சுயவிவரத்தை கண்டறிவதையும் பகிர்வதையும் எளிதாக்குங்கள்", "/n5KSF": "{n} மில்லி வினாடிகள்", "00LcfG": "மேலும் காண்க", @@ -19,6 +21,7 @@ "0BUTMv": "தேடு...", "0jOEtS": "தவறான LNURL", "0mch2Y": "பெயர் அங்கீகரிக்கப்படாத எழுத்துக்களைக் கொண்டுள்ளது", + "0uoY11": "Show Status", "0yO7wF": "{n} வினாடிகள்", "1A7TZk": "ஸ்நார்ட் என்றால் என்ன? அது எவ்வாறு வேலை செய்கிறது?", "1Mo59U": "இந்தக் குறிப்பைப் புக்மார்க்குகளிலிருந்து அகற்ற நிச்சயமாக விரும்புகிறீர்களா?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} புத்தகக் குறிகள்", "2k0Cv+": "விருப்பமின்மைகள் ({n})", "2ukA4d": "{n} மணித்துளிகள்", + "3KNMbJ": "Articles", "3Rx6Qo": "மேம்படுத்தப்பட்ட", "3cc4Ct": "ஒளி", "3gOsZq": "மொழிபெயர்ப்பாளர்கள்", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} பின்தொடர்வோர்", "3xCwbZ": "அல்லது", "3yk8fB": "பணப்பை", + "40VR6s": "Nostr Connect", "450Fty": "எதுவுமில்லை", "47FYwb": "ரத்துசெய்", "4IPzdn": "முதன்மை உருவாக்கி", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs முதல் NIP-05 வழங்குநர்களில் ஒன்றாகும். மேலும், இது நியாயமான விலையில் டொமைன்களின் நல்ல தொகுப்பை வழங்குகிறது", "4Z3t5i": "படங்களை சுருக்க imgproxy உபயோகிக்கவும்", "4rYCjn": "சுய குறிப்பு", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "கணக்கை உருவாக்கு", "5oTnfy": "பயனர் பெயரை வாங்கவும்", "5rOdPG": "உங்கள் சாவி மேலாளர் நீட்டிப்பை அமைத்து, ஒரு சாவியை உருவாக்கியதும், உங்கள் சுயவிவரத்தை அமைப்பதற்கு, எங்கள் 'புதிய பயனர்களின் ஓட்டத்தை' நீங்கள் பின்பற்றலாம். பிறகு, நீங்கள் பின்தொடருவதற்கு நாஸ்டரில் சுவாரஸ்யமான சிலரைக் கண்டறியலாம்.", "5u6iEc": "பொது சாவிக்கு பரிமாற்றவும்", "5vMmmR": "நாஸ்டரில் உள்ள பயனர்பெயர்கள் உங்களுக்கு மட்டுமேயான தனிப்பட்ட பயனர்பெயர் கிடையாது. நாஸ்டர் முகவரி மட்டுமே மனிதர்களால் வாசிக்க இயலும் தனிப்பட்ட முகவரி ஆகும். அந்த முகவரி நீங்கள் நாஸ்டரில் பதிவு செய்யும்போது உங்களுக்கு வழங்கப்படும்.", "5ykRmX": "ஜாப் அனுப்பு", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "ஒரு அடையாளங்காட்டியைப் பெறுங்கள்", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "விருப்பங்கள் ({n})", "6uMqL1": "கட்டணம் செலுத்தப்படாத", "7+Domh": "குறிப்புகள்", "7BX/yC": "கணக்கு மாற்றி", "7hp70g": "NIP-05", - "7xzTiH": "{action} பெறுநர்: {target}", "8/vBbP": "மறுபதிவுகள் ({n})", "89q5wc": "மறுப்பதிவுகளை உறுதி செய்யவும்", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "{n} ஸாட்கள் ஜாப் செய்", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "பெயர் மிக நீளமாக உள்ளது", "8v1NN+": "ஜோடிக்கும் சொற்றொடர்", + "8xNnhi": "Nostr Extension", "9+Ddtu": "அடுத்து", "9HU8vw": "பதில்", "9SvQep": "பின்தொடர்வுகள் {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "லைட்னிங் விலைப்பட்டியல்", "ADmfQT": "பெற்றோர்", "AGNz71": "Zap All {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "இந்தப் பதிவாளர் முடக்கப் பட்டுள்ளார்", "Adk34V": "உங்கள் சுய விவரத்தை அமைக்கவும்", "Ai8VHU": "ஸ்நார்ட் ரிலேயில் வரம்பற்ற குறிப்புகளை வைத்திரு", @@ -92,6 +107,7 @@ "BOr9z/": "ஸ்நோர்ட் என்பது ஆர்வமுள்ள மக்கள் தங்கள் ஓய்வு நேரத்தில் உருவாக்கிய திறந்த மூல திட்டம் ஆகும்", "BWpuKl": "புதுப்பி", "BcGMo+": "குறிப்புகள் உரை உள்ளடக்கத்தை வைத்திருக்கின்றன. இவற்றின் மிகவும் பிரபலமான பயன்பாடு \"ட்வீட் போன்ற\" செய்திகளைச் சேமிப்பதாகும்.", + "C1LjMx": "Lightning Donation", "C5xzTC": "உயர்தர", "C81/uG": "வெளியேறு", "C8HhVE": "பரிந்துரைக்கப்படும் பயனர்கள்", @@ -106,20 +122,23 @@ "DZzCem": "சமீபத்திய {n} குறிப்புகளைக் காட்டு", "DcL8P+": "ஆதரவாளர்", "Dh3hbq": "தானாக ஜாப்", + "Dn82AL": "Live", "DtYelJ": "பரிமாற்றம்", "E8a4yq": "சில பிரபலமான கணக்குகளைப் பின்தொடரவும்", "ELbg9p": "Data Providers", "EPYwm7": "உங்கள் தனிப்பட்ட சாவி உங்கள் கடவுச்சொல். இந்தச் சாவியை இழந்தால், உங்கள் கணக்கிற்கான அணுகலை இழப்பீர்கள்! அதை நகலெடுத்து பாதுகாப்பான இடத்தில் வைக்கவும். உங்கள் தனிப்பட்ட சாவியை மீட்டமைக்க வழி இல்லை.", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "முழுதளாவிய", "Ebl/B2": "{lang} இற்கு மொழிபெயர்க்கவும்", "EcZF24": "Custom Relays", "EcglP9": "சாவி", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "வாங்கு", "Eqjl5K": "ஸ்நார்ட் மற்றும் எங்கள் ஒருங்கிணைப்பு கூட்டாளர் அடையாளங்காட்டி மட்டுமே உங்களுக்கு வண்ணமயமான டொமைன் பெயரைத் தருகிறது, ஆனால் நீங்கள் மற்ற சேவைகளையும் பயன்படுத்தலாம்.", "F+B3x1": "உங்களுக்கு கூடுதல் விருப்பங்களை வழங்க நாங்கள் nostrplebs.com உடன் கூட்டு சேர்ந்துள்ளோம்", "F3l7xL": "கணக்கை சேர்", "FDguSC": "{n} ஜாப்கள்", - "FP+D3H": "ஜாப்களை அனுப்ப வேண்டிய LNURL", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "முடிந்தது!", "FSYL8G": "Trending Users", "FdhSU2": "இப்போது உரிமை கோரவும்", @@ -129,28 +148,32 @@ "G1BGCg": "பணப்பை தேர்வு", "GFOoEE": "உப்பு", "GL8aXW": "புக்மார்க்குகள் ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "உங்கள் ஸ்நார்ட் நாஸ்டர் முகவரியைப் பெறவும்", "Gcn9NQ": "மேக்னெட் இணைப்பு", "GspYR7": "{n} விருப்பமின்மை", + "Gxcr08": "Broadcast Event", "H+vHiz": "ஹெக்ஸ் சாவி..", "H0JBH6": "வெளியேறு", "H6/kLh": "ஆர்டர் செலுத்தப்பட்டது!", "HAlOn1": "பெயர்", - "HF4YnO": "நேரலையில் காண்க!", "HFls6j": "பெயர் பின்னர் கிடைக்கப் பெறும்", "HOzFdo": "ஒலியடக்கப்பட்டவை", "HWbkEK": "Clear cache and reload", "HbefNb": "திறந்த பணப்பை", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "ஸ்நார்ட் ஐப் பயன்படுத்தியதற்கு நன்றி, உங்களால் முடிந்தால் நன்கொடை அளிப்பதைக் கருத்தில் கொள்ளவும்.", "IEwZvs": "இந்தக் குறிப்பின் நிலையான பொறுத்தத்தை நிச்சயமாக நீக்க விரும்புகிறீர்களா?", "IKKHqV": "பின்தொடர்வுகள்", "INSqIz": "டுவிட்டர் பயனர்பெயர்...", "IUZC+0": "இதன் பொருள் என்னவென்றால் நீங்கள் உருவாக்கிய குறிப்புகளை யாராலும் மாற்ற முடியாது, மேலும் தாங்கள் படிக்கும் குறிப்புகள் உங்களால் உருவாக்கப்பட்டதா என்பதை அனைவரும் எளிதாகச் சரிபார்க்க முடியும்.", "Ig9/a1": "Sent {n} sats to {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "Trending Notes", "J+dIsA": "சந்தாக்கள்", "JCIgkj": "பயனர் பெயர்", + "JGrt9q": "Send sats to {name}", "JHEHCk": "ஜாப்கள் ({n})", "JPFYIM": "No lightning address", "JeoS4y": "மறுபதிவு", @@ -160,16 +183,19 @@ "K3r6DQ": "நீக்கு", "K7AkdL": "காண்பி", "KAhAcM": "LNDHub கட்டமைப்பை உள்ளிடவும்", - "KLo3SP": "காரணம்: {reason}", "KQvWvD": "நீக்கப்பட்டது", "KWuDfz": "நான் எனது சாவிகளை சேமித்து விட்டேன், தொடரவும்", "KahimY": "அறிந்திராத நிகழ்வு வகை: {kind}", "KoFlZg": "Enter mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "பிற இணைப்புகள்", + "LR1XjT": "Pin too short", "LXxsbk": "பெயரிலா", "LgbKvU": "கருத்து", "Lu5/Bj": "Open on Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "மெனுக்களை பிழை திருத்தவும்", "MBAYRO": "ஒவ்வொரு செய்தியிலும் சூழல் மெனுவில் \"IDஐ நகலெடு\" மற்றும் \"நிகழ்வு JSONஐ நகலெடு\" ஆகியவற்றைக் காட்டுகிறது", "MI2jkA": "கிடைக்கவில்லை:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "{item} வாங்கப் படுகிறது", "N2IrpM": "உறுதிசெய்", + "NAidKb": "Notifications", "NAuFNH": "உங்களிடம் ஏற்கனவே இந்த வகை சந்தா உள்ளது, புதுப்பிக்கவும் அல்லது பணம் செலுத்தவும்", "NNSu3d": "ட்விட்டர் பின்தொடரவுகளை இறக்குமதி செய்யவும்", "NdOYJJ": "ஹ்ம்ம் இங்கே எதுவும் இல்லை.. சில பரிந்துரைக்கப்பட்ட நாஸ்ட்ரிச்களைப் பின்பற்ற {newUsersPage} ஐப் பார்க்கவும்!", @@ -192,7 +219,6 @@ "OLEm6z": "அறியப்படாத உள்நுழைவு பிழை", "OQXnew": "உங்கள் சந்தா இன்னும் செயலில் உள்ளது, உங்களால் இன்னும் புதுப்பிக்க முடியாது", "ORGv1Q": "உருவாக்கியது", - "P04gQm": "இந்தக் குறிப்புக்கு அனுப்பப் படும் அனைத்து ஜாப்களும் கீழ்கண்ட LNURL ஆல் பெறப்படும்", "P61BTu": "நிகழ்வு JSONஐ நகலெடு", "P7FD0F": "கணினி (இயல்புநிலை)", "P7nJT9": "இன்றைய (UTC) மொத்தம்: {amount} சாட்கள்", @@ -207,7 +233,6 @@ "QxCuTo": "{name} மூலம் ஓவியம்", "Qxv0B2": "You currently have {number} sats in your zap pool.", "R/6nsx": "சந்தா", - "R1fEdZ": "ஜாப்களை முன்னனுப்பு", "R81upa": "நீங்கள் பின்தொடர்வோர்", "RDZVQL": "சோதிக்கவும்", "RahCRH": "காலாவதியாகிவிட்டது", @@ -217,19 +242,19 @@ "RoOyAh": "ரிலேகள்", "Rs4kCE": "புக்மார்க்", "RwFaYs": "வரிசைப்படுத்துக", + "SMO+on": "Send zap to {name}", "SOqbe9": "லைட்னிங் முகவரியை உள்ளிடவும்", "SP0+yi": "Buy Subscription", - "SX58hM": "படியெடு (நகலெடு)", "SYQtZ7": "LN முகவரி பதிலீடு", "ShdEie": "அனைத்தையும் படித்ததாகக் குறி", "Sjo1P4": "தனிப்பயன்", "Ss0sWu": "தொகை செலுத்தவும்", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", - "TMfYfY": "Cashu token", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "People", "UDYlxu": "நிலுவையிலுள்ள சந்தாக்கள்", - "ULotH9": "Amount: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "உங்கள் குறிப்பின் உள்ளடக்கத்தைக் காட்ட, உள்ளடக்க எச்சரிக்கையைப் பயனர்கள் ஏற்க வேண்டும்.", "Up5U7K": "முடக்கு", @@ -239,15 +264,16 @@ "VR5eHw": "பொது சாவி (npub/nprofile)", "VlJkSk": "{n} ஒலியடக்கப்பட்டவை", "VnXp8Z": "சுயவிவர படம்", - "VtPV/B": "நீட்டிப்புடன் உள்நுழையவும் (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "சாவிகள் எவ்வாறு வேலை செய்கின்றன?", "W1yoZY": "உங்களிடம் சந்தாக்கள் எதுவும் இல்லை என்பது போல் தெரிகிறது, நீங்கள் ஒன்றைப் பெறலாம் {link}", "W2PiAr": "{n} முடக்கப்பட்டவை", "W9355R": "ஒலியடக்கத்தை நீக்கு", "WONP5O": "நாஸ்டர் இல் உங்கள் ட்விட்டர் பின்தொடர்வதைக் கண்டறியவும் (தரவு வழங்கியது {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "உதாரணம். ஜாக்", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "File hosts", "XgWvGA": "எதிர்வினைகள்", "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", @@ -260,19 +286,21 @@ "Z4BMCZ": "இணைத்தல் சொற்றொடரை உள்ளிடவும்", "ZKORll": "இப்போது செயல்படுத்துக", "ZLmyG9": "பங்களிப்பாளர்கள்", - "ZUZedV": "லைட்னிங் நன்கொடை:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "Setup profile", "a5UPxh": "NIP-05 சரிபார்ப்பு சேவைகளை வழங்கும் டெவலப்பர்கள் மற்றும் தளங்களுக்கு நிதி உதவி செய்யுங்கள்", "a7TDNm": "Notes will stream in real time into global and notes tab", "aWpBzj": "மேலும் காட்டு", "b12Goz": "நினைவூட்டி", "b5vAk0": "உங்கள் பயனர் அடையாளம் மின்னல் முகவரிபோல் செயல்படும் மற்றும் நீங்கள் தேர்ந்தெடுத்த LNURL அல்லது மின்னல் முகவரிக்குத் திருப்பிவிடும்", + "bLZL5a": "Get Address", "bQdA2k": "உணர்திறன் மிக்க உள்ளடக்கம்", "bep9C3": "பொது சாவி", "bfvyfs": "Anon", "brAXSu": "ஒரு பயனர்பெயரைத் தேர்ந்தெடுக்கவும்", "bxv59V": "இப்போது", "c+oiJe": "நீட்டிப்பை நிறுவவும்", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "உங்களின் NIP-05 ஆர்டரைப் பற்றி விசாரணை இருந்தால், {link} க்கு DM செய்யவும்", "c3g2hL": "Broadcast Again", "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", @@ -287,6 +315,7 @@ "d7d0/x": "LN முகவரி", "dOQCL8": "காட்சி பெயர்", "e61Jf3": "விரைவில் வருகிறது", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "அனைத்தையும் படித்ததாகக் குறிக்கவும்", "eHAneD": "எதிர்வினை ஈமோஜி", "eJj8HD": "உங்கள் கணக்கைச் சரிபார்க்கப் பட்டதாக்கவும்", @@ -297,6 +326,7 @@ "fWZYP5": "நிலையாகப் பொருத்தப் பட்டவை", "filwqD": "படி", "flnGvv": "உங்கள் மனதில் என்ன இருக்கிறது?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "சேமிக்கப்பட்டது", "g5pX+a": "தகவல்", "g985Wp": "வாக்கை அனுப்ப முடியவில்லை", @@ -310,9 +340,9 @@ "h8XMJL": "பேட்ஜ்கள்", "hK5ZDk": "உலகம்", "hMzcSq": "அஞ்சல்கள்", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "ஆதரவு", "hicxcO": "பதில்களைக் காட்டு", + "hmZ3Bz": "Media", "hniz8Z": "இங்கே", "i/dBAR": "Zap Pool", "iCqGww": "எதிர்வினைகள் ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL மொழிபெயர்ப்புகள்", "iGT1eE": "போலி கணக்குகள் உங்களைப் போல் நடிப்பதை தடுக்கவும்", "iNWbVV": "பயனர்பெயர்", - "iUsU2x": "Mint: {url}", "iXPL0Z": "பாதுகாப்பற்ற இணைப்பில் தனிப்பட்ட சாவியுடன் உள்நுழைய முடியாது, அதற்குப் பதிலாக நாஸ்டர் விசை மேலாளர் நீட்டிப்பைப் பயன்படுத்தவும்", "ieGrWo": "பின்தொடர்", "itPgxd": "சுயவிவரம்", @@ -381,9 +410,11 @@ "qMx1sA": "இயல்புநிலை ஜாப் தொகை", "qUJTsT": "முடக்கப்பட்டவை", "qdGuQo": "உங்கள் தனிப்பட்ட சாவி (இதை யாருடனும் பகிர வேண்டாம்)", + "qfmMQh": "This note has been muted", "qkvYUb": "சுயவிவரத்தில் சேர்க்கவும்", "qmJ8kD": "மொழிபெயர்ப்பு தோல்வியடைந்தது", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "மென்பொருள்", "r5srDR": "பணப்பையின் கடவுச்சொல்லை உள்ளிடவும்", "rT14Ow": "ரிலேகளைச் சேர்க்கவும்", @@ -392,7 +423,10 @@ "rmdsT4": "{n} நாட்கள்", "rrfdTe": "பிட்காயினால் பயன்படுத்தப்படும் அதே தொழில்நுட்பம் இது மிகவும் பாதுகாப்பானது என்று நிரூபிக்கப்பட்டுள்ளது.", "rudscU": "பித்தொடர்வுகளை ஏற்றுவதில் தோல்வி, பிறகு முயற்சிக்கவும்", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "ட்விட்டரைப் போன்ற அனுபவத்தைப் பெறும் வகையில் ஸ்நார்ட் வடிவமைக்கப்பட்டுள்ளது.", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "பெயர் குந்துதல் மற்றும் ஆள்மாறாட்டம் அனுமதிக்கப்படாது. இந்த விதியை மீறியதற்காக உங்கள் பயனர் அடையாளத்தை(உங்கள் கணக்கு அல்ல - அதை யாரும் எடுத்துச் செல்ல முடியாது) துண்டிக்கும் உரிமையை ஸ்நார்ட் மற்றும் எங்கள் கூட்டாளர்களுக்கு உள்ளது.", "tOdNiY": "இருள்", "th5lxp": "Send note to a subset of your write relays", @@ -400,13 +434,12 @@ "ttxS0b": "ஆதரவாளர் பேட்ஜ்", "u/vOPu": "கட்டணத்திற்கு உரியது", "u4bHcR": "குறியீட்டை இங்கே பார்க்கவும்: {link}", - "uD/N6c": "{target} {n} ஸாட்கள் ஜாப் செய்", "uSV4Ti": "மறுபதிவுகள் கைமுறையாக உறுதிப்படுத்தப்பட வேண்டும்", + "uc0din": "Send sats splits to", "usAvMr": "சுயவிவரத்தைத் திருத்து", "ut+2Cd": "கூட்டாளர் அடையாளங்காட்டியைப் பெறுங்கள்", "v8lolG": "பேசத் தொடங்குக", "vOKedj": "{n,plural,=1{& {n} நபர்} other{& {n} நபர்கள்}}", - "vU71Ez": "{wallet} மூலம் பணம் செலுத்துதல்", "vZ4quW": "NIP-05 என்பது DNS அடிப்படையிலான சரிபார்ப்பு விவரக்குறிப்பாகும், இது உங்களை உண்மையான பயனராகச் சரிபார்க்க உதவுகிறது.", "vhlWFg": "வாக்கெடுப்பு விருப்பங்கள்", "vlbWtt": "விலையின்றிப் பெறுக", @@ -414,14 +447,16 @@ "vxwnbh": "அனைத்து பதியப்பட்ட நிகழ்வுகளிலும் மாற்றம் செய்யத் தேவைப்படும் பணிச் சுமை", "wEQDC6": "திருத்து", "wLtRCF": "உங்கள் சாவி", + "wSZR47": "Submit", "wWLwvh": "அநாமதேய", "wYSD2L": "நாஸ்டர் முகவரிகள்", "wih7iJ": "பெயர் முடக்கப் பட்டுள்ளது", + "wofVHy": "Moderation", "wqyN/i": "{link} இல் {service} பற்றிய கூடுதல் தகவலைக் கண்டறியவும்", "wtLjP6": "IDஐ நகல் எடு", "x/Fx2P": "நீங்கள் பயன்படுத்தும் சேவைக்குக் நன்கொடை அளிக்கலாம் - உங்கள் zap-களின் ஒரு பகுதியை நிதியத்தில் சேர்ப்பதன் வழியாக!", - "x/q8d5": "இந்தக் குறிப்பு உணர்திறன் வாய்ந்ததாகக் குறிக்கப்பட்டுள்ளது, வெளிப்படுத்த இங்கே கிளிக் செய்யவும்", "x82IOl": "ஒலியடக்கு", + "xIcAOU": "Votes by {type}", "xIoGG9": "செல்", "xJ9n2N": "உங்கள் பொது சாவி", "xKflGN": "நாஸ்டர் இல் {username} ஐப் பின்தொடர்வோர்", @@ -433,11 +468,13 @@ "y1Z3or": "மொழி", "yCLnBC": "LNURL அல்லது லைட்னிங் முகவரி", "yCmnnm": "முழுதளாவியதைப் படிக்கும் மூலம்", + "zCb8fX": "Weight", "zFegDD": "தொடர்பு", "zINlao": "உரிமையாளர்", "zQvVDJ": "அனைத்தும்", "zcaOTs": "ஜாப் தொகை ஸாட்களில்", "zjJZBd": "நீங்கள் தயார்!", "zonsdq": "LNURL சேவையை ஏற்றுவதில் தோல்வி", - "zvCDao": "சமீபத்திய குறிப்புகளைத் தானாகக் காட்டு" + "zvCDao": "சமீபத்திய குறிப்புகளைத் தானாகக் காட்டு", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/th_TH.json b/packages/app/src/translations/th_TH.json index 4cd95741..1c6b5e4e 100644 --- a/packages/app/src/translations/th_TH.json +++ b/packages/app/src/translations/th_TH.json @@ -3,6 +3,7 @@ "+PzQ9Y": "จ่ายเลยตอนนี้", "+Vxixo": "Secret Group Chat", "+aZY2h": "รูปแบบการ Zap", + "+tShPg": "following", "+vA//S": "เข้าสู่ระบบ", "+vIQlC": "โปรดตรวจสอบให้แน่ใจว่าคุณได้ทำการบันทึกรหัสผ่านต่อไปนี้ไว้เรียบร้อยแล้ว เพื่อให้สะดวกต่อจัดการในอนาคต", "+vVZ/G": "เชื่อมต่อ", @@ -11,6 +12,7 @@ "/JE/X+": "การสนับสนุนบัญชี", "/PCavi": "สาธารณะ", "/RD0e2": "Nostr ใช้เทคโนโลยีลายเซ็นดิจิทัลเพื่อแสดงบันทึกหลักฐานการปลอมแปลง ซึ่งสามารถทำซ้ำได้อย่างปลอดภัยไปยังรีเลย์จำนวนมาก เพื่อกระจายการจัดเก็บเนื้อหาที่ซ้ำซ้อนของคุณ", + "/Xf4UW": "Send anonymous usage metrics", "/d6vEc": "ทำให้ค้นหาและแบ่งปันโปรไฟล์ได้ง่ายขึ้น", "/n5KSF": "{n} ms", "00LcfG": "Load more", @@ -19,6 +21,7 @@ "0BUTMv": "ค้นหา...", "0jOEtS": "LNURL ไม่ถูกต้อง", "0mch2Y": "ชื่อมีอักขระที่ไม่อนุญาตให้ใช้", + "0uoY11": "Show Status", "0yO7wF": "{n} วินาที", "1A7TZk": "Snort คืออะไรและทำงานอย่างไร?", "1Mo59U": "คุณแน่ใจหรือว่าต้องการลบโน้ตนี้ออกจากบุ๊คมาร์ค?", @@ -34,6 +37,7 @@ "2a2YiP": "บุ๊คมาร์ค {n}", "2k0Cv+": "ไม่ถูกใจ ({n})", "2ukA4d": "{n} ชั่วโมง", + "3KNMbJ": "Articles", "3Rx6Qo": "ขั้นสูง", "3cc4Ct": "สว่าง", "3gOsZq": "แปลภาษา", @@ -42,6 +46,7 @@ "3tVy+Z": "ผู้ติดตาม {n}", "3xCwbZ": "หรือ", "3yk8fB": "กระเป๋า", + "40VR6s": "Nostr Connect", "450Fty": "ไม่มี", "47FYwb": "ยกเลิก", "4IPzdn": "นักพัฒนาหลัก", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs เป็นหนึ่งในผู้ให้บริการ NIP-05 รายแรก ๆ ในพื้นที่นี้ และเสนอชุดโดเมนที่ดีในราคาที่สมเหตุสมผล", "4Z3t5i": "ใช้ imgproxy เพื่อบีบอัดรูปภาพ", "4rYCjn": "หมายเหตุถึงตนเอง", + "5BVs2e": "zap", + "5CB6zB": "Zap Splits", "5JcXdV": "สร้างบัญชี", "5oTnfy": "รหัสการซื้อ", "5rOdPG": "เมื่อคุณตั้งค่า extension ตัวจัดการ key และสร้าง key แล้ว คุณสามารถติดตามผู้ใช้ใหม่ของเราเพื่อตั้งค่าโปรไฟล์ของคุณและช่วยคุณค้นหาคนที่น่าสนใจบน Nostr เพื่อติดตาม", "5u6iEc": "แปลงเป็น Pubkey", "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", "5ykRmX": "ส่ง zap", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "อิมเมจพร็อกซีจาก {host} ไม่สำเร็จ คลิกที่นี่เพื่อโหลดโดยตรง", + "6OSOXl": "Reason: {reason}", "6Yfvvp": "รับ identifier", + "6bgpn+": "Not all clients support this, you may still receive some zaps as if zap splits was not configured", "6ewQqw": "ถูกใจ ({n})", "6uMqL1": "ยังไม่ได้ชำระ", "7+Domh": "โน้ต", "7BX/yC": "สลับบัญชี", "7hp70g": "NIP-05", - "7xzTiH": "{action} ถึง {target}", "8/vBbP": "รีโพสต์ ({n})", "89q5wc": "ยืนยันรีโพสต์", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "Zap {n} sats", + "8Rkoyb": "Recipient", + "8Y6bZQ": "Invalid zap split: {input}", "8g2vyB": "ชื่อยาวเกินไป", "8v1NN+": "Pairing phrase", + "8xNnhi": "Nostr Extension", "9+Ddtu": "ถัดไป", "9HU8vw": "ตอบกลับ", "9SvQep": "ติดตาม {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "Lightning Invoice", "ADmfQT": "ตอบกลับจาก", "AGNz71": "Zap ทั้งหมด {n} sats", + "AN0Z7Q": "Muted Words", "ASRK0S": "ผู้เขียนถูกปิดการมองเห็น", "Adk34V": "ตั้งค่าโปรไฟล์ของคุณ", "Ai8VHU": "สามารถเก็บโน้ตได้อย่างไม่จำกัดบน Snort relay", @@ -92,6 +107,7 @@ "BOr9z/": "Snort เป็นโครงการโอเพนซอร์สที่สร้างขึ้นโดยผู้คนที่หลงใหลในเวลาว่างของพวกเขา", "BWpuKl": "อัปเดต", "BcGMo+": "ข้อความของโน๊ตต่าง ๆ ถูกใช้งานมากที่สุดในรูปแบบของข้อความเหมือนอย่าง \"tweet\"", + "C1LjMx": "Lightning Donation", "C5xzTC": "พรีเมี่ยม", "C81/uG": "ออกจากระบบ", "C8HhVE": "แนะนำให้ติดตาม", @@ -106,20 +122,23 @@ "DZzCem": "แสดงโน้ตล่าสุด {n} รายการ", "DcL8P+": "ผู้สนับสนุน", "Dh3hbq": "Zap โดยอัตโนมัติ", + "Dn82AL": "Live", "DtYelJ": "โอนย้าย", "E8a4yq": "ติดตามบัญชียอดนิยมบางบัญชี", "ELbg9p": "ผู้ให้บริการข้อมูล", "EPYwm7": "Private key ของคุณคือรหัสผ่านของคุณ ถ้าคุณทำมันหาย คุณจะไม่สามารถเข้าถึงบัญชีได้! คัดลอกและเก็บรักษามันในที่ที่ปลอดภัย และไม่มีทางที่จะรีเซ็ต private key ของคุณได้", + "EQKRE4": "Show badges on profile pages", "EWyQH5": "Global", "Ebl/B2": "แปลจาก {lang}", "EcZF24": "รีเลย์ที่กำหนดเอง", "EcglP9": "Key", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "ซื้อ", "Eqjl5K": "เฉพาะ Snort และพันธมิตรของเราเท่านั้นที่จะมอบชื่อโดเมนสีสันสดใสให้คุณ แต่คุณสามารถใช้บริการอื่น ๆ ได้ด้วย", "F+B3x1": "นอกจากนี้เรายังร่วมมือกับ nostrplebs.com เพื่อให้คุณมีทางเลือกมากขึ้น", "F3l7xL": "เพิ่มบัญชี", "FDguSC": "{n} Zaps", - "FP+D3H": "LNURL เพื่อส่งต่อ zaps ไปที่", + "FMfjrl": "Show status messages on profile pages", "FS3b54": "เรียบร้อย!", "FSYL8G": "Trending Users", "FdhSU2": "รับทันที", @@ -129,28 +148,32 @@ "G1BGCg": "เลือก wallet", "GFOoEE": "Salt", "GL8aXW": "บุ๊คมาร์ค ({n})", + "GQPtfk": "Join Stream", "GSye7T": "Lightning Address", "GUlSVG": "รับ Snort nostr address ของคุณ", "Gcn9NQ": "Magnet Link", "GspYR7": "{n} ไม่ถูกใจ", + "Gxcr08": "Broadcast Event", "H+vHiz": "Hex Key..", "H0JBH6": "ออกจากระบบ", "H6/kLh": "ได้รับการชำระเงินเรียบร้อย!", "HAlOn1": "ชื่อ", - "HF4YnO": "Watch Live!", "HFls6j": "ชื่อจะสามารถใช้ได้ในภายหลัง", "HOzFdo": "ปิดการมองเห็น", "HWbkEK": "ล้างแคชและดาวน์โหลดใหม่อีกครั้ง", "HbefNb": "เปิด Wallet", + "HhcAVH": "You don't follow this person, click here to load media from {link}, or update your preferences to always load media from everybody.", "IDjHJ6": "ของคุณที่ใช้ Snort โปรดสนับสนุนเราถ้าคุณสามารถทำได้", "IEwZvs": "คุณแน่ใจแล้วใช่มั้ยว่าต้องการลบโน้ตนี้?", "IKKHqV": "Follows", "INSqIz": "ชื่อผู้ใช้ Twitter...", "IUZC+0": "หมายความว่าไม่มีใครสามารถแก้ไขโน้ตที่คุณสร้างได้ และทุกคนสามารถตรวจสอบได้อย่างง่ายดายว่าโน้ตที่พวกเขากำลังอ่านนั้นสร้างโดยคุณ", "Ig9/a1": "ส่ง {n} sats ถึง {name}", + "IoQq+a": "Click here to load anyway", "Ix8l+B": "โน๊ตที่กำลังมาแรง", "J+dIsA": "การสมัครสมาชิก", "JCIgkj": "ชื่อผู้ใช้", + "JGrt9q": "Send sats to {name}", "JHEHCk": "Zaps ({n})", "JPFYIM": "ไม่มี lightning address นี้", "JeoS4y": "Repost", @@ -160,16 +183,19 @@ "K3r6DQ": "ลบ", "K7AkdL": "แสดง", "KAhAcM": "ใส่ LNDHub config", - "KLo3SP": "เหตุผล: {reason}", "KQvWvD": "ลบทิ้งแล้ว", "KWuDfz": "ฉันได้บันทึก key ของฉันและพร้อมจะดำเนินการต่อ", "KahimY": "ไม่รู้จัก event kind: {kind}", "KoFlZg": "ใส่ mint URL", + "KtsyO0": "Enter Pin", "LF5kYT": "การเชื่อมต่ออื่น ๆ", + "LR1XjT": "Pin too short", "LXxsbk": "ไม่ระบุตัวตน", "LgbKvU": "ความคิดเห็น", "Lu5/Bj": "เปิด Zapstr", "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "LwYmVi": "Zaps on this note will be split to the following users.", + "M10zFV": "Nostr Connect", "M3Oirc": "เมนูการแก้ปัญหา", "MBAYRO": "แสดง \"Copy ID\" และ \"Copy Event JSON\" ในเมนูของแต่ละข้อความ", "MI2jkA": "ไม่พร้อมใช้งาน:", @@ -180,6 +206,7 @@ "MuVeKe": "Buy nostr address", "MzRYWH": "กำลังซื้อ {item}", "N2IrpM": "ยืนยัน", + "NAidKb": "Notifications", "NAuFNH": "คุณได้สมัครสมาชิกประเภทนี้แล้ว โปรดต่ออายุหรือชำระเงิน", "NNSu3d": "นำเข้าผู้ติดตามจากทวิตเตอร์", "NdOYJJ": "อืม ไม่มีอะไรที่นี่.. โปรดตรวจสอบ {newUsersPage} เพื่อติดตาม nostrich's ที่แนะนำ!", @@ -192,7 +219,6 @@ "OLEm6z": "พบข้อผิดพลาดการเข้าสู่ระบบที่ไม่ทราบสาเหตุ", "OQXnew": "การสมัครสมาชิกของคุณยังคงใช้งานได้ คุณยังไม่จำเป็นต้องต่ออายุ", "ORGv1Q": "สร้าง", - "P04gQm": "Zaps ทั้งหมดที่ส่งไปยังโน๊ตนี้จะได้รับโดย LNURL ต่อไปนี้", "P61BTu": "คัดลอก Event JSON", "P7FD0F": "ระบบ (ค่าเริ่มต้น)", "P7nJT9": "ยอดรวมวันนี้ (UTC): {amount} sats", @@ -207,7 +233,6 @@ "QxCuTo": "รังสรรค์โดย {name}", "Qxv0B2": "ขณะนี้มี {number} sats ใน zappool ของคุณ", "R/6nsx": "สมัครสมาชิก", - "R1fEdZ": "ส่งต่อ zaps", "R81upa": "People you follow", "RDZVQL": "ตรวจสอบ", "RahCRH": "หมดอายุแล้ว", @@ -217,19 +242,19 @@ "RoOyAh": "รีเลย์", "Rs4kCE": "บุ๊คมาร์ก", "RwFaYs": "การจัดเรียง", + "SMO+on": "Send zap to {name}", "SOqbe9": "อัพเดต Lightning Address", "SP0+yi": "ซื้อการสมัครสมาชิก", - "SX58hM": "คัดลอก", "SYQtZ7": "LN Address Proxy", "ShdEie": "Mark all read", "Sjo1P4": "ปรับแต่งเอง", "Ss0sWu": "ชำระเงินทันที", + "StKzTE": "The author has marked this note as a sensitive topic", "TDR5ge": "สื่อในโน้ตจะแสดงโดยอัตโนมัติสำหรับบุคคลที่เลือกเท่านั้น คนอื่น ๆ จะแสดงเฉพาะลิงก์", - "TMfYfY": "โทเคน Cashu", + "TP/cMX": "Ended", "TpgeGw": "Hex Salt..", "Tpy00S": "ผู้คน", "UDYlxu": "รอการสมัครสมาชิก", - "ULotH9": "จำนวน: {amount} sats", "UT7Nkj": "New Chat", "UUPFlt": "ผู้ใช้จะต้องยอมรับคำเตือนเพื่อแสดงเนื้อหาในโน้ตของคุณ", "Up5U7K": "บล็อก", @@ -239,15 +264,16 @@ "VR5eHw": "Public key (npub/nprofile)", "VlJkSk": "ปิดการมองเห็น {n} บัญชี", "VnXp8Z": "อวตาร", - "VtPV/B": "เข้าสู่ระบบด้วย Extension (NIP-07)", "VvaJst": "View Wallets", "Vx7Zm2": "Keys ทำงานอย่างไร?", "W1yoZY": "ดูเหมือนว่าคุณไม่มีการสมัครสมาชิกใด ๆ คุณสามารถสมัครรับข้อมูลได้ {link}", "W2PiAr": "{n} บล็อค", "W9355R": "เปิดการมองเห็น", "WONP5O": "ตามหาคนที่คุณติดตามบน twitter ใน Nostr (ข้อมูลเหล่านี้รับมาจาก {provider})", + "WvGmZT": "npub / nprofile / nostr address", "WxthCV": "ตัวอย่างเช่น Jack", "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XECMfW": "Send usage metrics", "XICsE8": "สถานที่เก็บไฟล์", "XgWvGA": "Reactions", "Xopqkl": "จํานวน zap เริ่มต้นของคุณคือ {number} sats ค่าตัวอย่างคํานวณจากสิ่งนี้", @@ -260,19 +286,21 @@ "Z4BMCZ": "ใส่ pairing phrase", "ZKORll": "เปิดใช้งานทันที", "ZLmyG9": "ผู้มีส่วนร่วม", - "ZUZedV": "บริจาคด้วย Linghtning:", + "ZS+jRE": "Send zap splits to", "Zr5TMx": "ตั้งค่าโปรไฟล์", "a5UPxh": "สนับสนุนนักพัฒนาและแฟลตฟอร์มที่ให้บริการ NIP-05", "a7TDNm": "โน้ตจะแสดงแบบเรียลไทม์บนหน้า global และ โน้ต", "aWpBzj": "ดูเพิ่มเติม", "b12Goz": "Mnemonic", "b5vAk0": "สิ่งนี้เหมือนกับ Lightning Address และจะเปลี่ยนการเชื่อมต่อไปที่ LNURL หรือ Lightning Addresses ที่คุณเลือก", + "bLZL5a": "Get Address", "bQdA2k": "เนื้อหาที่ละเอียดอ่อน", "bep9C3": "Public Key", "bfvyfs": "Anon", "brAXSu": "เลือกชื่อบัญชีผู้ใช้", "bxv59V": "เมื่อสักครู่", "c+oiJe": "ติดตั้ง Extension", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "หากคุณมีคําถามใด ๆ เกี่ยวกับคําสั่งซื้อ NIP-05 ของคุณ โปรดติดต่อที่ {link}", "c3g2hL": "เผยแพร่อีกครั้ง", "cFbU1B": "ใช้ Alby? ไปที่{link} เพื่อรับ NWC config ของคุณ", @@ -287,6 +315,7 @@ "d7d0/x": "LN address", "dOQCL8": "ชื่อที่แสดง", "e61Jf3": "เร็ว ๆ นี้", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "ทำเครื่องหมายเป็นอ่านแล้วทั้งหมด", "eHAneD": "Reaction emoji", "eJj8HD": "รับการยืนยัน!", @@ -297,6 +326,7 @@ "fWZYP5": "ปักหมุดข้อความแล้ว", "filwqD": "อ่าน", "flnGvv": "คุณกำลังคิดอะไรอยู่?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "บันทึกแล้ว", "g5pX+a": "เกี่ยวกับ", "g985Wp": "การโหวตไม่สำเร็จ", @@ -310,9 +340,9 @@ "h8XMJL": "เหรียญตรา", "hK5ZDk": "โลก", "hMzcSq": "ข้อความ", - "hWSp+B": "Nostr Connect (NIP-46)", "hY4lzx": "สนับสนุน", "hicxcO": "แสดงการตอบกลับ", + "hmZ3Bz": "Media", "hniz8Z": "ที่นี่", "i/dBAR": "Zap Pool", "iCqGww": "Reactions ({n})", @@ -320,7 +350,6 @@ "iEoXYx": "แปล DeepL", "iGT1eE": "ป้องกันไม่ให้บัญชีปลอมเลียนแบบคุณ", "iNWbVV": "จัดการ", - "iUsU2x": "Mint: {url}", "iXPL0Z": "ไม่สามารถเข้าสู่ระบบด้วย private key ในการเชื่อมต่อที่ไม่ปลอดภัย โปรดใช้ Nostr key manager extension ในการจัดการ", "ieGrWo": "ติดตาม", "itPgxd": "โปรไฟล์", @@ -381,9 +410,11 @@ "qMx1sA": "จำนวน zap พื้นฐาน", "qUJTsT": "บล็อค", "qdGuQo": "Private key ของคุณคือ(อย่าส่งข้อมูลนี้ให้ผู้อื่น)", + "qfmMQh": "This note has been muted", "qkvYUb": "เพิ่มไปยังโปรไฟล์", "qmJ8kD": "แปลล้มเหลว", "qtWLmt": "Like", + "qz9fty": "Incorrect pin", "r3C4x/": "ซอฟต์แวร์", "r5srDR": "โปรดใส่รหัส wallet", "rT14Ow": "เพิ่มรีเลย์", @@ -392,7 +423,10 @@ "rmdsT4": "{n} days", "rrfdTe": "นี่คือเทคโนโลยีเดียวกันกับที่บิตคอยน์ใช้และได้รับการพิสูจน์แล้วว่าปลอดภัยอย่างยิ่ง", "rudscU": "ไม่สามารถโหลดผู้ที่คุณติดตามได้ โปรดลองอีกครั้งภายหลัง", + "sKDn4e": "Show Badges", + "sUNhQE": "user", "sWnYKw": "Snort ได้รับการออกแบบให้มีประสบการณ์คล้ายกับ Twitter", + "sZQzjQ": "Failed to parse zap split: {input}", "svOoEH": "ไม่อนุญาติให้มีการตั้งชื่อตัดหน้าหรือการปลอมเป็นคนอื่น Snort และพันธมิตรของเราขอสงวนสิทธิ์ในการยกเลิกการเข้าถึงของคุณ (ไม่ใช่บัญชีของคุณ - ไม่มีใครสามารถเอาสิ่งนั้นออกไปได้) สำหรับการละเมิดกฎนี้", "tOdNiY": "มืด", "th5lxp": "ส่งโน้ตของคุณไปยังชุดของรีเลย์ที่คุณอนุญาติให้เขียน", @@ -400,13 +434,12 @@ "ttxS0b": "เหรียญตราของผู้สนับสนุน", "u/vOPu": "ชำระเงินแล้ว", "u4bHcR": "ตรวจสอบ code ที่นี่: {link}", - "uD/N6c": "Zap {target} {n} sats", "uSV4Ti": "การรีโพสจะต้องได้รับการยืนยันด้วยตนเอง", + "uc0din": "Send sats splits to", "usAvMr": "แก้ไขโปร์ไฟล์", "ut+2Cd": "รับ partner identifier", "v8lolG": "Start chat", "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", - "vU71Ez": "จ่ายด้วย {wallet}", "vZ4quW": "จ่ายด้วย {wallet}", "vhlWFg": "ตัวเลือกโพลล์", "vlbWtt": "Get a free one", @@ -414,14 +447,16 @@ "vxwnbh": "Amount of work to apply to all published events", "wEQDC6": "แก้ไข", "wLtRCF": "Key ของคุณ", + "wSZR47": "Submit", "wWLwvh": "ไม่ระบุชื่อ", "wYSD2L": "Nostr Adddress", "wih7iJ": "ชื่อนี้ถูกบล๊อค", + "wofVHy": "Moderation", "wqyN/i": "ค้นหาข้อมูลเพิ่มเติมเกี่ยวกับ {service} ได้ที่ {link}", "wtLjP6": "คัดลอก ID", "x/Fx2P": "จัดหาเงินทุนให้กับบริการที่คุณใช้โดยแบ่งส่วนของ zaps ทั้งหมดของคุณออกเป็นกองทุน!", - "x/q8d5": "โน้ตนี้นี้ถูกทำเครื่องหมายว่าละเอียดอ่อน คลิกที่นี่เพื่อเปิด", "x82IOl": "ปิดการมองเห็น", + "xIcAOU": "Votes by {type}", "xIoGG9": "ไปยัง", "xJ9n2N": "Public key ของคุณ", "xKflGN": "ติดตาม {username}''s ได้ที่ Nostr", @@ -433,11 +468,13 @@ "y1Z3or": "ภาษา", "yCLnBC": "LNURL หรือ Lightning Address", "yCmnnm": "อ่านเธรดโลกจาก", + "zCb8fX": "Weight", "zFegDD": "รายชื่อผู้ติดต่อ", "zINlao": "เจ้าของ", "zQvVDJ": "ทั้งหมด", "zcaOTs": "จำนวน zap ในหน่วย sats", "zjJZBd": "คุณพร้อมแล้ว!", "zonsdq": "ไม่สามารถโหลดบริการ LNURL", - "zvCDao": "โชว์โน้ตแบบย่ออัตโนมัติ" + "zvCDao": "โชว์โน้ตแบบย่ออัตโนมัติ", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/tr_TR.json b/packages/app/src/translations/tr_TR.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/tr_TR.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/uk_UA.json b/packages/app/src/translations/uk_UA.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/uk_UA.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/vi_VN.json b/packages/app/src/translations/vi_VN.json new file mode 100644 index 00000000..e1b96dd4 --- /dev/null +++ b/packages/app/src/translations/vi_VN.json @@ -0,0 +1,446 @@ +{ + "+D82kt": "Are you sure you want to repost: {id}", + "+PzQ9Y": "Payout Now", + "+Vxixo": "Secret Group Chat", + "+aZY2h": "Zap Type", + "+vA//S": "Logins", + "+vIQlC": "Please make sure to save the following password in order to manage your handle in the future", + "+vVZ/G": "Connect", + "+xliwN": "{name} reposted", + "/4tOwT": "Skip", + "/JE/X+": "Account Support", + "/PCavi": "Public", + "/RD0e2": "Nostr uses digital signature technology to provide tamper proof notes which can safely be replicated to many relays to provide redundant storage of your content.", + "/d6vEc": "Make your profile easier to find and share", + "/n5KSF": "{n} ms", + "00LcfG": "Load more", + "08zn6O": "Export Keys", + "0Azlrb": "Manage", + "0BUTMv": "Search...", + "0jOEtS": "Invalid LNURL", + "0mch2Y": "name has disallowed characters", + "0yO7wF": "{n} secs", + "1A7TZk": "What is Snort and how does it work?", + "1Mo59U": "Are you sure you want to remove this note from bookmarks?", + "1R43+L": "Enter Nostr Wallet Connect config", + "1c4YST": "Connected to: {node} 🎉", + "1iQ8GN": "Toggle Preview", + "1nYUGC": "{n} Following", + "1udzha": "Conversations", + "2/2yg+": "Add", + "25V4l1": "Banner", + "2IFGap": "Donate", + "2LbrkB": "Enter password", + "2a2YiP": "{n} Bookmarks", + "2k0Cv+": "Dislikes ({n})", + "2ukA4d": "{n} hours", + "3Rx6Qo": "Advanced", + "3cc4Ct": "Light", + "3gOsZq": "Translators", + "3qnJlS": "You are voting with {amount} sats", + "3t3kok": "{n,plural,=1{{n} new note} other{{n} new notes}}", + "3tVy+Z": "{n} Followers", + "3xCwbZ": "OR", + "3yk8fB": "Wallet", + "450Fty": "None", + "47FYwb": "Cancel", + "4IPzdn": "Primary Developers", + "4L2vUY": "Your new NIP-05 handle is:", + "4OB335": "Dislike", + "4Vmpt4": "Nostr Plebs is one of the first NIP-05 providers in the space and offers a good collection of domains at reasonable prices", + "4Z3t5i": "Use imgproxy to compress images", + "4rYCjn": "Note to Self", + "5BVs2e": "zap", + "5JcXdV": "Create Account", + "5oTnfy": "Buy Handle", + "5rOdPG": "Once you setup your key manager extension and generated a key, you can follow our new users flow to setup your profile and help you find some interesting people on Nostr to follow.", + "5u6iEc": "Transfer to Pubkey", + "5vMmmR": "Usernames are not unique on Nostr. The nostr address is your unique human-readable address that is unique to you upon registration.", + "5ykRmX": "Send zap", + "65BmHb": "Failed to proxy image from {host}, click here to load directly", + "6Yfvvp": "Get an identifier", + "6ewQqw": "Likes ({n})", + "6uMqL1": "Unpaid", + "7+Domh": "Notes", + "7BX/yC": "Account Switcher", + "7hp70g": "NIP-05", + "7xzTiH": "{action} to {target}", + "8/vBbP": "Reposts ({n})", + "89q5wc": "Confirm Reposts", + "8QDesP": "Zap {n} sats", + "8g2vyB": "name too long", + "8v1NN+": "Pairing phrase", + "9+Ddtu": "Next", + "9HU8vw": "Reply", + "9SvQep": "Follows {n}", + "9WRlF4": "Send", + "9gqH2W": "Login", + "9pMqYs": "Nostr Address", + "9wO4wJ": "Lightning Invoice", + "ADmfQT": "Parent", + "AGNz71": "Zap All {n} sats", + "ASRK0S": "This author has been muted", + "Adk34V": "Setup your Profile", + "Ai8VHU": "Unlimited note retention on Snort relay", + "AkCxS/": "Reason", + "AnLrRC": "Non-Zap", + "AyGauy": "Login", + "B4C47Y": "name too short", + "B6+XJy": "zapped", + "B6H7eJ": "nsec, npub, nip-05, hex", + "BGCM48": "Write access to Snort relay, with 1 year of event retention", + "BOUMjw": "No nostr users found for {twitterUsername}", + "BOr9z/": "Snort is an open source project built by passionate people in their free time", + "BWpuKl": "Update", + "BcGMo+": "Notes hold text content, the most popular usage of these notes is to store \"tweet like\" messages.", + "C5xzTC": "Premium", + "C81/uG": "Logout", + "C8HhVE": "Suggested Follows", + "CHTbO3": "Failed to load invoice", + "CVWeJ6": "Trending People", + "CmZ9ls": "{n} Muted", + "CsCUYo": "{n} sats", + "Cu/K85": "Translated from {lang}", + "D+KzKd": "Automatically zap every note when loaded", + "D3idYv": "Settings", + "DKnriN": "Send sats", + "DZzCem": "Show latest {n} notes", + "DcL8P+": "Supporter", + "Dh3hbq": "Auto Zap", + "DtYelJ": "Transfer", + "E8a4yq": "Follow some popular accounts", + "ELbg9p": "Data Providers", + "EPYwm7": "Your private key is your password. If you lose this key, you will lose access to your account! Copy it and keep it in a safe place. There is no way to reset your private key.", + "EWyQH5": "Global", + "Ebl/B2": "Translate to {lang}", + "EcZF24": "Custom Relays", + "EcglP9": "Key", + "EnCOBJ": "Buy", + "Eqjl5K": "Only Snort and our integration partner identifier gives you a colorful domain name, but you are welcome to use other services too.", + "F+B3x1": "We have also partnered with nostrplebs.com to give you more options", + "F3l7xL": "Add Account", + "FDguSC": "{n} Zaps", + "FP+D3H": "LNURL to forward zaps to", + "FS3b54": "Done!", + "FSYL8G": "Trending Users", + "FdhSU2": "Claim Now", + "FfYsOb": "An error has occured!", + "FmXUJg": "follows you", + "G/yZLu": "Remove", + "G1BGCg": "Select Wallet", + "GFOoEE": "Salt", + "GL8aXW": "Bookmarks ({n})", + "GSye7T": "Lightning Address", + "GUlSVG": "Claim your included Snort nostr address", + "Gcn9NQ": "Magnet Link", + "GspYR7": "{n} Dislike", + "H+vHiz": "Hex Key..", + "H0JBH6": "Log Out", + "H6/kLh": "Order Paid!", + "HAlOn1": "Name", + "HF4YnO": "Watch Live!", + "HFls6j": "name will be available later", + "HOzFdo": "Muted", + "HWbkEK": "Clear cache and reload", + "HbefNb": "Open Wallet", + "IDjHJ6": "Thanks for using Snort, please consider donating if you can.", + "IEwZvs": "Are you sure you want to unpin this note?", + "IKKHqV": "Follows", + "INSqIz": "Twitter username...", + "IUZC+0": "This means that nobody can modify notes which you have created and everybody can easily verify that the notes they are reading are created by you.", + "Ig9/a1": "Sent {n} sats to {name}", + "Ix8l+B": "Trending Notes", + "J+dIsA": "Subscriptions", + "JCIgkj": "Username", + "JHEHCk": "Zaps ({n})", + "JPFYIM": "No lightning address", + "JeoS4y": "Repost", + "JjGgXI": "Search users", + "JkLHGw": "Website", + "JymXbw": "Private Key", + "K3r6DQ": "Delete", + "K7AkdL": "Show", + "KAhAcM": "Enter LNDHub config", + "KLo3SP": "Reason: {reason}", + "KQvWvD": "Deleted", + "KWuDfz": "I have saved my keys, continue", + "KahimY": "Unknown event kind: {kind}", + "KoFlZg": "Enter mint URL", + "LF5kYT": "Other Connections", + "LXxsbk": "Anonymous", + "LgbKvU": "Comment", + "Lu5/Bj": "Open on Zapstr", + "Lw+I+J": "{n,plural,=0{{name} zapped} other{{name} & {n} others zapped}}", + "M3Oirc": "Debug Menus", + "MBAYRO": "Shows \"Copy ID\" and \"Copy Event JSON\" in the context menu on each message", + "MI2jkA": "Not available:", + "MP54GY": "Wallet password", + "MRp6Ly": "Twitter username", + "MWTx65": "Default Page", + "Mrpkot": "Pay for subscription", + "MuVeKe": "Buy nostr address", + "MzRYWH": "Buying {item}", + "N2IrpM": "Confirm", + "NAuFNH": "You already have a subscription of this type, please renew or pay", + "NNSu3d": "Import Twitter Follows", + "NdOYJJ": "Hmm nothing here.. Checkout {newUsersPage} to follow some recommended nostrich's!", + "NepkXH": "Can't vote with {amount} sats, please set a different default zap amount", + "NfNk2V": "Your private key", + "NndBJE": "New users page", + "O9GTIc": "Profile picture", + "OEW7yJ": "Zaps", + "OKhRC6": "Share", + "OLEm6z": "Unknown login error", + "OQXnew": "You subscription is still active, you can't renew yet", + "ORGv1Q": "Created", + "P04gQm": "All zaps sent to this note will be received by the following LNURL", + "P61BTu": "Copy Event JSON", + "P7FD0F": "System (Default)", + "P7nJT9": "Total today (UTC): {amount} sats", + "PCSt5T": "Preferences", + "PLSbmL": "Your mnemonic phrase", + "PamNxw": "Unknown file header: {name}", + "Pe0ogR": "Theme", + "PrsIg7": "Reactions will be shown on every page, if disabled no reactions will be shown", + "QDFTjG": "{n} Relays", + "QWhotP": "Zap Pool only works if you use one of the supported wallet connections (WebLN, LNC, LNDHub or Nostr Wallet Connect)", + "QawghE": "You can change your username at any point.", + "QxCuTo": "Art by {name}", + "Qxv0B2": "You currently have {number} sats in your zap pool.", + "R/6nsx": "Subscription", + "R1fEdZ": "Forward Zaps", + "R81upa": "People you follow", + "RDZVQL": "Check", + "RahCRH": "Expired", + "RfhLwC": "By: {author}", + "RhDAoS": "Are you sure you want to delete {id}", + "RjpoYG": "Recent", + "RoOyAh": "Relays", + "Rs4kCE": "Bookmark", + "RwFaYs": "Sort", + "SOqbe9": "Update Lightning Address", + "SP0+yi": "Buy Subscription", + "SX58hM": "Copy", + "SYQtZ7": "LN Address Proxy", + "ShdEie": "Mark all read", + "Sjo1P4": "Custom", + "Ss0sWu": "Pay Now", + "TDR5ge": "Media in notes will automatically be shown for selected people, otherwise only the link will show", + "TMfYfY": "Cashu token", + "TpgeGw": "Hex Salt..", + "Tpy00S": "People", + "UDYlxu": "Pending Subscriptions", + "ULotH9": "Amount: {amount} sats", + "UT7Nkj": "New Chat", + "UUPFlt": "Users must accept the content warning to show the content of your note.", + "Up5U7K": "Block", + "VBadwB": "Hmm, can't find a key manager extension.. try reloading the page.", + "VN0+Fz": "Balance: {amount} sats", + "VOjC1i": "Pick which upload service you want to upload attachments to", + "VR5eHw": "Public key (npub/nprofile)", + "VlJkSk": "{n} muted", + "VnXp8Z": "Avatar", + "VtPV/B": "Login with Extension (NIP-07)", + "VvaJst": "View Wallets", + "Vx7Zm2": "How do keys work?", + "W1yoZY": "It looks like you dont have any subscriptions, you can get one {link}", + "W2PiAr": "{n} Blocked", + "W9355R": "Unmute", + "WONP5O": "Find your twitter follows on nostr (Data provided by {provider})", + "WxthCV": "e.g. Jack", + "X7xU8J": "nsec, npub, nip-05, hex, mnemonic", + "XICsE8": "File hosts", + "XgWvGA": "Reactions", + "Xopqkl": "Your default zap amount is {number} sats, example values are calculated from this.", + "XrSk2j": "Redeem", + "XzF0aC": "Key manager extensions are more secure and allow you to easily login to any Nostr client, here are some well known extensions:", + "Y31HTH": "Help fund the development of Snort", + "YDURw6": "Service URL", + "YXA3AH": "Enable reactions", + "Z0FDj+": "Subscribe to Snort {plan} for {price} and receive the following rewards", + "Z4BMCZ": "Enter pairing phrase", + "ZKORll": "Activate Now", + "ZLmyG9": "Contributors", + "ZUZedV": "Lightning Donation:", + "Zr5TMx": "Setup profile", + "a5UPxh": "Fund developers and platforms providing NIP-05 verification services", + "a7TDNm": "Notes will stream in real time into global and notes tab", + "aWpBzj": "Show more", + "b12Goz": "Mnemonic", + "b5vAk0": "Your handle will act like a lightning address and will redirect to your chosen LNURL or Lightning address", + "bQdA2k": "Sensitive Content", + "bep9C3": "Public Key", + "bfvyfs": "Anon", + "brAXSu": "Pick a username", + "bxv59V": "Just now", + "c+oiJe": "Install Extension", + "c35bj2": "If you have an enquiry about your NIP-05 order please DM {link}", + "c3g2hL": "Broadcast Again", + "cFbU1B": "Using Alby? Go to {link} to get your NWC config!", + "cPIKU2": "Following", + "cQfLWb": "URL..", + "cWx9t8": "Mute all", + "cg1VJ2": "Connect Wallet", + "cuP16y": "Multi account support", + "cuV2gK": "name is registered", + "cyR7Kh": "Back", + "d6CyG5": "History", + "d7d0/x": "LN Address", + "dOQCL8": "Display name", + "e61Jf3": "Coming soon", + "e7qqly": "Mark All Read", + "eHAneD": "Reaction emoji", + "eJj8HD": "Get Verified", + "eSzf2G": "A single zap of {nIn} sats will allocate {nOut} sats to the zap pool.", + "eXT2QQ": "Group Chat", + "fBI91o": "Zap", + "fOksnD": "Can't vote because LNURL service does not support zaps", + "fWZYP5": "Pinned", + "filwqD": "Read", + "flnGvv": "What's on your mind?", + "fsB/4p": "Saved", + "g5pX+a": "About", + "g985Wp": "Failed to send vote", + "gBdUXk": "Save your keys!", + "gDZkld": "Snort is a Nostr UI, nostr is a decentralised protocol for saving and distributing \"notes\".", + "gDzDRs": "Emoji to send when reactiong to a note", + "gXgY3+": "Not all clients support this yet", + "gczcC5": "Subscribe", + "gjBiyj": "Loading...", + "grQ+mI": "Proof of Work", + "h8XMJL": "Badges", + "hK5ZDk": "the world", + "hMzcSq": "Messages", + "hWSp+B": "Nostr Connect (NIP-46)", + "hY4lzx": "Supports", + "hicxcO": "Show replies", + "hniz8Z": "here", + "i/dBAR": "Zap Pool", + "iCqGww": "Reactions ({n})", + "iDGAbc": "Get a Snort identifier", + "iEoXYx": "DeepL translations", + "iGT1eE": "Prevent fake accounts from imitating you", + "iNWbVV": "Handle", + "iUsU2x": "Mint: {url}", + "iXPL0Z": "Can't login with private key on an insecure connection, please use a Nostr key manager extension instead", + "ieGrWo": "Follow", + "itPgxd": "Profile", + "izWS4J": "Unfollow", + "jA3OE/": "{n,plural,=1{{n} sat} other{{n} sats}}", + "jCA7Cw": "Preview on snort", + "jMzO1S": "Internal error: {msg}", + "jfV8Wr": "Back", + "juhqvW": "Improve login security with browser extensions", + "jvo0vs": "Save", + "jzgQ2z": "{n} Reactions", + "k2veDA": "Write", + "k7sKNy": "Our very own NIP-05 verification service, help support the development of this site and get a shiny special badge on our site!", + "kJYo0u": "{n,plural,=0{{name} reposted} other{{name} & {n} others reposted}}", + "kaaf1E": "now", + "kuPHYE": "{n,plural,=0{{name} liked} other{{name} & {n} others liked}}", + "l+ikU1": "Everything in {plan}", + "lBboHo": "If you want to try out some others, check out {link} for more!", + "lCILNz": "Buy Now", + "lD3+8a": "Pay", + "lPWASz": "Snort nostr address", + "lTbT3s": "Wallet password", + "lgg1KN": "account page", + "ll3xBp": "Image proxy service", + "lnaT9F": "Following {n}", + "lsNFM1": "Click to load content from {link}", + "lvlPhZ": "Pay Invoice", + "mErPop": "It looks like you dont have any, check {link} to buy one!", + "mH91FY": "Each contributor will get paid a percentage of all donations and NIP-05 orders, you can see the split amounts below", + "mKAr6h": "Follow all", + "mKh2HS": "File upload service", + "mKhgP9": "{n,plural,=0{} =1{zapped} other{zapped}}", + "mTJFgF": "Popular", + "mfe8RW": "Option: {n}", + "n1Whvj": "Switch", + "nDejmx": "Unblock", + "nGBrvw": "Bookmarks", + "nN9XTz": "Share your thoughts with {link}", + "nOaArs": "Setup Profile", + "nWQFic": "Renew", + "nn1qb3": "Your donations are greatly appreciated", + "nwZXeh": "{n} blocked", + "o6Uy3d": "Only the secret key can be used to publish (sign events), everything else logs you in read-only mode.", + "o7e+nJ": "{n} followers", + "oJ+JJN": "Nothing found :/", + "odFwjL": "Follows only", + "odhABf": "Login", + "ojzbwv": "Hey, it looks like you dont have a Nostr Address yet, you should get one! Check out {link}", + "osUr8O": "You can also use these extensions to login to most Nostr sites.", + "oxCa4R": "Getting an identifier helps confirm the real you to people who know you. Many people can have a username @jack, but there is only one jack@cash.app.", + "p4N05H": "Upload", + "p85Uwy": "Active Subscriptions", + "pI+77w": "Downloadable backups from Snort relay", + "puLNUJ": "Pin", + "pzTOmv": "Followers", + "qD9EUF": "Email <> DM bridge for your Snort nostr address", + "qDwvZ4": "Unknown error", + "qMx1sA": "Default Zap amount", + "qUJTsT": "Blocked", + "qdGuQo": "Your Private Key Is (do not share this with anyone)", + "qkvYUb": "Add to Profile", + "qmJ8kD": "Translation failed", + "qtWLmt": "Like", + "r3C4x/": "Software", + "r5srDR": "Enter wallet password", + "rT14Ow": "Add Relays", + "reJ6SM": "It is recommended to use one of the following browser extensions if you are on a desktop computer to secure your key:", + "rfuMjE": "(Default)", + "rmdsT4": "{n} days", + "rrfdTe": "This is the same technology which is used by Bitcoin and has been proven to be extremely secure.", + "rudscU": "Failed to load follows, please try again later", + "sUNhQE": "user", + "sWnYKw": "Snort is designed to have a similar experience to Twitter.", + "svOoEH": "Name-squatting and impersonation is not allowed. Snort and our partners reserve the right to terminate your handle (not your account - nobody can take that away) for violating this rule.", + "tOdNiY": "Dark", + "th5lxp": "Send note to a subset of your write relays", + "thnRpU": "Getting NIP-05 verified can help:", + "ttxS0b": "Supporter Badge", + "u/vOPu": "Paid", + "u4bHcR": "Check out the code here: {link}", + "uD/N6c": "Zap {target} {n} sats", + "uSV4Ti": "Reposts need to be manually confirmed", + "usAvMr": "Edit Profile", + "ut+2Cd": "Get a partner identifier", + "v8lolG": "Start chat", + "vOKedj": "{n,plural,=1{& {n} other} other{& {n} others}}", + "vU71Ez": "Paying with {wallet}", + "vZ4quW": "NIP-05 is a DNS based verification spec which helps to validate you as a real user.", + "vhlWFg": "Poll Options", + "vlbWtt": "Get a free one", + "vrTOHJ": "{amount} sats", + "vxwnbh": "Amount of work to apply to all published events", + "wEQDC6": "Edit", + "wLtRCF": "Your key", + "wWLwvh": "Anon", + "wYSD2L": "Nostr Adddress", + "wih7iJ": "name is blocked", + "wqyN/i": "Find out more info about {service} at {link}", + "wtLjP6": "Copy ID", + "x/Fx2P": "Fund the services that you use by splitting a portion of all your zaps into a pool of funds!", + "x/q8d5": "This note has been marked as sensitive, click here to reveal", + "x82IOl": "Mute", + "xIcAOU": "Votes by {type}", + "xIoGG9": "Go to", + "xJ9n2N": "Your public key", + "xKflGN": "{username}''s Follows on Nostr", + "xQtL3v": "Unlock", + "xaj9Ba": "Provider", + "xbVgIm": "Automatically load media", + "xhQMeQ": "Expires", + "xmcVZ0": "Search", + "y1Z3or": "Language", + "yCLnBC": "LNURL or Lightning Address", + "yCmnnm": "Read global from", + "zFegDD": "Contact", + "zINlao": "Owner", + "zQvVDJ": "All", + "zcaOTs": "Zap amount in sats", + "zjJZBd": "You're ready!", + "zonsdq": "Failed to load LNURL service", + "zvCDao": "Automatically show latest notes" +} diff --git a/packages/app/src/translations/zh_CN.json b/packages/app/src/translations/zh_CN.json index 6023e80c..7e991edb 100644 --- a/packages/app/src/translations/zh_CN.json +++ b/packages/app/src/translations/zh_CN.json @@ -3,6 +3,7 @@ "+PzQ9Y": "立即支出", "+Vxixo": "秘密群聊", "+aZY2h": "打闪种类", + "+tShPg": "已关注", "+vA//S": "登录", "+vIQlC": "请确保将以下密码妥善保存以便将来管理你的代号", "+vVZ/G": "连接", @@ -11,6 +12,7 @@ "/JE/X+": "帐户支持", "/PCavi": "公开", "/RD0e2": "Nostr 利用数字签名技术实现防篡改的笔记并可令其安全的复制到大量中继节点,给你的内容作为冗余存储。", + "/Xf4UW": "发送匿名使用资料", "/d6vEc": "使你的帐号可更方便地被找到及分享", "/n5KSF": "{n} 毫秒", "00LcfG": "加载更多", @@ -19,6 +21,7 @@ "0BUTMv": "搜索...", "0jOEtS": "LNURL无效", "0mch2Y": "名称中有禁用字符", + "0uoY11": "显示状态", "0yO7wF": "{n} 秒", "1A7TZk": "Snort是什么?它是如何运作的?", "1Mo59U": "是否确定要从收藏中移除此条笔记?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} 个收藏", "2k0Cv+": "踩 ({n})", "2ukA4d": "{n} 小时", + "3KNMbJ": "文章", "3Rx6Qo": "高级", "3cc4Ct": "浅色", "3gOsZq": "翻译人员", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} 个粉丝", "3xCwbZ": "或者", "3yk8fB": "钱包", + "40VR6s": "Nostr Connect", "450Fty": "无", "47FYwb": "取消", "4IPzdn": "主要开发人员", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs 是该领域首批 NIP-05 提供商之一,以合理的价格提供大量域名", "4Z3t5i": "使用 imgproxy 压缩图片", "4rYCjn": "自用笔记", + "5BVs2e": "打闪", + "5CB6zB": "打闪拆分", "5JcXdV": "创建帐户", "5oTnfy": "购买代号", "5rOdPG": "一旦你设置了你的密钥管理器扩展程序并生成了密钥, 你就可以使用我们的新用户流程来设置你的个人档案,并帮助你在 Nostr 找到一些有趣的人关注。", "5u6iEc": "转移到公钥", "5vMmmR": "在 nostr 上,用户名不是独特的。Nostr 地址是你的独特的人类可读的地址。", "5ykRmX": "发送打闪", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "从 {host} 代理图像失败,点击此处直接加载", + "6OSOXl": "原因:{reason}", "6Yfvvp": "获取标识符", + "6bgpn+": "并非所有客户端都支持,就算已配置了打闪拆分,你仍然可能会收到一些打闪", "6ewQqw": "赞 ({n})", "6uMqL1": "未付款", "7+Domh": "笔记", "7BX/yC": "帐户切换", "7hp70g": "NIP-05", - "7xzTiH": "{action} 到 {target}", "8/vBbP": "转发 ({n})", "89q5wc": "确认转发", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "打闪 {n} 聪", + "8Rkoyb": "接收方", + "8Y6bZQ": "无效打闪拆分:{input}", "8g2vyB": "名称过长", "8v1NN+": "配对词句", + "8xNnhi": "Nostr Extension", "9+Ddtu": "下一步", "9HU8vw": "回复", "9SvQep": "关注 {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "闪电发票", "ADmfQT": "上一层", "AGNz71": "打闪所有 {n} 聪", + "AN0Z7Q": "Muted Words", "ASRK0S": "该作者已被静音", "Adk34V": "设置你的个人档案", "Ai8VHU": "Snort 中继器上无限制笔记保留", @@ -92,6 +107,7 @@ "BOr9z/": "Snort是由热心人士利用空闲时间开发的开源项目", "BWpuKl": "更新", "BcGMo+": "笔记含有文本内容,这些笔记最常用的用法是存储类似“推文”的消息。", + "C1LjMx": "Lightning Donation", "C5xzTC": "高级会员", "C81/uG": "登出", "C8HhVE": "推荐关注", @@ -106,20 +122,23 @@ "DZzCem": "显示最新的 {n} 条笔记", "DcL8P+": "支持者", "Dh3hbq": "自动打闪", + "Dn82AL": "Live", "DtYelJ": "转移", "E8a4yq": "关注一些受欢迎的帐户", "ELbg9p": "数据提供方", "EPYwm7": "你的私钥相当于你的密码。如果你丢失了此私钥,你将无法访问你的账户!复制它并将其保存到安全的地方。你的私钥是无法重置的。", + "EQKRE4": "在个人档案页面上显示徽章", "EWyQH5": "全球", "Ebl/B2": "翻译成 {lang}", "EcZF24": "自定义中继器", "EcglP9": "密钥", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "购买", "Eqjl5K": "只有 Snort 和我们的合作伙伴的标识符为你提供彩色的域名,但也欢迎你使用其他服务。", "F+B3x1": "我们还与 nostrplebs.com 合作,为你提供更多选择", "F3l7xL": "添加帐户", "FDguSC": "{n} 次打闪", - "FP+D3H": "接收转发打闪的 LNURL", + "FMfjrl": "在个人档案页面上显示状态消息", "FS3b54": "完成!", "FSYL8G": "热门用户", "FdhSU2": "立即领取", @@ -129,28 +148,32 @@ "G1BGCg": "选择钱包", "GFOoEE": "盐", "GL8aXW": "收藏 ({n})", + "GQPtfk": "Join Stream", "GSye7T": "闪电地址", "GUlSVG": "认领你包含的 Snort nostr 地址", "Gcn9NQ": "磁力链接", "GspYR7": "{n} 踩", + "Gxcr08": "Broadcast Event", "H+vHiz": "十六进制密钥...", "H0JBH6": "登出", "H6/kLh": "订单已支付!", "HAlOn1": "名称", - "HF4YnO": "观看直播!", "HFls6j": "名称稍后可用", "HOzFdo": "已静音", "HWbkEK": "清除缓存并重新加载", "HbefNb": "打开钱包", + "HhcAVH": "你不关注这个用户,点击此处从{link}加载多媒体,或更新你的选项来自动加载来自任何人的多媒体。", "IDjHJ6": "感谢你使用 Snort,请考虑捐赠。", "IEwZvs": "是否确定要取消置顶此条笔记?", "IKKHqV": "关注", "INSqIz": "推特用户名...", "IUZC+0": "这意味着没有人能够篡改你创建的笔记,并且任何人都可以轻松验证他们正在阅读的笔记是否是由你创建的。", "Ig9/a1": "向 {name} 发送了 {n} 聪", + "IoQq+a": "仍然要加载点击这里", "Ix8l+B": "热门笔记", "J+dIsA": "订阅", "JCIgkj": "用户名", + "JGrt9q": "将聪发送到 {name}", "JHEHCk": "打闪 ({n})", "JPFYIM": "没有闪电地址", "JeoS4y": "转发", @@ -160,16 +183,19 @@ "K3r6DQ": "删除", "K7AkdL": "显示", "KAhAcM": "输入 LNDHub 配置", - "KLo3SP": "原因: {reason}", "KQvWvD": "已删除", "KWuDfz": "我已经保存了我的密钥,继续", "KahimY": "未知事件类型:{kind}", "KoFlZg": "输入铸币厂 URL", + "KtsyO0": "Enter Pin", "LF5kYT": "其他连接", + "LR1XjT": "Pin too short", "LXxsbk": "匿名", "LgbKvU": "评论", "Lu5/Bj": "在 Zapstr 上打开", "Lw+I+J": "{n,plural,=0{{name}已打闪} other{{name}和{n}个其他用户已打闪}}", + "LwYmVi": "此笔记上的打闪将被拆分给以下用户。", + "M10zFV": "Nostr Connect", "M3Oirc": "调试菜单", "MBAYRO": "在每条消息的上下文菜单中显示“复制 ID”和“复制事件 JSON”", "MI2jkA": "无法使用:", @@ -180,6 +206,7 @@ "MuVeKe": "购买 nostr 地址", "MzRYWH": "购买{item}", "N2IrpM": "确认", + "NAidKb": "通知", "NAuFNH": "你已经有此类型的订阅,请续订或支付", "NNSu3d": "导入推特关注", "NdOYJJ": "嗯这里什么都没有.. 去看看 {newUsersPage} 以关注一些推荐的 nostrich!", @@ -192,7 +219,6 @@ "OLEm6z": "未知登录错误", "OQXnew": "你的订阅仍然活跃,你还不能续订", "ORGv1Q": "已创建", - "P04gQm": "发送到此条笔记的所有打闪将由以下LNURL 接收。", "P61BTu": "复制事件 JSON", "P7FD0F": "系统(默认)", "P7nJT9": "今天总计 (UTC):{amount} 聪", @@ -207,7 +233,6 @@ "QxCuTo": "{name} 的艺术作品", "Qxv0B2": "目前你的打闪池中有 {number} 聪。", "R/6nsx": "订阅", - "R1fEdZ": "转发打闪", "R81upa": "你关注的用户", "RDZVQL": "查看", "RahCRH": "已过期", @@ -217,19 +242,19 @@ "RoOyAh": "中继器", "Rs4kCE": "收藏", "RwFaYs": "排序", + "SMO+on": "将打闪发送到 {name}", "SOqbe9": "更新闪电地址", "SP0+yi": "购买订阅", - "SX58hM": "复制", "SYQtZ7": "闪电地址代理", "ShdEie": "全标已读", "Sjo1P4": "自定义", "Ss0sWu": "立即支付", + "StKzTE": "作者已将此笔记标记为敏感主题", "TDR5ge": "笔记中的媒体将自动显示给选定的人,否则只会显示链接", - "TMfYfY": "Cashu 代币", + "TP/cMX": "Ended", "TpgeGw": "十六进制盐..", "Tpy00S": "用户", "UDYlxu": "待定订阅", - "ULotH9": "金额: {amount} 聪", "UT7Nkj": "新聊天", "UUPFlt": "用户必须接受内容警告才能显示你的笔记的内容。", "Up5U7K": "屏蔽", @@ -239,15 +264,16 @@ "VR5eHw": "公钥 (npub/nprofile)", "VlJkSk": "{n} 已静音", "VnXp8Z": "头像", - "VtPV/B": "使用扩展程序登录 (NIP-07)", "VvaJst": "查看钱包", "Vx7Zm2": "密钥是如何运作的?", "W1yoZY": "看起来你没有任何订阅,你可以获取一个{link}", "W2PiAr": "{n} 已屏蔽", "W9355R": "解除静音", "WONP5O": "在 nostr 上找到你的推特关注(数据由 {provider} 提供)", + "WvGmZT": "npub / nprofile / nostr 地址", "WxthCV": "例如杰克", "X7xU8J": "nsec、npub、NIP-05、十六进制、助记词句", + "XECMfW": "发送使用资料", "XICsE8": "文件主机", "XgWvGA": "回应", "Xopqkl": "你的默认打闪金额是 {number} 聪,示例值是以此计算的。", @@ -260,19 +286,21 @@ "Z4BMCZ": "输入配对词句", "ZKORll": "立即激活", "ZLmyG9": "贡献者", - "ZUZedV": "闪电捐赠:", + "ZS+jRE": "将打闪拆分发送到", "Zr5TMx": "设置个人档案", "a5UPxh": "资助提供 NIP-05 验证服务的开发人员和平台", "a7TDNm": "笔记将实时流式传输到全球和帖子选项卡", "aWpBzj": "显示更多", "b12Goz": "助记词", "b5vAk0": "你的代号将像闪电地址一样重定向至你所选的 LNURL 或闪电地址", + "bLZL5a": "Get Address", "bQdA2k": "敏感内容", "bep9C3": "公钥", "bfvyfs": "匿名", "brAXSu": "选择一个用户名", "bxv59V": "刚刚", "c+oiJe": "安装扩展插件", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "如果你对 NIP-05 订单有任何疑问,请私信 {link}", "c3g2hL": "再次广播", "cFbU1B": "使用 Alby?前往 {link} 来配置你的 NWC!", @@ -287,6 +315,7 @@ "d7d0/x": "闪电地址", "dOQCL8": "显示名称", "e61Jf3": "即将上线", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "全标已读", "eHAneD": "回应表情符号", "eJj8HD": "获取验证", @@ -297,6 +326,7 @@ "fWZYP5": "已置顶", "filwqD": "读", "flnGvv": "你在想些什么?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "已保存", "g5pX+a": "关于", "g985Wp": "发送投票失败", @@ -310,9 +340,9 @@ "h8XMJL": "徽章", "hK5ZDk": "世界", "hMzcSq": "消息", - "hWSp+B": "Nostr 连接(NIP-46)", "hY4lzx": "支持", "hicxcO": "显示回复", + "hmZ3Bz": "多媒体", "hniz8Z": "这里", "i/dBAR": "打闪池", "iCqGww": "回应({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL 翻译", "iGT1eE": "防止虚假帐户冒充你", "iNWbVV": "代号", - "iUsU2x": "铸币厂:{url}", "iXPL0Z": "无法在不安全的连接上使用私钥登录,请使用Nostr密钥管理器扩展程序", "ieGrWo": "关注", "itPgxd": "个人档案", @@ -349,7 +378,7 @@ "lsNFM1": "点击以从 {link} 加载内容", "lvlPhZ": "支付发票", "mErPop": "看起来你没有 nostr 地址,查看 {link} 以购买!", - "mH91FY": "每个贡献者将获得所有捐赠和 NIP-05 订单的一定百分比的报酬,你可以在下面看到分账金额", + "mH91FY": "每个贡献者将获得所有捐赠和 NIP-05 订单的一定百分比的报酬,你可以在下面看到拆分金额", "mKAr6h": "关注所有", "mKh2HS": "文件上传服务", "mKhgP9": "{n,plural,=0{} =1{已打闪} other{已打闪}}", @@ -381,9 +410,11 @@ "qMx1sA": "默认打闪金额", "qUJTsT": "已屏蔽", "qdGuQo": "你的私钥是(不要与任何人分享)", + "qfmMQh": "This note has been muted", "qkvYUb": "添加至个人档案", "qmJ8kD": "翻译失败", "qtWLmt": "点赞", + "qz9fty": "Incorrect pin", "r3C4x/": "软件", "r5srDR": "输入钱包密码", "rT14Ow": "添加中继", @@ -392,7 +423,10 @@ "rmdsT4": "{n} 天", "rrfdTe": "这与比特币使用的技术相同,并已被证明非常安全。", "rudscU": "加载关注失败,请稍后重试", + "sKDn4e": "显示徽章", + "sUNhQE": "用户", "sWnYKw": "Snort 旨在提供类似推特的体验。", + "sZQzjQ": "解析打闪拆分失败了:{input}", "svOoEH": "不允许抢注和冒充。 Snort 和我们的合作伙伴保留因违反此规则而终止你的代号(不是你的帐户 - 没有人可以拿走它)的权利。", "tOdNiY": "深色", "th5lxp": "将笔记发送到你的写入中继器的子集", @@ -400,13 +434,12 @@ "ttxS0b": "支持者徽章", "u/vOPu": "已付款", "u4bHcR": "在此处查看代码:{link}", - "uD/N6c": "打闪 {target} {n} 聪", "uSV4Ti": "转发需要人工确认", + "uc0din": "将聪拆分发送到", "usAvMr": "编辑个人档案", "ut+2Cd": "获取合作伙伴标识符", "v8lolG": "开始聊天", "vOKedj": "{n,plural,=1{和{n}个其他} other{和{n}个其他}}", - "vU71Ez": "正在使用 {wallet} 支付", "vZ4quW": "NIP-05 是一种基于 DNS 的验证规范,可帮助验证你是真实用户。", "vhlWFg": "投票选项", "vlbWtt": "获取免费的", @@ -414,14 +447,16 @@ "vxwnbh": "适用于所有发布事件的工作量", "wEQDC6": "编辑", "wLtRCF": "你的密钥", + "wSZR47": "Submit", "wWLwvh": "匿名", "wYSD2L": "Nostr 地址", "wih7iJ": "名称已被屏蔽", + "wofVHy": "Moderation", "wqyN/i": "在 {link} 了解有关 {service} 的更多信息", "wtLjP6": "复制 ID", "x/Fx2P": "将部分打闪分配到资金库来资助你使用的服务!", - "x/q8d5": "此条笔记已标记为敏感内容,点击此处显示", "x82IOl": "静音", + "xIcAOU": "根据 {type} 的投票", "xIoGG9": "前往", "xJ9n2N": "你的公钥", "xKflGN": "{username} 在 Nostr 上的关注", @@ -433,11 +468,13 @@ "y1Z3or": "语言", "yCLnBC": "LNURL 或闪电地址", "yCmnnm": "全球提要来自", + "zCb8fX": "权重", "zFegDD": "联系人", "zINlao": "所有者", "zQvVDJ": "全部", "zcaOTs": "以聪为单位的打闪金额", "zjJZBd": "你已准备就绪!", "zonsdq": "加载 LNURL 服务失败", - "zvCDao": "自动显示最新笔记" + "zvCDao": "自动显示最新笔记", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/translations/zh_TW.json b/packages/app/src/translations/zh_TW.json index 9a8c55d2..31d73015 100644 --- a/packages/app/src/translations/zh_TW.json +++ b/packages/app/src/translations/zh_TW.json @@ -3,6 +3,7 @@ "+PzQ9Y": "立即支出", "+Vxixo": "祕密羣聊", "+aZY2h": "打閃種類", + "+tShPg": "已關注", "+vA//S": "登錄", "+vIQlC": "請確保將以下密碼妥善保存以便將來管理你的代號", "+vVZ/G": "連接", @@ -11,6 +12,7 @@ "/JE/X+": "帳戶支持", "/PCavi": "公開", "/RD0e2": "Nostr 利用數字簽名技術實現防篡改的筆記並可令其安全的複製到大量的中繼節點,給你的內容作為冗余存儲。", + "/Xf4UW": "傳送匿名使用資料", "/d6vEc": "使你的帳號可更方便地被找到及分享", "/n5KSF": "{n} 毫秒", "00LcfG": "加載更多", @@ -19,6 +21,7 @@ "0BUTMv": "搜索...", "0jOEtS": "LNURL 無效", "0mch2Y": "名稱中有禁用字符", + "0uoY11": "顯示狀態", "0yO7wF": "{n} 秒", "1A7TZk": "Snort 是什麼?它是如何運作的?", "1Mo59U": "是否確定要從收藏中移除此條筆記?", @@ -34,6 +37,7 @@ "2a2YiP": "{n} 個收藏", "2k0Cv+": "踩 ({n})", "2ukA4d": "{n}小時", + "3KNMbJ": "文章", "3Rx6Qo": "高級", "3cc4Ct": "淺色", "3gOsZq": "翻譯人員", @@ -42,6 +46,7 @@ "3tVy+Z": "{n} 個粉絲", "3xCwbZ": "或者", "3yk8fB": "錢包", + "40VR6s": "Nostr Connect", "450Fty": "無", "47FYwb": "取消", "4IPzdn": "主要開發人員", @@ -50,25 +55,34 @@ "4Vmpt4": "Nostr Plebs 是該領域首批 NIP-05 供應商之一,以合理的價格提供大量域名", "4Z3t5i": "使用 imgproxy 壓縮圖片", "4rYCjn": "自用筆記", + "5BVs2e": "打閃", + "5CB6zB": "打閃拆分", "5JcXdV": "創建帳戶", "5oTnfy": "購買代號", "5rOdPG": "一旦你設置了你的密鑰管理器擴展程序並生成了密鑰,你就可以使用我們的新用戶流程來設置你的個人檔案,並幫助你在 nostr 上找到一些有趣的人關注。", "5u6iEc": "轉移到公鑰", "5vMmmR": "在 nostr 上,用戶名不是獨特的。Nostr 地址是你的獨特的人類可讀的地址。", "5ykRmX": "發送打閃", + "6/SF6e": "

{n}

Cashu sats", + "6/hB3S": "Watch Replay", "65BmHb": "從 {host} 代理圖像失敗,點擊此處直接加載", + "6OSOXl": "原因:{reason}", "6Yfvvp": "獲取標識符", + "6bgpn+": "並非所有客戶端都支持,就算已配置了打閃拆分,你仍然可能會收到一些打閃", "6ewQqw": "贊({n})", "6uMqL1": "未付款", "7+Domh": "筆記", "7BX/yC": "帳戶切換", "7hp70g": "NIP-05", - "7xzTiH": "{action} 到 {target}", "8/vBbP": "轉發({n})", "89q5wc": "確認轉發", + "8Kboo2": "Scan this QR code with your signer app to get started", "8QDesP": "打閃 {n} 聰", + "8Rkoyb": "接收方", + "8Y6bZQ": "無效打閃拆分:{input}", "8g2vyB": "名稱過長", "8v1NN+": "配對詞句", + "8xNnhi": "Nostr Extension", "9+Ddtu": "下一步", "9HU8vw": "回覆", "9SvQep": "關注 {n}", @@ -78,6 +92,7 @@ "9wO4wJ": "閃電發票", "ADmfQT": "上一層", "AGNz71": "打閃所有 {n} 聰", + "AN0Z7Q": "Muted Words", "ASRK0S": "该作者已被静音", "Adk34V": "設置你的個人檔案", "Ai8VHU": "Snort 中繼器上無限制筆記保留", @@ -92,6 +107,7 @@ "BOr9z/": "Snort 是由熱心人士利用空閒時間開發的開源項目", "BWpuKl": "更新", "BcGMo+": "筆記含有文本內容,這些筆記最常用的用法是存儲類似“推文”的消息。", + "C1LjMx": "Lightning Donation", "C5xzTC": "高級會員", "C81/uG": "登出", "C8HhVE": "推薦關注", @@ -106,20 +122,23 @@ "DZzCem": "顯示最新的 {n} 條筆記", "DcL8P+": "支持者", "Dh3hbq": "自動打閃", + "Dn82AL": "Live", "DtYelJ": "轉移", "E8a4yq": "關注一些受歡迎的帳戶", "ELbg9p": "數據提供方", "EPYwm7": "你的私鑰相當於你的密碼。如果你丟失了此私鑰,你將無法訪問你的帳戶!複製它並將其保存到安全的地方。你的私鑰是無法重置的。", + "EQKRE4": "在個人檔案頁面上顯示徽章", "EWyQH5": "全球", "Ebl/B2": "翻譯成 {lang}", "EcZF24": "自定義中繼器", "EcglP9": "密鑰", + "EjFyoR": "On-chain Donation Address", "EnCOBJ": "購買", "Eqjl5K": "只有 Snort 和我們的合作夥伴的標識符為你提供彩色的域名,但也歡迎你使用其他服務。", "F+B3x1": "我們還與 nostrplebs.com 合作,為你提供更多選擇", "F3l7xL": "添加帳戶", "FDguSC": "{n} 打閃", - "FP+D3H": "接收轉發打閃的LNURL", + "FMfjrl": "在個人檔案頁面上顯示狀態消息", "FS3b54": "完成!", "FSYL8G": "熱門用戶", "FdhSU2": "立即領取", @@ -129,28 +148,32 @@ "G1BGCg": "選擇錢包", "GFOoEE": "鹽", "GL8aXW": "收藏 ({n})", + "GQPtfk": "Join Stream", "GSye7T": "閃電地址", "GUlSVG": "認領你包含的 Snort nostr 地址", "Gcn9NQ": "磁力鏈接", "GspYR7": "{n} 踩", + "Gxcr08": "Broadcast Event", "H+vHiz": "十六進制密鑰...", "H0JBH6": "登出", "H6/kLh": "訂單已支付!", "HAlOn1": "名稱", - "HF4YnO": "觀看直播!", "HFls6j": "名稱稍後可用", "HOzFdo": "已静音", "HWbkEK": "清除緩存並重新加載", "HbefNb": "打開錢包", + "HhcAVH": "你不關注此用戶,點擊此處從{link}加載多媒體,或更新你的選項來自動加載來自任何人的多媒體。", "IDjHJ6": "感謝你使用 Snort,請考慮捐贈。", "IEwZvs": "是否確定要取消置頂此條筆記?", "IKKHqV": "關注", "INSqIz": "推特用戶名", "IUZC+0": "這意味著沒有人能夠篡改你創建的筆記,並且任何人都可以輕鬆驗證他們正在閱讀的筆記是否是由你創建的。", "Ig9/a1": "向 {name} 發送了 {n} 聰", + "IoQq+a": "仍然要加載點擊這裡", "Ix8l+B": "熱門筆記", "J+dIsA": "訂閱", "JCIgkj": "用戶名", + "JGrt9q": "將聰發送到 {name}", "JHEHCk": "打閃({n})", "JPFYIM": "沒有閃電地址", "JeoS4y": "轉發", @@ -160,16 +183,19 @@ "K3r6DQ": "刪除", "K7AkdL": "顯示", "KAhAcM": "輸入 LNDHub 配置", - "KLo3SP": "原因:{reason}", "KQvWvD": "已刪除", "KWuDfz": "我已經保存好了我的密鑰,繼續", "KahimY": "未知事件類型:{kind}", "KoFlZg": "輸入鑄幣廠 URL", + "KtsyO0": "Enter Pin", "LF5kYT": "其他連接", + "LR1XjT": "Pin too short", "LXxsbk": "匿名", "LgbKvU": "評論", "Lu5/Bj": "在 Zapstr 上打開", "Lw+I+J": "{n,plural,=0{{name}已打閃} other{{name}和{n}個其他用戶已打閃}}", + "LwYmVi": "此筆記上的打閃將被拆分給以下的用戶。", + "M10zFV": "Nostr Connect", "M3Oirc": "調試選單", "MBAYRO": "每條消息的上下文菜單中顯示“複製 ID”和“複製事件 JSON”", "MI2jkA": "無法使用:", @@ -180,6 +206,7 @@ "MuVeKe": "購買 nostr 地址", "MzRYWH": "購買 {item}", "N2IrpM": "確認", + "NAidKb": "通知", "NAuFNH": "你已經有此類型的訂閱,請續訂或支付", "NNSu3d": "導入推特關注", "NdOYJJ": "嗯在這裡什麼都沒有.. 去看看 {newUsersPage} 以關注一些推薦的 nostrich!", @@ -192,7 +219,6 @@ "OLEm6z": "未知登錄錯誤", "OQXnew": "你的訂閱仍然活躍,你還不能續訂", "ORGv1Q": "已創建", - "P04gQm": "發送到此條筆記的所有打閃將由以下 LNURL 接收。", "P61BTu": "複製事件 JSON", "P7FD0F": "系統(默認)", "P7nJT9": "今天總計(UTC):{amount} 聰", @@ -207,7 +233,6 @@ "QxCuTo": "{name} 的藝術作品", "Qxv0B2": "目前你的打閃池中有 {number} 聰。", "R/6nsx": "訂閱", - "R1fEdZ": "轉發打閃", "R81upa": "你關注的用戶", "RDZVQL": "查看", "RahCRH": "已過期", @@ -217,19 +242,19 @@ "RoOyAh": "中繼器", "Rs4kCE": "收藏", "RwFaYs": "排序", + "SMO+on": "將打閃發送到 {name}", "SOqbe9": "更新閃電地址", "SP0+yi": "購買訂閱", - "SX58hM": "複製", "SYQtZ7": "閃電地址代理", "ShdEie": "全標已讀", "Sjo1P4": "自定義", "Ss0sWu": "立即支付", + "StKzTE": "作者已將此筆記標記為敏感主題", "TDR5ge": "帖子中的媒體將自動顯示給選定的人,否則只會顯示鏈接", - "TMfYfY": "Cashu 代幣", + "TP/cMX": "Ended", "TpgeGw": "十六進制鹽..", "Tpy00S": "用戶", "UDYlxu": "待定訂閱", - "ULotH9": "金額:{amount} 聰", "UT7Nkj": "新聊天", "UUPFlt": "用戶必須接受內容警告才能顯示你的筆記的內容。", "Up5U7K": "屏蔽", @@ -239,15 +264,16 @@ "VR5eHw": "公鑰(npub/nprofile)", "VlJkSk": "{n} 已静音", "VnXp8Z": "頭像", - "VtPV/B": "使用擴展程序登錄 (NIP-07)", "VvaJst": "查看錢包", "Vx7Zm2": "密鑰是如何運作的?", "W1yoZY": "看起來你沒有任何訂閱,你可以獲取一個{link}", "W2PiAr": "{n} 已屏蔽", "W9355R": "解除静音", "WONP5O": "在 nostr 找到你的推特關注(數據由 {provider} 提供)", + "WvGmZT": "npub / nprofile / nostr 地址", "WxthCV": "例如傑克", "X7xU8J": "nsec、nsec、NIP-05、十六進制、助記詞句", + "XECMfW": "傳送使用資料", "XICsE8": "文件主機", "XgWvGA": "回應", "Xopqkl": "你的默認打閃金額是 {number} 聰,示例值是以此計算的。", @@ -260,19 +286,21 @@ "Z4BMCZ": "輸入配對詞句", "ZKORll": "立即激活", "ZLmyG9": "貢獻者", - "ZUZedV": "閃電捐贈:", + "ZS+jRE": "將打閃拆分發送到", "Zr5TMx": "設置個人檔案", "a5UPxh": "資助提供 NIP-05 驗證服務的開發人員和平台", "a7TDNm": "筆記將實時流式傳輸到全球和帖子選項卡", "aWpBzj": "顯示更多", "b12Goz": "助記詞", "b5vAk0": "你的代號將像閃電地址一樣重定向至你所選的 LNURL 或閃電地址", + "bLZL5a": "Get Address", "bQdA2k": "敏感內容", "bep9C3": "公鑰", "bfvyfs": "匿名", "brAXSu": "選擇用戶名", "bxv59V": "剛剛", "c+oiJe": "安装擴展程序", + "c2DTVd": "Enter a pin to encrypt your private key, you must enter this pin every time you open Snort.", "c35bj2": "如果你對 NIP-05 訂單有任何疑問,請私信 {link}", "c3g2hL": "再次廣播", "cFbU1B": "使用 Alby?前往 {link} 來配置你的 NWC!", @@ -287,6 +315,7 @@ "d7d0/x": "閃電地址", "dOQCL8": "顯示名稱", "e61Jf3": "即將上線", + "e7VmYP": "Enter pin to unlock your private key", "e7qqly": "全標已讀", "eHAneD": "回應表情符號", "eJj8HD": "獲取驗證", @@ -297,6 +326,7 @@ "fWZYP5": "置頂", "filwqD": "讀", "flnGvv": "你在想些什麼?", + "fqwcJ1": "On-chain Donation", "fsB/4p": "已保存", "g5pX+a": "關於", "g985Wp": "發送投票失敗", @@ -310,9 +340,9 @@ "h8XMJL": "徽章", "hK5ZDk": "世界", "hMzcSq": "消息", - "hWSp+B": "Nostr 連接(NIP-46)", "hY4lzx": "支持", "hicxcO": "顯示回覆", + "hmZ3Bz": "多媒體", "hniz8Z": "這裡", "i/dBAR": "打閃池", "iCqGww": "回應({n})", @@ -320,7 +350,6 @@ "iEoXYx": "DeepL 翻譯", "iGT1eE": "防止虛假帳戶冒充你", "iNWbVV": "代號", - "iUsU2x": "鑄幣廠:{url}", "iXPL0Z": "無法在不安全的連接上使用私鑰登錄,請使用 nostr 密鑰管理器擴展程序", "ieGrWo": "關注", "itPgxd": "個人檔案", @@ -349,7 +378,7 @@ "lsNFM1": "點擊以從 {link} 加載內容", "lvlPhZ": "支付發票", "mErPop": "看起來你還沒有 nostr 地址,查看 {link} 以購買!", - "mH91FY": "每個貢獻者將獲得使用捐贈和 NIP-05 訂單的一定百分比的報酬,你可以在下面看到分帳金額", + "mH91FY": "每個貢獻者將獲得使用捐贈和 NIP-05 訂單的一定百分比的報酬,你可以在下面看到拆分金額", "mKAr6h": "全部關注", "mKh2HS": "文件上傳服務", "mKhgP9": "{n,plural,=0{} =1{已打閃} other{已打閃}}", @@ -381,9 +410,11 @@ "qMx1sA": "默認打閃金額", "qUJTsT": "已屏蔽", "qdGuQo": "你的私鑰是(不要與任何人分享)", + "qfmMQh": "This note has been muted", "qkvYUb": "添加至個人檔案", "qmJ8kD": "翻譯失敗", "qtWLmt": "點贊", + "qz9fty": "Incorrect pin", "r3C4x/": "軟件", "r5srDR": "輸入錢包密碼", "rT14Ow": "添加中繼器", @@ -392,7 +423,10 @@ "rmdsT4": "{n}天", "rrfdTe": "這與比特幣使用的技術系統,並已被證明非常安全。", "rudscU": "加載關注失敗,請稍候重試", + "sKDn4e": "顯示徽章", + "sUNhQE": "用戶", "sWnYKw": "Snort 旨在提供類似推特的體驗。", + "sZQzjQ": "解析打閃拆分失敗了:{input}", "svOoEH": "不允許搶註和冒充。Snort 和我們的合作夥伴保留因違反此規則而終止你的代號(不是你的帳戶 - 沒有人可以拿走它)的權利。", "tOdNiY": "深色", "th5lxp": "將筆記發送到你的寫入中繼器的子集", @@ -400,13 +434,12 @@ "ttxS0b": "支持者徽章", "u/vOPu": "已支付", "u4bHcR": "在此處查看代碼:{link}", - "uD/N6c": "打閃 {target} {n} 聰", "uSV4Ti": "轉發需要人工確認", + "uc0din": "將聰拆分發送到", "usAvMr": "編輯個人檔案", "ut+2Cd": "獲取合作夥伴標識符", "v8lolG": "開始聊天", "vOKedj": "{n,plural,=1{和{n}個其他} other{和{n}個其他}}", - "vU71Ez": "正在使用 {wallet} 支付", "vZ4quW": "NIP-05 是一種基於 DNS 的驗證規範,可幫助驗證你是真實用戶。", "vhlWFg": "投票選項", "vlbWtt": "獲取免費的", @@ -414,14 +447,16 @@ "vxwnbh": "適用於所有發佈事件的工作量", "wEQDC6": "編輯", "wLtRCF": "你的密鑰", + "wSZR47": "Submit", "wWLwvh": "匿名", "wYSD2L": "Nostr 地址", "wih7iJ": "名稱已被屏蔽", + "wofVHy": "Moderation", "wqyN/i": "在 {link} 瞭解有關 {service} 的更多信息", "wtLjP6": "複製ID", "x/Fx2P": "將部份打閃分配到資金庫來資助你使用的服務!", - "x/q8d5": "此條筆記被標記為敏感內容,點擊此處顯示", "x82IOl": "静音", + "xIcAOU": "根據 {type} 的投票", "xIoGG9": "前往", "xJ9n2N": "你的公鑰", "xKflGN": "{username} 在 nostr 的關注", @@ -433,11 +468,13 @@ "y1Z3or": "語言", "yCLnBC": "LNURL 或閃電地址", "yCmnnm": "全球提要來自", + "zCb8fX": "權重", "zFegDD": "聯絡", "zINlao": "所有者", "zQvVDJ": "全部", "zcaOTs": "以聰為單位的打閃金額", "zjJZBd": "你已經準備就緒!", "zonsdq": "加載 LNURL 服務失敗", - "zvCDao": "自動顯示最新筆記" + "zvCDao": "自動顯示最新筆記", + "zwb6LR": "Mint: {url}" } diff --git a/packages/app/src/useCopy.ts b/packages/app/src/useCopy.ts index dde650bb..32892ac3 100644 --- a/packages/app/src/useCopy.ts +++ b/packages/app/src/useCopy.ts @@ -5,10 +5,21 @@ export const useCopy = (timeout = 2000) => { const [copied, setCopied] = useState(false); const copy = async (text: string) => { + setError(false); try { - await navigator.clipboard.writeText(text); + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(text); + } else { + const textArea = document.createElement("textarea"); + textArea.value = text; + textArea.style.position = "absolute"; + textArea.style.opacity = "0"; + document.body.appendChild(textArea); + textArea.select(); + await document.execCommand("copy"); + textArea.remove(); + } setCopied(true); - setError(false); } catch (error) { setError(true); } diff --git a/packages/app/webpack.config.js b/packages/app/webpack.config.js index 1708f242..fa0d3d7e 100644 --- a/packages/app/webpack.config.js +++ b/packages/app/webpack.config.js @@ -9,6 +9,8 @@ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); const CopyPlugin = require("copy-webpack-plugin"); const WorkboxPlugin = require("workbox-webpack-plugin"); const IntlTsTransformer = require("@formatjs/ts-transformer"); +const { DefinePlugin } = require("webpack"); +const appConfig = require("config"); const isProduction = process.env.NODE_ENV == "production"; @@ -19,6 +21,7 @@ const config = { import: require.resolve("@snort/system/dist/pow-worker.js"), filename: "pow.js", }, + bench: "./src/benchmarks.ts", }, target: "browserslist", mode: isProduction ? "production" : "development", @@ -46,8 +49,16 @@ const config = { }), new HtmlWebpackPlugin({ template: "public/index.html", - favicon: "public/favicon.ico", - excludeChunks: ["pow"], + favicon: appConfig.get("favicon"), + excludeChunks: ["pow", "bench"], + templateParameters: { + appTitle: appConfig.get("appTitle"), + }, + }), + new HtmlWebpackPlugin({ + filename: "bench.html", + template: "public/bench.html", + chunks: ["bench"], }), new ESLintPlugin({ extensions: ["js", "mjs", "jsx", "ts", "tsx"], @@ -63,6 +74,11 @@ const config = { swSrc: "./src/service-worker.ts", }) : false, + new DefinePlugin({ + "process.env.APP_NAME": JSON.stringify(appConfig.get("appName")), + "process.env.APP_NAME_CAPITALIZED": JSON.stringify(appConfig.get("appNameCapitalized")), + "process.env.NIP05_DOMAIN": JSON.stringify(appConfig.get("nip05Domain")), + }), ], module: { rules: [ @@ -115,7 +131,7 @@ const config = { use: [MiniCssExtractPlugin.loader, require.resolve("css-loader")], }, { - test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif|webp)$/i, + test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif|webp|wasm)$/i, type: "asset", }, ], diff --git a/packages/nostr/.eslintrc.cjs b/packages/nostr/.eslintrc.cjs deleted file mode 100644 index b732448c..00000000 --- a/packages/nostr/.eslintrc.cjs +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = { - extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint"], - root: true, - ignorePatterns: ["dist/", "src/legacy"], - env: { - browser: true, - node: true, - mocha: true, - }, - rules: { - "require-await": "error", - eqeqeq: "error", - "object-shorthand": "warn", - }, -} diff --git a/packages/nostr/.gitignore b/packages/nostr/.gitignore deleted file mode 100644 index 849ddff3..00000000 --- a/packages/nostr/.gitignore +++ /dev/null @@ -1 +0,0 @@ -dist/ diff --git a/packages/nostr/.prettierignore b/packages/nostr/.prettierignore deleted file mode 100644 index 77731f20..00000000 --- a/packages/nostr/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -dist/ -src/legacy diff --git a/packages/nostr/README.md b/packages/nostr/README.md deleted file mode 100644 index c7845da2..00000000 --- a/packages/nostr/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# `@snort/nostr` - -A strongly-typed nostr client for Node and the browser. - -## NIP support - -### Applicable - -The goal of the project is to have all of the following implemented -and tested against a real-world relay implementation. - -_Progress: 8/34 (23%)._ - -- [x] NIP-01: Basic protocol flow description -- [x] NIP-02: Contact List and Petnames -- [ ] NIP-03: OpenTimestamps Attestations for Events -- [x] NIP-04: Encrypted Direct Message -- [x] NIP-05: Mapping Nostr keys to DNS-based internet identifiers -- [ ] NIP-06: Basic key derivation from mnemonic seed phrase -- [ ] NIP-07: window.nostr capability for web browsers -- [x] NIP-09: Event Deletion -- [ ] NIP-10: Conventions for clients' use of `e` and `p` tags in text events - - TODO Check if this applies -- [x] NIP-11: Relay Information Document -- [x] NIP-12: Generic Tag Queries -- [ ] NIP-13: Proof of Work -- [ ] NIP-14: Subject tag in text events -- [x] NIP-15: End of Stored Events Notice -- [ ] NIP-19: bech32-encoded entities - - [x] `npub` - - [x] `nsec` - - [ ] `note`, `nprofile`, `nevent`, `nrelay`, `naddr` -- [x] NIP-20: Command Results -- [ ] NIP-21: `nostr:` URL scheme -- [ ] NIP-23: Long-form Content -- [ ] NIP-25: Reactions -- [ ] NIP-26: Delegated Event Signing -- [ ] NIP-27: Text Note References -- [ ] NIP-28: Public Chat -- [ ] NIP-36: Sensitive Content -- [ ] NIP-39: External Identities in Profiles -- [ ] NIP-40: Expiration Timestamp -- [ ] NIP-42: Authentication of clients to relays -- [ ] NIP-46: Nostr Connect - - Not sure how much of this applies, but I sure would love to see WalletConnect disappear -- [ ] NIP-50: Keywords filter -- [ ] NIP-51: Lists -- [ ] NIP-56: Reporting -- [ ] NIP-57: Lightning Zaps -- [ ] NIP-58: Badges -- [ ] NIP-65: Relay List Metadata -- [ ] NIP-78: Application-specific data -- [x] NIP-94: File Header - -### Not Applicable - -These NIPs only apply to relays and have no implications for a generic nostr client. - -- NIP-16: Event Treatment -- NIP-22: Event `created_at` Limits -- NIP-33: Parameterized Replaceable Events - -### Others - -_If you notice an accepted NIP missing from both lists above, please [open an -issue](https://https://git.v0l.io/Kieran/snort/issues/new?assignees=&labels=&template=feature_request.md&title=) -to let us know_. diff --git a/packages/nostr/docker-compose.yaml b/packages/nostr/docker-compose.yaml deleted file mode 100644 index b0f18438..00000000 --- a/packages/nostr/docker-compose.yaml +++ /dev/null @@ -1,33 +0,0 @@ -version: "3.1" -services: - well-known: - build: ./docker/well-known - restart: on-failure - - relay: - build: ./docker/relay - restart: on-failure - - well-known-proxy: - build: ./docker/cors-proxy - restart: on-failure - environment: - TARGET: "well-known:80" - ports: - - 12647:80 - - relay-proxy: - build: ./docker/cors-proxy - restart: on-failure - environment: - TARGET: "relay:8080" - ports: - - 12648:80 - - relay-restart-proxy: - build: ./docker/cors-proxy - restart: on-failure - environment: - TARGET: "relay:8000" - ports: - - 12649:80 diff --git a/packages/nostr/docker/cors-proxy/Dockerfile b/packages/nostr/docker/cors-proxy/Dockerfile deleted file mode 100644 index dbaa0400..00000000 --- a/packages/nostr/docker/cors-proxy/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -# An nginx proxy which adds "Access-Control-Allow-Origin: *" to responses. - -FROM nginx -COPY config.sh /docker-entrypoint.d/ diff --git a/packages/nostr/docker/cors-proxy/config.sh b/packages/nostr/docker/cors-proxy/config.sh deleted file mode 100755 index d1a4484c..00000000 --- a/packages/nostr/docker/cors-proxy/config.sh +++ /dev/null @@ -1,24 +0,0 @@ -echo "\ -map \$http_upgrade \$connection_upgrade {\ - default upgrade;\ - '' close;\ -}\ -\ -proxy_read_timeout 600s;\ -\ -server {\ - listen 80;\ - server_name default;\ -\ - location / {\ - proxy_pass http://$TARGET;\ - proxy_http_version 1.1;\ - proxy_set_header Upgrade \$http_upgrade;\ - proxy_set_header Connection \$connection_upgrade;\ - # The NIP defines that the relay should return Access-Control-Allow-Origin: * here, so don't do it twice.\n\ - if (\$http_accept != 'application/nostr+json') {\ - add_header 'Access-Control-Allow-Origin' '*';\ - }\ - }\ -}" > /etc/nginx/conf.d/default.conf -cat /etc/nginx/conf.d/default.conf diff --git a/packages/nostr/docker/relay/.dockerignore b/packages/nostr/docker/relay/.dockerignore deleted file mode 100644 index 1b3037c1..00000000 --- a/packages/nostr/docker/relay/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -Dockerfile -node_modules/ diff --git a/packages/nostr/docker/relay/.gitignore b/packages/nostr/docker/relay/.gitignore deleted file mode 100644 index c2658d7d..00000000 --- a/packages/nostr/docker/relay/.gitignore +++ /dev/null @@ -1 +0,0 @@ -node_modules/ diff --git a/packages/nostr/docker/relay/Dockerfile b/packages/nostr/docker/relay/Dockerfile deleted file mode 100644 index 1c85f840..00000000 --- a/packages/nostr/docker/relay/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM scsibug/nostr-rs-relay - -USER root -RUN apt-get update && apt-get install -y curl nodejs npm -RUN npm i -g yarn - -EXPOSE 8000 -EXPOSE 8080 - -COPY . . -USER $APP_USER -RUN yarn -CMD ["/bin/bash", "-c", "while :; do yarn app /bin/bash -c 'rm -rf /usr/src/app/db/* && ./nostr-rs-relay --db /usr/src/app/db --config ./config.toml'; done;"] diff --git a/packages/nostr/docker/relay/config.toml b/packages/nostr/docker/relay/config.toml deleted file mode 100644 index 10bacad1..00000000 --- a/packages/nostr/docker/relay/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -[info] -relay_url = "wss://nostr.example.com/" -name = "nostr-rs-relay" -description = "nostr-rs-relay description" -contact = "mailto:contact@example.com" -favicon = "favicon.ico" - -[authorization] -nip42_auth = true -# This seems to have no effect. -nip42_dms = true diff --git a/packages/nostr/docker/relay/index.ts b/packages/nostr/docker/relay/index.ts deleted file mode 100644 index 1105f157..00000000 --- a/packages/nostr/docker/relay/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Allows the relay to be shut down with an HTTP request, after which - * docker-compose will restart it. This allows each test to have a clean - * slate. The drawback is that the tests can't run in parallel, so the - * test suite is very slow. A better option would be to have this relay - * server manage the relay completely: star/stop isolated relay instances - * with HTTP requests and allow multiple instances to run at the same - * time so that the tests can be parallelized. - */ - -import http from "node:http" -import { spawn } from "node:child_process" - -const child = spawn(process.argv[2], process.argv.slice(3), { - stdio: "inherit", -}) - -const server = http.createServer((_, res) => { - if (!child.kill(9)) { - console.error("killing the subprocess failed") - } - res.end() - process.exit(1) -}) - -server.listen(8000) diff --git a/packages/nostr/docker/relay/package.json b/packages/nostr/docker/relay/package.json deleted file mode 100644 index d6eba799..00000000 --- a/packages/nostr/docker/relay/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "relay", - "version": "1.0.0", - "main": "index.js", - "license": "MIT", - "scripts": { - "app": "ts-node index.ts" - }, - "dependencies": { - "@types/node": "^18.15.0", - "ts-node": "^10.9.1", - "typescript": "^4.9.5" - } -} diff --git a/packages/nostr/docker/well-known/Dockerfile b/packages/nostr/docker/well-known/Dockerfile deleted file mode 100644 index bbbb8afe..00000000 --- a/packages/nostr/docker/well-known/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -# An nginx server to return a .well-known/nostr.json file for testing. -FROM nginx -RUN mkdir /usr/share/nginx/html/.well-known -COPY nostr.json /usr/share/nginx/html/.well-known/nostr.json diff --git a/packages/nostr/docker/well-known/nostr.json b/packages/nostr/docker/well-known/nostr.json deleted file mode 100644 index 8d268ecf..00000000 --- a/packages/nostr/docker/well-known/nostr.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "names": { - "bob": "be4be3c0c4f2d18f11215087d7d18fad2c9e0269fc50e19d526bdfb11a522a64" - }, - "relays": { - "be4be3c0c4f2d18f11215087d7d18fad2c9e0269fc50e19d526bdfb11a522a64": [ - "ws://example.com" - ] - } -} diff --git a/packages/nostr/package.json b/packages/nostr/package.json deleted file mode 100644 index f4b34860..00000000 --- a/packages/nostr/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@snort/nostr", - "version": "1.0.0", - "main": "dist/lib.js", - "types": "dist/src/index.d.ts", - "scripts": { - "build": "webpack --node-env=production", - "watch": "webpack -w", - "clean": "rm -rf dist", - "test": "ts-mocha --type-check -j 1 --timeout 5s test/test.*.ts", - "test-browser": "ts-node test/browser/server.ts", - "lint": "eslint ." - }, - "devDependencies": { - "@types/events": "^3.0.0", - "@types/expect": "^24.3.0", - "@types/express": "^4.17.17", - "@types/mocha": "^10.0.1", - "@types/node": "^20.4.1", - "@typescript-eslint/eslint-plugin": "^5.53.0", - "@typescript-eslint/parser": "^5.53.0", - "eslint": "^8.34.0", - "express": "^4.18.2", - "mocha": "^10.2.0", - "ts-mocha": "^10.0.0", - "ts-node": "^10.9.1", - "typescript": "^4.9.5" - }, - "prettier": { - "semi": false - }, - "dependencies": { - "@noble/curves": "^1.0.0", - "@noble/hashes": "^1.2.0", - "@types/chai": "^4.3.4", - "@types/uuid": "^9.0.1", - "base64-js": "^1.5.1", - "bech32": "^2.0.0", - "chai": "^4.3.7", - "events": "^3.3.0", - "isomorphic-ws": "^5.0.0", - "ts-loader": "^9.4.2", - "uuid": "^9.0.0", - "webpack": "^5.77.0", - "webpack-cli": "^5.0.1", - "ws": "^8.12.1" - }, - "directories": { - "test": "test" - }, - "browserslist": { - "production": [ - "chrome >= 67", - "edge >= 79", - "firefox >= 68", - "opera >= 54", - "safari >= 14" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "keywords": [], - "author": "", - "license": "ISC", - "description": "" -} diff --git a/packages/nostr/src/client/conn.ts b/packages/nostr/src/client/conn.ts deleted file mode 100644 index 7f3f557d..00000000 --- a/packages/nostr/src/client/conn.ts +++ /dev/null @@ -1,323 +0,0 @@ -import { NostrError, parseJson } from "../common" -import { SubscriptionId } from "." -import { EventId, RawEvent } from "../event" -import WebSocket from "isomorphic-ws" -import { Filters } from "../filters" - -/** - * The connection to a relay. This is the lowest layer of the nostr protocol. - * The only responsibility of this type is to send and receive - * well-formatted nostr messages on the underlying websocket. All other details of the protocol - * are handled by `Nostr`. This type does not know anything about event semantics. - * - * @see Nostr - */ -export class Conn { - readonly #socket: WebSocket - // TODO This should probably be moved to Nostr (ConnState) because deciding whether or not to send a message - // requires looking at relay info which the Conn should know nothing about. - /** - * Messages which were requested to be sent before the websocket was ready. - * Once the websocket becomes ready, these messages will be sent and cleared. - */ - #pending: OutgoingMessage[] = [] - /** - * Callback for errors. - */ - readonly #onError: (err: unknown) => void - - get url(): string { - return this.#socket.url - } - - constructor({ - url, - onMessage, - onOpen, - onClose, - onError, - }: { - url: URL - onMessage: (msg: IncomingMessage) => void - onOpen: () => Promise - onClose: () => void - onError: (err: unknown) => void - }) { - this.#onError = onError - this.#socket = new WebSocket(url) - - // Handle incoming messages. - this.#socket.addEventListener("message", async (msgData) => { - try { - const value = msgData.data.valueOf() - // Validate and parse the message. - if (typeof value !== "string") { - throw new NostrError(`invalid message data: ${value}`) - } - const msg = parseIncomingMessage(value) - onMessage(msg) - } catch (err) { - onError(err) - } - }) - - // When the connection is ready, send any outstanding messages. - this.#socket.addEventListener("open", async () => { - try { - for (const msg of this.#pending) { - this.send(msg) - } - this.#pending = [] - await onOpen() - } catch (e) { - onError(e) - } - }) - - this.#socket.addEventListener("close", () => { - try { - onClose() - } catch (e) { - onError(e) - } - }) - this.#socket.addEventListener("error", onError) - } - - send(msg: OutgoingMessage): void { - try { - if (this.#socket.readyState < WebSocket.OPEN) { - this.#pending.push(msg) - return - } - this.#socket.send(serializeOutgoingMessage(msg), (err) => { - if (err !== undefined && err !== null) { - this.#onError?.(err) - } - }) - } catch (err) { - this.#onError?.(err) - } - } - - close(): void { - try { - this.#socket.close() - } catch (err) { - this.#onError?.(err) - } - } -} - -/** - * A message sent from a relay to the client. - */ -export type IncomingMessage = - | IncomingEvent - | IncomingNotice - | IncomingOk - | IncomingEose - | IncomingAuth - -export type IncomingKind = "event" | "notice" | "ok" | "eose" | "auth" - -/** - * Incoming "EVENT" message. - */ -export interface IncomingEvent { - kind: "event" - subscriptionId: SubscriptionId - event: RawEvent -} - -/** - * Incoming "NOTICE" message. - */ -export interface IncomingNotice { - kind: "notice" - notice: string -} - -/** - * Incoming "OK" message. - */ -export interface IncomingOk { - kind: "ok" - eventId: EventId - ok: boolean - message: string -} - -/** - * Incoming "EOSE" message. - */ -export interface IncomingEose { - kind: "eose" - subscriptionId: SubscriptionId -} - -/** - * Incoming "AUTH" message. - */ -export interface IncomingAuth { - kind: "auth" -} - -/** - * A message sent from the client to a relay. - */ -export type OutgoingMessage = - | OutgoingEvent - | OutgoingOpenSubscription - | OutgoingCloseSubscription - -export type OutgoingKind = "event" | "openSubscription" | "closeSubscription" - -/** - * Outgoing "EVENT" message. - */ -export interface OutgoingEvent { - kind: "event" - event: RawEvent -} - -/** - * Outgoing "REQ" message, which opens a subscription. - */ -export interface OutgoingOpenSubscription { - kind: "openSubscription" - id: SubscriptionId - filters: Filters[] -} - -/** - * Outgoing "CLOSE" message, which closes a subscription. - */ -export interface OutgoingCloseSubscription { - kind: "closeSubscription" - id: SubscriptionId -} - -function serializeOutgoingMessage(msg: OutgoingMessage): string { - if (msg.kind === "event") { - return JSON.stringify(["EVENT", msg.event]) - } else if (msg.kind === "openSubscription") { - // If there are no filters, the client is expected to specify a single empty filter. - const filters = msg.filters.length === 0 ? [{}] : msg.filters - return JSON.stringify(["REQ", msg.id.toString(), ...filters]) - } else if (msg.kind === "closeSubscription") { - return JSON.stringify(["CLOSE", msg.id.toString()]) - } else { - throw new NostrError(`invalid message: ${JSON.stringify(msg)}`) - } -} - -function parseIncomingMessage(data: string): IncomingMessage { - // Parse the incoming data as a nonempty JSON array. - const json = parseJson(data) - if (!(json instanceof Array)) { - throw new NostrError(`incoming message is not an array: ${data}`) - } - if (json.length === 0) { - throw new NostrError(`incoming message is an empty array: ${data}`) - } - - // Handle incoming events. - if (json[0] === "EVENT") { - if (typeof json[1] !== "string") { - throw new NostrError( - `second element of "EVENT" should be a string, but wasn't: ${data}`, - ) - } - if (typeof json[2] !== "object") { - throw new NostrError( - `second element of "EVENT" should be an object, but wasn't: ${data}`, - ) - } - const event = parseEventData(json[2]) - return { - kind: "event", - subscriptionId: json[1], - event, - } - } - - // Handle incoming notices. - if (json[0] === "NOTICE") { - if (typeof json[1] !== "string") { - throw new NostrError( - `second element of "NOTICE" should be a string, but wasn't: ${data}`, - ) - } - return { - kind: "notice", - notice: json[1], - } - } - - // Handle incoming "OK" messages. - if (json[0] === "OK") { - if (typeof json[1] !== "string") { - throw new NostrError( - `second element of "OK" should be a string, but wasn't: ${data}`, - ) - } - if (typeof json[2] !== "boolean") { - throw new NostrError( - `third element of "OK" should be a boolean, but wasn't: ${data}`, - ) - } - if (typeof json[3] !== "string") { - throw new NostrError( - `fourth element of "OK" should be a string, but wasn't: ${data}`, - ) - } - return { - kind: "ok", - eventId: json[1], - ok: json[2], - message: json[3], - } - } - - // Handle incoming "EOSE" messages. - if (json[0] === "EOSE") { - if (typeof json[1] !== "string") { - throw new NostrError( - `second element of "EOSE" should be a string, but wasn't: ${data}`, - ) - } - return { - kind: "eose", - subscriptionId: json[1], - } - } - - // TODO This is incomplete - // Handle incoming "AUTH" messages. - if (json[0] === "AUTH") { - return { - kind: "auth", - } - } - - throw new NostrError(`unknown incoming message: ${data}`) -} - -function parseEventData(json: { [key: string]: unknown }): RawEvent { - if ( - typeof json["id"] !== "string" || - typeof json["pubkey"] !== "string" || - typeof json["created_at"] !== "number" || - typeof json["kind"] !== "number" || - !(json["tags"] instanceof Array) || - !json["tags"].every( - (x) => x instanceof Array && x.every((y) => typeof y === "string"), - ) || - typeof json["content"] !== "string" || - typeof json["sig"] !== "string" - ) { - throw new NostrError(`invalid event: ${JSON.stringify(json)}`) - } - return json as unknown as RawEvent -} diff --git a/packages/nostr/src/client/emitter.ts b/packages/nostr/src/client/emitter.ts deleted file mode 100644 index e9f229ef..00000000 --- a/packages/nostr/src/client/emitter.ts +++ /dev/null @@ -1,226 +0,0 @@ -import Base from "events" -import { Nostr, SubscriptionId } from "." -import { Event, EventId } from "../event" - -/** - * Overrides providing better types for EventEmitter methods. - */ -export class EventEmitter extends Base { - constructor() { - super({ captureRejections: true }) - } - - override addListener(eventName: "newListener", listener: NewListener): this - override addListener( - eventName: "removeListener", - listener: RemoveListener, - ): this - override addListener(eventName: "open", listener: OpenListener): this - override addListener(eventName: "close", listener: CloseListener): this - override addListener(eventName: "event", listener: EventListener): this - override addListener(eventName: "notice", listener: NoticeListener): this - override addListener(eventName: "ok", listener: OkListener): this - override addListener(eventName: "eose", listener: EoseListener): this - override addListener(eventName: "error", listener: ErrorListener): this - override addListener(eventName: EventName, listener: Listener): this { - return super.addListener(eventName, listener) - } - - override emit(eventName: "newListener", listener: NewListener): boolean - override emit(eventName: "removeListener", listener: RemoveListener): boolean - override emit(eventName: "open", relay: URL, nostr: Nostr): boolean - override emit(eventName: "close", relay: URL, nostr: Nostr): boolean - override emit(eventName: "event", params: EventParams, nostr: Nostr): boolean - override emit(eventName: "notice", notice: string, nostr: Nostr): boolean - override emit(eventName: "ok", params: OkParams, nostr: Nostr): boolean - override emit( - eventName: "eose", - subscriptionId: SubscriptionId, - nostr: Nostr, - ): boolean - override emit(eventName: "error", err: unknown, nostr: Nostr): boolean - override emit(eventName: EventName, ...args: unknown[]): boolean { - return super.emit(eventName, ...args) - } - - override eventNames(): EventName[] { - return super.eventNames() as EventName[] - } - - override listeners(eventName: "newListener"): EventListener[] - override listeners(eventName: "removeListener"): EventListener[] - override listeners(eventName: "open"): OpenListener[] - override listeners(eventName: "close"): CloseListener[] - override listeners(eventName: "event"): EventListener[] - override listeners(eventName: "notice"): NoticeListener[] - override listeners(eventName: "ok"): OkListener[] - override listeners(eventName: "eose"): EoseListener[] - override listeners(eventName: "error"): ErrorListener[] - override listeners(eventName: EventName): Listener[] { - return super.listeners(eventName) as Listener[] - } - - override off(eventName: "newListener", listener: NewListener): this - override off(eventName: "removeListener", listener: RemoveListener): this - override off(eventName: "open", listener: OpenListener): this - override off(eventName: "close", listener: CloseListener): this - override off(eventName: "event", listener: EventListener): this - override off(eventName: "notice", listener: NoticeListener): this - override off(eventName: "ok", listener: OkListener): this - override off(eventName: "eose", listener: EoseListener): this - override off(eventName: "error", listener: ErrorListener): this - override off(eventName: EventName, listener: Listener): this { - return super.off(eventName, listener) - } - - override on(eventName: "newListener", listener: NewListener): this - override on(eventName: "removeListener", listener: RemoveListener): this - override on(eventName: "open", listener: OpenListener): this - override on(eventName: "close", listener: CloseListener): this - override on(eventName: "event", listener: EventListener): this - override on(eventName: "notice", listener: NoticeListener): this - override on(eventName: "ok", listener: OkListener): this - override on(eventName: "eose", listener: EoseListener): this - override on(eventName: "error", listener: ErrorListener): this - override on(eventName: EventName, listener: Listener): this { - return super.on(eventName, listener) - } - - override once(eventName: "newListener", listener: NewListener): this - override once(eventName: "removeListener", listener: RemoveListener): this - override once(eventName: "open", listener: OpenListener): this - override once(eventName: "close", listener: CloseListener): this - override once(eventName: "event", listener: EventListener): this - override once(eventName: "notice", listener: NoticeListener): this - override once(eventName: "ok", listener: OkListener): this - override once(eventName: "eose", listener: EoseListener): this - override once(eventName: "error", listener: ErrorListener): this - override once(eventName: EventName, listener: Listener): this { - return super.once(eventName, listener) - } - - override prependListener( - eventName: "newListener", - listener: NewListener, - ): this - override prependListener( - eventName: "removeListener", - listener: RemoveListener, - ): this - override prependListener(eventName: "open", listener: OpenListener): this - override prependListener(eventName: "close", listener: CloseListener): this - override prependListener(eventName: "event", listener: EventListener): this - override prependListener(eventName: "notice", listener: NoticeListener): this - override prependListener(eventName: "ok", listener: OkListener): this - override prependListener(eventName: "eose", listener: EoseListener): this - override prependListener(eventName: "error", listener: ErrorListener): this - override prependListener(eventName: EventName, listener: Listener): this { - return super.prependListener(eventName, listener) - } - - override prependOnceListener( - eventName: "newListener", - listener: NewListener, - ): this - override prependOnceListener( - eventName: "removeListener", - listener: RemoveListener, - ): this - override prependOnceListener(eventName: "open", listener: OpenListener): this - override prependOnceListener( - eventName: "close", - listener: CloseListener, - ): this - override prependOnceListener( - eventName: "event", - listener: EventListener, - ): this - override prependOnceListener( - eventName: "notice", - listener: NoticeListener, - ): this - override prependOnceListener(eventName: "ok", listener: OkListener): this - override prependOnceListener(eventName: "eose", listener: EoseListener): this - override prependOnceListener( - eventName: "error", - listener: ErrorListener, - ): this - override prependOnceListener(eventName: EventName, listener: Listener): this { - return super.prependOnceListener(eventName, listener) - } - - override removeAllListeners(event?: EventName): this { - return super.removeAllListeners(event) - } - - override removeListener(eventName: "newListener", listener: NewListener): this - override removeListener( - eventName: "removeListener", - listener: RemoveListener, - ): this - override removeListener(eventName: "open", listener: OpenListener): this - override removeListener(eventName: "close", listener: CloseListener): this - override removeListener(eventName: "event", listener: EventListener): this - override removeListener(eventName: "notice", listener: NoticeListener): this - override removeListener(eventName: "ok", listener: OkListener): this - override removeListener(eventName: "eose", listener: EoseListener): this - override removeListener(eventName: "error", listener: ErrorListener): this - override removeListener(eventName: EventName, listener: Listener): this { - return super.removeListener(eventName, listener) - } - - override rawListeners(eventName: EventName): Listener[] { - return super.rawListeners(eventName) as Listener[] - } -} - -// TODO Refactor the params to always be a single interface -// TODO Params should always include relay as well -// TODO Params should not include Nostr, `this` should be Nostr -// TODO Ideas for events: "auth" for NIP-42 AUTH, "message" for the raw incoming messages, -// "publish" for published events, "send" for sent messages -type EventName = - | "newListener" - | "removeListener" - | "open" - | "close" - | "event" - | "notice" - | "ok" - | "eose" - | "error" - -type NewListener = (eventName: EventName, listener: Listener) => void -type RemoveListener = (eventName: EventName, listener: Listener) => void -type OpenListener = (relay: URL, nostr: Nostr) => void -type CloseListener = (relay: URL, nostr: Nostr) => void -type EventListener = (params: EventParams, nostr: Nostr) => void -type NoticeListener = (notice: string, nostr: Nostr) => void -type OkListener = (params: OkParams, nostr: Nostr) => void -type EoseListener = (subscriptionId: SubscriptionId, nostr: Nostr) => void -type ErrorListener = (error: unknown, nostr: Nostr) => void - -type Listener = - | NewListener - | RemoveListener - | OpenListener - | CloseListener - | EventListener - | NoticeListener - | OkListener - | EoseListener - | ErrorListener - -// TODO Document this -export interface EventParams { - event: Event - subscriptionId: SubscriptionId -} - -// TODO Document this -export interface OkParams { - eventId: EventId - relay: URL - ok: boolean - message: string -} diff --git a/packages/nostr/src/client/index.ts b/packages/nostr/src/client/index.ts deleted file mode 100644 index 3ffc3865..00000000 --- a/packages/nostr/src/client/index.ts +++ /dev/null @@ -1,334 +0,0 @@ -import { NostrError } from "../common" -import { RawEvent, parseEvent } from "../event" -import { Conn } from "./conn" -import * as utils from "@noble/curves/abstract/utils" -import { EventEmitter } from "./emitter" -import { fetchRelayInfo, ReadyState, Relay } from "./relay" -import { Filters } from "../filters" - -/** - * A nostr client. - * - * TODO Document the events here - * TODO When document this type, remember to explicitly say that promise rejections will also be routed to "error"! - */ -export class Nostr extends EventEmitter { - static get CONNECTING(): ReadyState.CONNECTING { - return ReadyState.CONNECTING - } - - static get OPEN(): ReadyState.OPEN { - return ReadyState.OPEN - } - - static get CLOSED(): ReadyState.CLOSED { - return ReadyState.CLOSED - } - - /** - * Open connections to relays. - */ - readonly #conns: ConnState[] = [] - - /** - * Mapping of subscription IDs to corresponding filters. - */ - readonly #subscriptions: Map = new Map() - - /** - * Open a connection and start communicating with a relay. This method recreates all existing - * subscriptions on the new relay as well. If there is already an existing connection, - * this method will only update it with the new options, and an exception will be thrown - * if no options are specified. - */ - open( - url: URL | string, - opts?: { read?: boolean; write?: boolean; fetchInfo?: boolean }, - ): void { - const relayUrl = new URL(url) - - // If the connection already exists, update the options. - const existingConn = this.#conns.find( - (c) => c.relay.url.toString() === relayUrl.toString(), - ) - if (existingConn !== undefined) { - if (opts === undefined) { - throw new NostrError( - `called connect with existing connection ${url}, but options were not specified`, - ) - } - if (opts.read !== undefined) { - existingConn.read = opts.read - } - if (opts.write !== undefined) { - existingConn.write = opts.write - } - return - } - - // Fetch the relay info in parallel to opening the WebSocket connection. - const fetchInfo = - opts?.fetchInfo === false - ? Promise.resolve({}) - : fetchRelayInfo(relayUrl).catch((e) => { - this.#error(e) - return {} - }) - - // If there is no existing connection, open a new one. - const conn = new Conn({ - url: relayUrl, - - // Handle messages on this connection. - onMessage: (msg) => { - if (msg.kind === "event") { - this.emit( - "event", - { - event: parseEvent(msg.event), - subscriptionId: msg.subscriptionId, - }, - this, - ) - } else if (msg.kind === "notice") { - this.emit("notice", msg.notice, this) - } else if (msg.kind === "ok") { - this.emit( - "ok", - { - eventId: msg.eventId, - relay: relayUrl, - ok: msg.ok, - message: msg.message, - }, - this, - ) - } else if (msg.kind === "eose") { - this.emit("eose", msg.subscriptionId, this) - } else if (msg.kind === "auth") { - // TODO This is incomplete - } else { - this.#error(new NostrError(`invalid message ${JSON.stringify(msg)}`)) - } - }, - - // Handle "open" events. - onOpen: async () => { - // Update the connection readyState. - const conn = this.#conns.find( - (c) => c.relay.url.toString() === relayUrl.toString(), - ) - if (conn === undefined) { - this.#error( - new NostrError( - `bug: expected connection to ${relayUrl.toString()} to be in the map`, - ), - ) - } else { - if (conn.relay.readyState !== ReadyState.CONNECTING) { - this.#error( - new NostrError( - `bug: expected connection to ${relayUrl.toString()} to have readyState CONNECTING, got ${ - conn.relay.readyState - }`, - ), - ) - } - conn.relay = { - ...conn.relay, - readyState: ReadyState.OPEN, - info: await fetchInfo, - } - } - // Forward the event to the user. - this.emit("open", relayUrl, this) - }, - - // Handle "close" events. - onClose: () => { - // Update the connection readyState. - const conn = this.#conns.find( - (c) => c.relay.url.toString() === relayUrl.toString(), - ) - if (conn === undefined) { - this.#error( - new NostrError( - `bug: expected connection to ${relayUrl.toString()} to be in the map`, - ), - ) - } else { - conn.relay.readyState = ReadyState.CLOSED - } - // Forward the event to the user. - this.emit("close", relayUrl, this) - }, - - // TODO If there is no error handler, this will silently swallow the error. Maybe have an - // #onError method which re-throws if emit() returns false? This should at least make - // some noise. - // Forward errors on this connection. - onError: (err) => this.#error(err), - }) - - // Resend existing subscriptions to this connection. - for (const [key, filters] of this.#subscriptions.entries()) { - conn.send({ - kind: "openSubscription", - id: key, - filters, - }) - } - - this.#conns.push({ - relay: { - url: relayUrl, - readyState: ReadyState.CONNECTING, - }, - conn, - auth: false, - read: opts?.read ?? true, - write: opts?.write ?? true, - }) - } - - /** - * Close connections to relays. - * - * @param url If specified, only close the connection to this relay. If the connection does - * not exist, an exception will be thrown. If this parameter is not specified, all connections - * will be closed. - */ - close(url?: URL | string): void { - if (url === undefined) { - for (const { conn } of this.#conns.values()) { - conn.close() - } - return - } - const relayUrl = new URL(url) - const c = this.#conns.find( - (c) => c.relay.url.toString() === relayUrl.toString(), - ) - if (c === undefined) { - throw new NostrError(`connection to ${url} doesn't exist`) - } - c.conn.close() - } - - /** - * Check if a subscription exists. - */ - subscribed(subscriptionId: SubscriptionId): boolean { - return this.#subscriptions.has(subscriptionId.toString()) - } - - /** - * Create a new subscription. If the subscription already exists, it will be overwritten (as per NIP-01). - * - * @param filters The filters to apply to this message. If any filter passes, the message is let through. - * @param subscriptionId An optional subscription ID, otherwise a random subscription ID will be used. - * @returns The subscription ID. - */ - subscribe( - filters: Filters[], - subscriptionId: SubscriptionId = randomSubscriptionId(), - ): SubscriptionId { - this.#subscriptions.set(subscriptionId, filters) - for (const { conn, read } of this.#conns.values()) { - if (!read) { - continue - } - conn.send({ - kind: "openSubscription", - id: subscriptionId, - filters, - }) - } - return subscriptionId - } - - /** - * Remove a subscription. If the subscription does not exist, an exception is thrown. - * - * TODO Reference subscribed() - */ - unsubscribe(subscriptionId: SubscriptionId): void { - if (!this.#subscriptions.delete(subscriptionId)) { - throw new NostrError(`subscription ${subscriptionId} does not exist`) - } - for (const { conn, read } of this.#conns.values()) { - if (!read) { - continue - } - conn.send({ - kind: "closeSubscription", - id: subscriptionId, - }) - } - } - - /** - * Publish an event. - */ - publish(event: RawEvent): void { - for (const { conn, write } of this.#conns.values()) { - if (!write) { - continue - } - conn.send({ - kind: "event", - event, - }) - } - } - - /** - * Get the relays which this client has tried to open connections to. - */ - get relays(): Relay[] { - return this.#conns.map(({ relay }) => { - if (relay.readyState === ReadyState.CONNECTING) { - return { ...relay } - } else { - const info = - relay.info === undefined - ? undefined - : // Deep copy of the info. - JSON.parse(JSON.stringify(relay.info)) - return { ...relay, info } - } - }) - } - - #error(e: unknown) { - if (!this.emit("error", e, this)) { - throw e - } - } -} - -interface ConnState { - relay: Relay - conn: Conn - /** - * Has this connection been authenticated via NIP-44 AUTH? - */ - auth: boolean - /** - * Should this connection be used for receiving events? - */ - read: boolean - /** - * Should this connection be used for publishing events? - */ - write: boolean -} - -/** - * A string uniquely identifying a client subscription. - */ -export type SubscriptionId = string - -function randomSubscriptionId(): SubscriptionId { - return utils.bytesToHex(globalThis.crypto.getRandomValues(new Uint8Array(32))) -} diff --git a/packages/nostr/src/client/relay.ts b/packages/nostr/src/client/relay.ts deleted file mode 100644 index fc02d6d5..00000000 --- a/packages/nostr/src/client/relay.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { PublicKey } from "../crypto" -import { NostrError } from "../common" - -export type Relay = - | { - url: URL - readyState: ReadyState.CONNECTING - } - | { - url: URL - readyState: ReadyState.OPEN - info: RelayInfo - } - | { - url: URL - readyState: ReadyState.CLOSED - /** - * If the relay is closed before the opening process is fully finished, - * the relay info may be undefined. - */ - info?: RelayInfo - } - -/** - * The information that a relay broadcasts about itself as defined in NIP-11. - */ -export interface RelayInfo { - name?: string - description?: string - pubkey?: PublicKey - contact?: string - supported_nips?: number[] - software?: string - version?: string - [key: string]: unknown -} - -/** - * The state of a relay connection. - */ -export enum ReadyState { - /** - * The connection has not been established yet. - */ - CONNECTING = 0, - /** - * The connection has been established. - */ - OPEN = 1, - /** - * The connection has been closed, forcefully or gracefully, by either party. - */ - CLOSED = 2, -} - -// TODO Keep in mind this should be part of the public API of the lib -/** - * Fetch the NIP-11 relay info with some reasonable timeout. Throw an error if - * the info is invalid. - */ -export async function fetchRelayInfo(url: URL | string): Promise { - url = new URL(url.toString().trim().replace(/^ws/, "http")) - const abort = new AbortController() - const timeout = setTimeout(() => abort.abort(), 15_000) - const res = await fetch(url, { - signal: abort.signal, - headers: { - Accept: "application/nostr+json", - }, - }) - clearTimeout(timeout) - const info = await res.json() - // Validate the known fields in the JSON. - if (info.name !== undefined && typeof info.name !== "string") { - info.name = undefined - throw new NostrError( - `invalid relay info, expected "name" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - if (info.description !== undefined && typeof info.description !== "string") { - info.description = undefined - throw new NostrError( - `invalid relay info, expected "description" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - if (info.pubkey !== undefined && typeof info.pubkey !== "string") { - info.pubkey = undefined - throw new NostrError( - `invalid relay info, expected "pubkey" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - if (info.contact !== undefined && typeof info.contact !== "string") { - info.contact = undefined - throw new NostrError( - `invalid relay info, expected "contact" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - if (info.supported_nips !== undefined) { - if (info.supported_nips instanceof Array) { - if (info.supported_nips.some((e: unknown) => typeof e !== "number")) { - info.supported_nips = undefined - throw new NostrError( - `invalid relay info, expected "supported_nips" elements to be numbers: ${JSON.stringify( - info, - )}`, - ) - } - } else { - info.supported_nips = undefined - throw new NostrError( - `invalid relay info, expected "supported_nips" to be an array: ${JSON.stringify( - info, - )}`, - ) - } - } - if (info.software !== undefined && typeof info.software !== "string") { - info.software = undefined - throw new NostrError( - `invalid relay info, expected "software" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - if (info.version !== undefined && typeof info.version !== "string") { - info.version = undefined - throw new NostrError( - `invalid relay info, expected "version" to be a string: ${JSON.stringify( - info, - )}`, - ) - } - return info -} diff --git a/packages/nostr/src/common.ts b/packages/nostr/src/common.ts deleted file mode 100644 index b1c542f6..00000000 --- a/packages/nostr/src/common.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * A UNIX timestamp. - */ -export type Timestamp = number - -/** - * Calculate the unix timestamp (seconds since epoch) of the `Date`. If no date is specified, - * return the current unix timestamp. - */ -export function unixTimestamp(date?: Date): Timestamp { - return Math.floor((date ?? new Date()).getTime() / 1000) -} - -/** - * Throw if the parameter is null or undefined. Return the parameter otherwise. - */ -export function defined(v: T | undefined | null): T { - if (v === undefined || v === null) { - throw new NostrError("bug: unexpected undefined") - } - return v -} - -/** - * Parse the JSON and throw a @see {@link NostrError} in case of error. - */ -export function parseJson(data: string) { - try { - return JSON.parse(data) - } catch (e) { - throw new NostrError(`invalid json: ${e}: ${data}`) - } -} - -/** - * The error thrown by this library. - */ -export class NostrError extends Error { - constructor(message?: string) { - super(message) - } -} diff --git a/packages/nostr/src/crypto.ts b/packages/nostr/src/crypto.ts deleted file mode 100644 index 4b382d6c..00000000 --- a/packages/nostr/src/crypto.ts +++ /dev/null @@ -1,186 +0,0 @@ -import * as secp from "@noble/curves/secp256k1" -import * as utils from "@noble/curves/abstract/utils" -import { sha256 as sha } from "@noble/hashes/sha256" -import base64 from "base64-js" -import { bech32 } from "bech32" - -// TODO Use toHex as well as toString? Might be more explicit -// Or maybe replace toString with toHex -// TODO Or maybe always store Uint8Array and properly use the format parameter passed into toString - -/** - * A lowercase hex string. - */ -export type Hex = string - -/** - * A public key encoded as hex. - */ -export type PublicKey = string - -/** - * A private key encoded as hex or bech32 with the "nsec" prefix. - */ -export type HexOrBechPublicKey = string - -/** - * A private key encoded as hex. - */ -export type PrivateKey = string - -/** - * A private key encoded as hex or bech32 with the "nsec" prefix. - */ -export type HexOrBechPrivateKey = string - -/** - * Get a public key corresponding to a private key. - */ -export function getPublicKey(priv: HexOrBechPrivateKey): PublicKey { - priv = parsePrivateKey(priv) - return toHex(secp.schnorr.getPublicKey(priv)) -} - -/** - * Convert the data to lowercase hex. - */ -function toHex(data: Uint8Array): Hex { - return utils.bytesToHex(data).toLowerCase() -} - -/** - * Convert the public key to hex. Accepts a hex or bech32 string with the "npub" prefix. - */ -export function parsePublicKey(key: HexOrBechPublicKey): PublicKey { - return parseKey(key, "npub") -} - -/** - * Convert the private key to hex. Accepts a hex or bech32 string with the "nsec" prefix. - */ -export function parsePrivateKey(key: HexOrBechPrivateKey): PrivateKey { - return parseKey(key, "nsec") -} - -/** - * Convert a public or private key into its hex representation. - */ -function parseKey(key: string, bechPrefix: string): Hex { - // If the key is bech32-encoded, decode it. - if (key.startsWith(bechPrefix)) { - const { words } = bech32.decode(key) - const bytes = Uint8Array.from(bech32.fromWords(words)) - return toHex(bytes) - } - return key -} - -/** - * Get the SHA256 hash of the data, in hex format. - */ -export function sha256(data: Uint8Array): Hex { - return toHex(sha(data)) -} - -/** - * Sign the data using elliptic curve cryptography. - */ -export function schnorrSign(data: Hex, priv: PrivateKey): Hex { - return toHex(secp.schnorr.sign(data, priv)) -} - -/** - * Verify that the elliptic curve signature is correct. - */ -export function schnorrVerify(sig: Hex, data: Hex, key: PublicKey): boolean { - return secp.schnorr.verify(sig.toString(), data.toString(), key.toString()) -} - -export async function aesEncryptBase64( - sender: PrivateKey, - recipient: PublicKey, - plaintext: string, -): Promise { - const sharedPoint = secp.secp256k1.getSharedSecret(sender, "02" + recipient) - const sharedKey = sharedPoint.slice(1, 33) - if (typeof window === "object") { - const key = await window.crypto.subtle.importKey( - "raw", - sharedKey, - { name: "AES-CBC" }, - false, - ["encrypt"], - ) - const iv = window.crypto.getRandomValues(new Uint8Array(16)) - const data = new TextEncoder().encode(plaintext) - const encrypted = await window.crypto.subtle.encrypt( - { - name: "AES-CBC", - iv, - }, - key, - data, - ) - return { - data: base64.fromByteArray(new Uint8Array(encrypted)), - iv: base64.fromByteArray(iv), - } - } else { - const crypto = await import("crypto") - const iv = crypto.randomFillSync(new Uint8Array(16)) - const cipher = crypto.createCipheriv( - "aes-256-cbc", - Buffer.from(sharedKey), - iv, - ) - let encrypted = cipher.update(plaintext, "utf8", "base64") - encrypted += cipher.final("base64") - return { - data: encrypted, - iv: Buffer.from(iv.buffer).toString("base64"), - } - } -} - -export async function aesDecryptBase64( - sender: PublicKey, - recipient: PrivateKey, - { data, iv }: AesEncryptedBase64, -): Promise { - const sharedPoint = secp.secp256k1.getSharedSecret(recipient, "02" + sender) - const sharedKey = sharedPoint.slice(1, 33) - if (typeof window === "object") { - const decodedData = base64.toByteArray(data) - const decodedIv = base64.toByteArray(iv) - const importedKey = await window.crypto.subtle.importKey( - "raw", - sharedKey, - { name: "AES-CBC" }, - false, - ["decrypt"], - ) - const plaintext = await window.crypto.subtle.decrypt( - { - name: "AES-CBC", - iv: decodedIv, - }, - importedKey, - decodedData, - ) - return new TextDecoder().decode(plaintext) - } else { - const crypto = await import("crypto") - const decipher = crypto.createDecipheriv( - "aes-256-cbc", - Buffer.from(sharedKey), - base64.toByteArray(iv), - ) - const plaintext = decipher.update(data, "base64", "utf8") - return plaintext + decipher.final() - } -} - -interface AesEncryptedBase64 { - data: string - iv: string -} diff --git a/packages/nostr/src/event/contact-list.ts b/packages/nostr/src/event/contact-list.ts deleted file mode 100644 index eb1d7db0..00000000 --- a/packages/nostr/src/event/contact-list.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { EventKind, RawEvent, signEvent } from "." -import { NostrError } from "../common" -import { HexOrBechPrivateKey, PublicKey } from "../crypto" - -/** - * Contact list event. - * - * Related NIPs: NIP-02. - */ -export interface ContactList extends RawEvent { - kind: EventKind.ContactList - - /** - * Get the contacts in from the contact list. - */ - getContacts(): Contact[] -} - -/** - * A contact from the contact list. - */ -export interface Contact { - pubkey: PublicKey - relay?: URL - petname?: string -} - -/** - * Create a contact list event. - */ -export function createContactList( - contacts: Contact[], - priv?: HexOrBechPrivateKey, -): Promise { - return signEvent( - { - kind: EventKind.ContactList, - tags: contacts.map((contact) => [ - "p", - contact.pubkey, - contact.relay?.toString() ?? "", - contact.petname ?? "", - ]), - content: "", - getContacts, - }, - priv, - ) -} - -export function getContacts(this: ContactList): Contact[] { - return this.tags - .filter((tags) => tags[0] === "p") - .map((tags) => { - // The first element is the pubkey. - const pubkey = tags[1] - if (pubkey === undefined) { - throw new NostrError( - `missing contact pubkey for contact list event: ${JSON.stringify( - this, - )}`, - ) - } - - // The second element is the optional relay URL. - let relay: URL | undefined - try { - if (tags[2] !== undefined && tags[2] !== "") { - relay = new URL(tags[2]) - } - } catch (e) { - throw new NostrError( - `invalid relay URL for contact list event: ${JSON.stringify(this)}`, - ) - } - - // The third element is the optional petname. - let petname: string | undefined - if (tags[3] !== undefined && tags[3] !== "") { - petname = tags[3] - } - - return { - pubkey, - relay, - petname, - } - }) -} diff --git a/packages/nostr/src/event/deletion.ts b/packages/nostr/src/event/deletion.ts deleted file mode 100644 index f65462a9..00000000 --- a/packages/nostr/src/event/deletion.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { EventId, EventKind, RawEvent, signEvent } from "." -import { NostrError } from "../common" -import { HexOrBechPrivateKey } from "../crypto" - -/** - * A deletion event. Used for marking published events as deleted. - * - * Related NIPs: NIP-09. - */ -export interface Deletion extends RawEvent { - kind: EventKind.Deletion - - /** - * The IDs of events to delete. - */ - getEvents(): EventId[] -} - -/** - * Create a deletion event. - */ -export function createDeletion( - { events, content }: { events: EventId[]; content?: string }, - priv?: HexOrBechPrivateKey, -): Promise { - return signEvent( - { - kind: EventKind.Deletion, - tags: events.map((id) => ["e", id]), - content: content ?? "", - getEvents, - }, - priv, - ) -} - -export function getEvents(this: Deletion): EventId[] { - return this.tags - .filter((tag) => tag[0] === "e") - .map((tag) => { - if (tag[1] === undefined) { - throw new NostrError( - `invalid deletion event tag: ${JSON.stringify(tag)}`, - ) - } - return tag[1] - }) -} diff --git a/packages/nostr/src/event/direct-message.ts b/packages/nostr/src/event/direct-message.ts deleted file mode 100644 index 391a3091..00000000 --- a/packages/nostr/src/event/direct-message.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { EventId, EventKind, RawEvent, signEvent } from "." -import { NostrError } from "../common" -import { - aesDecryptBase64, - aesEncryptBase64, - getPublicKey, - HexOrBechPrivateKey, - parsePrivateKey, - parsePublicKey, - PrivateKey, - PublicKey, -} from "../crypto" - -/** - * An encrypted direct message event. - * - * Related NIPs: NIP-04. - */ -export interface DirectMessage extends RawEvent { - kind: EventKind.DirectMessage - - /** - * Get the message plaintext, or undefined if you are not the recipient. - */ - getMessage(priv?: HexOrBechPrivateKey): Promise - /** - * Get the recipient pubkey. - */ - getRecipient(): PublicKey - /** - * Get the event ID of the previous message. - */ - getPrevious(): EventId | undefined -} - -// TODO Since you already require the private key, maybe this should return the message already signed? -// With NIP-07 the parameter will be optional, then what? -/** - * Create an encrypted direct message event. - */ -export async function createDirectMessage( - { - message, - recipient, - }: { - message: string - recipient: PublicKey - }, - priv?: PrivateKey, -): Promise { - recipient = parsePublicKey(recipient) - if (priv === undefined) { - if ( - typeof window === "undefined" || - window.nostr?.nip04?.encrypt === undefined - ) { - throw new NostrError("private key not specified") - } - const content = await window.nostr.nip04.encrypt(recipient, message) - return await signEvent( - { - kind: EventKind.DirectMessage, - tags: [["p", recipient]], - content, - getMessage, - getRecipient, - getPrevious, - }, - priv, - ) - } else { - priv = parsePrivateKey(priv) - const { data, iv } = await aesEncryptBase64(priv, recipient, message) - return await signEvent( - { - kind: EventKind.DirectMessage, - tags: [["p", recipient]], - content: `${data}?iv=${iv}`, - getMessage, - getRecipient, - getPrevious, - }, - priv, - ) - } -} - -export async function getMessage( - this: DirectMessage, - priv?: HexOrBechPrivateKey, -): Promise { - if (priv !== undefined) { - priv = parsePrivateKey(priv) - } - const [data, iv] = this.content.split("?iv=") - if (data === undefined || iv === undefined) { - throw new NostrError(`invalid direct message content ${this.content}`) - } - if (priv === undefined) { - if ( - typeof window === "undefined" || - window.nostr?.nip04?.decrypt === undefined - ) { - throw new NostrError("private key not specified") - } - return await window.nostr.nip04.decrypt(this.pubkey, this.content) - } else if (getPublicKey(priv) === this.getRecipient()) { - return await aesDecryptBase64(this.pubkey, priv, { data, iv }) - } - return undefined -} - -export function getRecipient(this: DirectMessage): PublicKey { - const recipientTag = this.tags.find((tag) => tag[0] === "p") - if (typeof recipientTag?.[1] !== "string") { - throw new NostrError( - `expected "p" tag to be of type string, but got ${recipientTag?.[1]} in ${JSON.stringify( - this, - )}`, - ) - } - return recipientTag[1] -} - -export function getPrevious(this: DirectMessage): EventId | undefined { - const previousTag = this.tags.find((tag) => tag[0] === "e") - if (previousTag === undefined) { - return undefined - } - if (typeof previousTag[1] !== "string") { - throw new NostrError( - `expected "e" tag to be of type string, but got ${previousTag?.[1]} in ${JSON.stringify( - this, - )}`, - ) - } - return previousTag[1] -} diff --git a/packages/nostr/src/event/index.ts b/packages/nostr/src/event/index.ts deleted file mode 100644 index 62710ca7..00000000 --- a/packages/nostr/src/event/index.ts +++ /dev/null @@ -1,240 +0,0 @@ -import { - PublicKey, - sha256, - schnorrSign, - schnorrVerify, - getPublicKey, - HexOrBechPrivateKey, - parsePrivateKey, -} from "../crypto" -import { Timestamp, unixTimestamp, NostrError } from "../common" -import { TextNote } from "./text" -import { - getUserMetadata, - SetMetadata, - verifyInternetIdentifier, -} from "./set-metadata" -import { - DirectMessage, - getMessage, - getPrevious, - getRecipient, -} from "./direct-message" -import { ContactList, getContacts } from "./contact-list" -import { Deletion, getEvents } from "./deletion" -import "../nostr-object" - -// TODO Add remaining event types - -// TODO -// Think about this more -// Perhaps the best option is for all these factory methods to have an overload which also accept a private -// key as last parameter and return the event already signed -// Or maybe opts: { sign?: boolean | HexOrBechPrivateKey } setting sign to true should use nip07, setting -// it to a string will use that string as the private key - -export enum EventKind { - SetMetadata = 0, // NIP-01 - TextNote = 1, // NIP-01 - RecommendServer = 2, // NIP-01 - ContactList = 3, // NIP-02 - DirectMessage = 4, // NIP-04 - Deletion = 5, // NIP-09 - Repost = 6, // NIP-18 - Reaction = 7, // NIP-25 - Relays = 10002, // NIP-65 - Auth = 22242, // NIP-42 - PubkeyLists = 30000, // NIP-51a - NoteLists = 30001, // NIP-51b - TagLists = 30002, // NIP-51c - ZapRequest = 9734, // NIP 57 - ZapReceipt = 9735, // NIP 57 -} - -/** - * A nostr event in the format that's sent across the wire. - */ -export interface RawEvent { - id: string - pubkey: PublicKey - created_at: Timestamp - kind: EventKind - tags: string[][] - content: string - sig: string - - [key: string]: unknown -} - -export interface Unknown extends RawEvent { - kind: Exclude< - EventKind, - | EventKind.SetMetadata - | EventKind.TextNote - | EventKind.ContactList - | EventKind.DirectMessage - | EventKind.Deletion - > -} - -export type Event = - | SetMetadata - | TextNote - | ContactList - | DirectMessage - | Deletion - | Unknown - -/** - * Event ID encoded as hex. - */ -export type EventId = string - -/** - * An unsigned event. - */ -export type Unsigned = { - [Property in keyof UnsignedWithPubkey as Exclude< - Property, - "pubkey" - >]: T[Property] -} & { - pubkey?: PublicKey -} - -/** - * Same as @see {@link Unsigned}, but with the pubkey field. - */ -type UnsignedWithPubkey = { - [Property in keyof T as Exclude< - Property, - "id" | "sig" | "created_at" - >]: T[Property] -} & { - id?: EventId - sig?: string - created_at?: number -} - -/** - * Add the "id," "sig," and "pubkey" fields to the event. Set "created_at" to the current timestamp - * if missing. Return the event. - */ -export async function signEvent( - event: Unsigned, - priv?: HexOrBechPrivateKey, -): Promise { - event.created_at ??= unixTimestamp() - if (priv !== undefined) { - priv = parsePrivateKey(priv) - event.pubkey = getPublicKey(priv) - const id = serializeEventId( - // This conversion is safe because the pubkey field is set above. - event as unknown as UnsignedWithPubkey, - ) - event.id = id - event.sig = schnorrSign(id, priv) - return event as T - } else { - if (typeof window === "undefined" || window.nostr === undefined) { - throw new NostrError("no private key provided") - } - // Extensions like nos2x expect to receive only the event data, without any of the methods. - const methods: { [key: string]: unknown } = {} - for (const [key, value] of Object.entries(event)) { - if (typeof value === "function") { - methods[key] = value - delete event[key] - } - } - const signed = await window.nostr.signEvent(event) - return { - ...signed, - ...methods, - } - } -} - -/** - * Parse an event from its raw format. - */ -export function parseEvent(event: RawEvent): Event { - if (event.id !== serializeEventId(event)) { - throw new NostrError( - `invalid id ${event.id} for event ${JSON.stringify( - event, - )}, expected ${serializeEventId(event)}`, - ) - } - if (!schnorrVerify(event.sig, event.id, event.pubkey)) { - throw new NostrError(`invalid signature for event ${JSON.stringify(event)}`) - } - - // TODO Validate all the fields. Lowercase hex fields, etc. Make sure everything is correct. - // TODO Also validate that tags have at least one element - - if (event.kind === EventKind.TextNote) { - return { - ...event, - kind: EventKind.TextNote, - } - } - - if (event.kind === EventKind.SetMetadata) { - return { - ...event, - kind: EventKind.SetMetadata, - getUserMetadata, - verifyInternetIdentifier, - } - } - - if (event.kind === EventKind.DirectMessage) { - return { - ...event, - kind: EventKind.DirectMessage, - getMessage, - getRecipient, - getPrevious, - } - } - - if (event.kind === EventKind.ContactList) { - return { - ...event, - kind: EventKind.ContactList, - getContacts, - } - } - - if (event.kind === EventKind.Deletion) { - return { - ...event, - kind: EventKind.Deletion, - getEvents, - } - } - - return { - ...event, - kind: event.kind, - } -} - -function serializeEventId(event: UnsignedWithPubkey): EventId { - const serialized = JSON.stringify([ - 0, - event.pubkey, - event.created_at, - event.kind, - event.tags, - event.content, - ]) - return sha256(Uint8Array.from(charCodes(serialized))) -} - -function* charCodes(data: string): Iterable { - for (let i = 0; i < data.length; i++) { - yield data.charCodeAt(i) - } -} diff --git a/packages/nostr/src/event/set-metadata.ts b/packages/nostr/src/event/set-metadata.ts deleted file mode 100644 index 849a4b00..00000000 --- a/packages/nostr/src/event/set-metadata.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { EventKind, RawEvent, signEvent } from "." -import { NostrError, parseJson } from "../common" -import { HexOrBechPrivateKey } from "../crypto" - -/** - * Set metadata event. Used for disseminating use profile information. - * - * Related NIPs: NIP-01. - */ -export interface SetMetadata extends RawEvent { - kind: EventKind.SetMetadata - - /** - * Get the user metadata specified in this event. - */ - getUserMetadata(): UserMetadata - /** - * Verify the NIP-05 DNS-based internet identifier associated with the user metadata. - * Throws if the internet identifier is invalid or fails verification. - * @param pubkey The public key to use if the event does not specify a pubkey. If the event - * does specify a pubkey - * @return The internet identifier. `undefined` if there is no internet identifier. - */ - verifyInternetIdentifier( - opts?: VerificationOptions, - ): Promise -} - -export interface UserMetadata { - name: string - about: string - picture: string - nip05?: string -} - -/** - * Create a set metadata event. - */ -export function createSetMetadata( - content: UserMetadata, - priv?: HexOrBechPrivateKey, -): Promise { - return signEvent( - { - kind: EventKind.SetMetadata, - tags: [], - content: JSON.stringify(content), - getUserMetadata, - verifyInternetIdentifier, - }, - priv, - ) -} - -export function getUserMetadata(this: SetMetadata): UserMetadata { - const userMetadata = parseJson(this.content) - if ( - typeof userMetadata.name !== "string" || - typeof userMetadata.about !== "string" || - typeof userMetadata.picture !== "string" - ) { - throw new NostrError( - `invalid user metadata ${userMetadata} in ${JSON.stringify(this)}`, - ) - } - return userMetadata -} - -export async function verifyInternetIdentifier( - this: SetMetadata, - opts?: VerificationOptions, -): Promise { - const metadata = this.getUserMetadata() - if (metadata.nip05 === undefined) { - return undefined - } - const [name, domain] = metadata.nip05.split("@") - if ( - name === undefined || - domain === undefined || - !/^[a-zA-Z0-9-_]+$/.test(name) - ) { - throw new NostrError( - `invalid NIP-05 internet identifier: ${metadata.nip05}`, - ) - } - const res = await fetch( - `${ - opts?.https === false ? "http" : "https" - }://${domain}/.well-known/nostr.json?name=${name}`, - { redirect: "error" }, - ) - const wellKnown = await res.json() - const pubkey = wellKnown.names?.[name] - if (pubkey !== this.pubkey) { - throw new NostrError( - `invalid NIP-05 internet identifier: ${ - metadata.nip05 - } pubkey does not match, ${JSON.stringify(wellKnown)}`, - ) - } - const relays = wellKnown.relays?.[pubkey] - if ( - relays !== undefined && - (!(relays instanceof Array) || - relays.some((relay) => typeof relay !== "string")) - ) { - throw new NostrError(`invalid NIP-05 relays: ${wellKnown}`) - } - return { - name, - relays, - } -} - -export interface InternetIdentifier { - name: string - relays?: string[] -} - -export interface VerificationOptions { - https?: boolean -} diff --git a/packages/nostr/src/event/text.ts b/packages/nostr/src/event/text.ts deleted file mode 100644 index 0f3b109d..00000000 --- a/packages/nostr/src/event/text.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { EventKind, RawEvent, signEvent } from "." -import { HexOrBechPrivateKey } from "../crypto" - -/** - * A text note event. Used for transmitting user posts. - * - * Related NIPs: NIP-01. - */ -export interface TextNote extends RawEvent { - kind: EventKind.TextNote -} - -export function createTextNote( - content: string, - priv?: HexOrBechPrivateKey, -): Promise { - return signEvent( - { - kind: EventKind.TextNote, - tags: [], - content, - }, - priv, - ) -} diff --git a/packages/nostr/src/filters.ts b/packages/nostr/src/filters.ts deleted file mode 100644 index 6211fc99..00000000 --- a/packages/nostr/src/filters.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { PublicKey } from "./crypto" -import { EventId, EventKind } from "./event" -import { Timestamp } from "./common" - -/** - * Subscription filters. All filters from the fields must pass for a message to get through. - */ -export interface Filters extends TagFilters { - // TODO Document the filters, document that for the arrays only one is enough for the message to pass - ids?: EventId[] - authors?: string[] - kinds?: EventKind[] - since?: Timestamp - until?: Timestamp - limit?: number - - /** - * Allows for arbitrary, nonstandard extensions. - */ - [key: string]: unknown -} - -/** - * Generic tag queries as defined by NIP-12. - */ -interface TagFilters { - ["#e"]: EventId[] - ["#p"]: PublicKey[] - - ["#a"]: string[] - ["#b"]: string[] - ["#c"]: string[] - ["#d"]: string[] - ["#f"]: string[] - ["#g"]: string[] - ["#h"]: string[] - ["#i"]: string[] - ["#j"]: string[] - ["#k"]: string[] - ["#l"]: string[] - ["#m"]: string[] - ["#n"]: string[] - ["#o"]: string[] - ["#q"]: string[] - ["#r"]: string[] - ["#s"]: string[] - ["#t"]: string[] - ["#u"]: string[] - ["#v"]: string[] - ["#w"]: string[] - ["#x"]: string[] - ["#y"]: string[] - ["#z"]: string[] - - ["#A"]: string[] - ["#B"]: string[] - ["#C"]: string[] - ["#D"]: string[] - ["#E"]: string[] - ["#F"]: string[] - ["#G"]: string[] - ["#H"]: string[] - ["#I"]: string[] - ["#J"]: string[] - ["#K"]: string[] - ["#L"]: string[] - ["#M"]: string[] - ["#N"]: string[] - ["#O"]: string[] - ["#P"]: string[] - ["#Q"]: string[] - ["#R"]: string[] - ["#S"]: string[] - ["#T"]: string[] - ["#U"]: string[] - ["#V"]: string[] - ["#W"]: string[] - ["#X"]: string[] - ["#Y"]: string[] - ["#Z"]: string[] -} diff --git a/packages/nostr/src/index.ts b/packages/nostr/src/index.ts deleted file mode 100644 index 5a4d885e..00000000 --- a/packages/nostr/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import "./nostr-object" - -// TODO This file should only contain re-exports and only re-export what is needed diff --git a/packages/nostr/src/nostr-object.ts b/packages/nostr/src/nostr-object.ts deleted file mode 100644 index da74197d..00000000 --- a/packages/nostr/src/nostr-object.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { PublicKey } from "./crypto" -import { RawEvent, Unsigned } from "./event" - -declare global { - interface Window { - nostr?: { - getPublicKey: () => Promise - signEvent: (event: Unsigned) => Promise - - getRelays?: () => Promise< - Record - > - - nip04?: { - encrypt?: (pubkey: PublicKey, plaintext: string) => Promise - decrypt?: (pubkey: PublicKey, ciphertext: string) => Promise - } - } - } -} diff --git a/packages/nostr/test/browser/index.html b/packages/nostr/test/browser/index.html deleted file mode 100644 index 713ece01..00000000 --- a/packages/nostr/test/browser/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - Tests - - - - -
- -
- - - -
- - - - - - - - - - - diff --git a/packages/nostr/test/browser/server.ts b/packages/nostr/test/browser/server.ts deleted file mode 100644 index e0bda810..00000000 --- a/packages/nostr/test/browser/server.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Serve tests in the browser. - */ - -import express from "express" -import * as path from "path" -import * as fs from "fs" - -const port = 33543 -const app = express() - -app.use("/", (req: express.Request, res: express.Response) => { - if (req.path === "/" || req.path === "/nostr-object") { - const index = fs.readFileSync(path.join(__dirname, "index.html"), { - encoding: "utf8", - }) - const tests = fs - .readdirSync(path.join(__dirname, "..", "..", "dist", "test")) - .filter( - (f) => - f.startsWith("test.") && !f.endsWith(".map") && !f.endsWith(".d.ts"), - ) - .map((src) => ``) - .join("\n") - res.set("Content-Type", "text/html") - res.send(index.replace("", tests)) - res.end() - } else if (req.path === "/favicon.ico") { - res.status(404) - res.end() - } else { - const file = path.join(__dirname, "..", "..", "dist", "test", req.path) - res.sendFile(file, (err) => { - if (err) { - console.error(err) - res.status(404) - } - res.end() - }) - } -}) - -app.listen(port, () => { - console.log(`Browser tests: http://localhost:${port}`) -}) diff --git a/packages/nostr/test/setup.ts b/packages/nostr/test/setup.ts deleted file mode 100644 index 4301badb..00000000 --- a/packages/nostr/test/setup.ts +++ /dev/null @@ -1,148 +0,0 @@ -import "../src/nostr-object" -import { Nostr } from "../src/client" -import { Timestamp, unixTimestamp } from "../src/common" -import { - aesDecryptBase64, - aesEncryptBase64, - parsePrivateKey, - parsePublicKey, - PublicKey, -} from "../src/crypto" -import { RawEvent, signEvent, Unsigned } from "../src/event" - -export const relayUrl = new URL("ws://localhost:12648") - -export interface Setup { - publisher: Nostr - publisherSecret?: string - publisherPubkey: string - subscriber: Nostr - subscriberSecret: string - subscriberPubkey: string - timestamp: Timestamp - url: URL - /** - * Signal that the test is done. Call this instead of the callback provided by - * mocha. This will also take care of test cleanup. - */ - done: (e?: unknown) => void -} - -export async function setup( - done: (e?: unknown) => void, - test: (setup: Setup) => void | Promise, -) { - try { - await restartRelay() - - const publisherPubkey = - "npub1he978sxy7tgc7yfp2zra05v045kfuqnfl3gwr82jd00mzxjj9fjqzw2dg7" - const publisherSecret = - "nsec15fnff4uxlgyu79ua3l7327w0wstrd6x565cx6zze78zgkktmr8vs90j363" - - if (typeof window !== "undefined") { - if (window.location.pathname === "/nostr-object") { - // Mock the global window.nostr object for the publisher. - window.nostr = { - getPublicKey: () => Promise.resolve(parsePublicKey(publisherPubkey)), - signEvent: (event: Unsigned) => - signEvent(event, publisherSecret), - - getRelays: () => Promise.resolve({}), - - nip04: { - encrypt: async (pubkey: PublicKey, plaintext: string) => { - const { data, iv } = await aesEncryptBase64( - parsePrivateKey(publisherSecret), - pubkey, - plaintext, - ) - return `${data}?iv=${iv}` - }, - decrypt: async (pubkey: PublicKey, ciphertext: string) => { - const [data, iv] = ciphertext.split("?iv=") - return await aesDecryptBase64( - pubkey, - parsePrivateKey(publisherSecret), - { - data, - iv, - }, - ) - }, - }, - } - } else { - // Disable the user's nostr extension if they have one. - window.nostr = undefined - } - } - - const publisher = new Nostr() - const subscriber = new Nostr() - - publisher.on("error", done) - subscriber.on("error", done) - - const openPromise = Promise.all([ - new Promise((resolve) => publisher.on("open", resolve)), - new Promise((resolve) => subscriber.on("open", resolve)), - ]) - - publisher.open(relayUrl) - subscriber.open(relayUrl) - - await openPromise - - const result = test({ - publisher, - publisherSecret: - typeof window === "undefined" || window.nostr === undefined - ? publisherSecret - : undefined, - publisherPubkey, - subscriber, - subscriberSecret: - "nsec1fxvlyqn3rugvxwaz6dr5h8jcfn0fe0lxyp7pl4mgntxfzqr7dmgst7z9ps", - subscriberPubkey: - "npub1mtwskm558jugtj724nsgf3jf80c5adl39ttydngrn48250l6xmjqa00yxd", - timestamp: unixTimestamp(), - url: relayUrl, - done: (e?: unknown) => { - publisher.close() - subscriber.close() - done(e) - }, - }) - if (result instanceof Promise) { - await result - } - } catch (e) { - done(e) - } -} - -async function restartRelay() { - // Make a request to the endpoint which will exit the process and cause it to restart. - await fetch("http://localhost:12649") - - // Wait until the relay process is ready. - for (;;) { - const ok = await new Promise((resolve) => { - const nostr = new Nostr() - nostr.on("error", () => { - nostr.close() - resolve(false) - }) - nostr.on("open", () => { - nostr.close() - resolve(true) - }) - nostr.open("ws://localhost:12648", { fetchInfo: false }) - }) - if (ok) { - break - } - await new Promise((resolve) => setTimeout(resolve, 100)) - } -} diff --git a/packages/nostr/test/test.contact-list.ts b/packages/nostr/test/test.contact-list.ts deleted file mode 100644 index 8227af2f..00000000 --- a/packages/nostr/test/test.contact-list.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { assert } from "chai" -import { EventKind } from "../src/event" -import { createContactList } from "../src/event/contact-list" -import { setup } from "./setup" - -describe("contact-list", () => { - it("publish and receive the contact list", (done) => { - setup(done, ({ publisher, subscriber, subscriberSecret, done }) => { - const contacts = [ - { - pubkey: - "db9df52f7fcaf30b2718ad17e4c5521058bb20b95073b5c4ff53221b36447c4f", - relay: undefined, - petname: undefined, - }, - { - pubkey: - "94d5ce4cb06f67cab69a2f6e28e0a795222a74ac6a1dd6223743913cc99eaf37", - relay: new URL("ws://example.com"), - petname: undefined, - }, - { - pubkey: - "e6e9a25dbf3e931c991f43c97378e294c25f59e88adc91eda11ed17249a00c20", - relay: undefined, - petname: "john", - }, - { - pubkey: - "13d629a3a879f2157199491408711ff5e1450002a9f9d8b0ad750f1c6b96661d", - relay: new URL("ws://example2.com"), - petname: "jack", - }, - ] - - subscriber.on("event", ({ event }) => { - assert.strictEqual(event.kind, EventKind.ContactList) - assert.strictEqual(event.content, "") - if (event.kind === EventKind.ContactList) { - assert.deepStrictEqual(event.getContacts(), contacts) - } - done() - }) - - subscriber.subscribe([]) - - // After the subscription event sync is done, publish the test event. - subscriber.on("eose", async () => { - // TODO No signEvent, have a convenient way to do this - publisher.publish(await createContactList(contacts, subscriberSecret)) - }) - }) - }) -}) diff --git a/packages/nostr/test/test.deletion.ts b/packages/nostr/test/test.deletion.ts deleted file mode 100644 index a7459581..00000000 --- a/packages/nostr/test/test.deletion.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { assert } from "chai" -import { EventKind } from "../src/event" -import { parsePublicKey } from "../src/crypto" -import { setup } from "./setup" -import { createTextNote } from "../src/event/text" -import { createDeletion } from "../src/event/deletion" - -describe("deletion", () => { - // Test that a deletion event deletes existing events. Test that the deletion event - // is propagated to subscribers. - it("deletes existing events", (done) => { - setup( - done, - ({ - publisher, - publisherSecret, - publisherPubkey, - subscriber, - timestamp, - done, - }) => { - // The event ID to delete. - let textNoteId: string - // The deletion event ID. - let deletionId: string - - // Expect the deletion event (and not the text note event). - subscriber.on("event", ({ event }) => { - assert.strictEqual(event.kind, EventKind.Deletion) - assert.strictEqual(event.id, deletionId) - assert.strictEqual(event.pubkey, parsePublicKey(publisherPubkey)) - assert.strictEqual(event.created_at, timestamp) - assert.strictEqual(event.content, "") - if (event.kind === EventKind.Deletion) { - assert.deepStrictEqual(event.getEvents(), [textNoteId]) - } - done() - }) - - createTextNote("hello world", publisherSecret).then((textNote) => { - textNoteId = textNote.id - publisher.publish({ - ...textNote, - created_at: timestamp, - }) - }) - - publisher.on("ok", async ({ eventId, ok }) => { - assert.strictEqual(ok, true) - - if (eventId === textNoteId) { - // After the text note has been published, delete it. - const deletion = await createDeletion( - { events: [textNoteId] }, - publisherSecret, - ) - deletionId = deletion.id - publisher.publish({ - ...deletion, - created_at: timestamp, - }) - } - - if (eventId === deletionId) { - // After the deletion has been published, subscribe to the publisher. - subscriber.subscribe([]) - } - }) - }, - ) - }) -}) diff --git a/packages/nostr/test/test.direct-message.ts b/packages/nostr/test/test.direct-message.ts deleted file mode 100644 index bee6038d..00000000 --- a/packages/nostr/test/test.direct-message.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { assert } from "chai" -import { EventKind } from "../src/event" -import { parsePublicKey } from "../src/crypto" -import { setup } from "./setup" -import { createDirectMessage } from "../src/event/direct-message" - -describe("direct-message", () => { - const message = "for your eyes only" - - // Test that the intended recipient can receive and decrypt the direct message. - it("to intended recipient", (done) => { - setup( - done, - ({ - publisher, - publisherPubkey, - publisherSecret, - subscriber, - subscriberPubkey, - subscriberSecret, - timestamp, - done, - }) => { - // Expect the direct message. - subscriber.on( - "event", - async ({ event, subscriptionId: actualSubscriptionId }, nostr) => { - assert.strictEqual(nostr, subscriber) - assert.strictEqual(event.kind, EventKind.DirectMessage) - assert.strictEqual(event.pubkey, parsePublicKey(publisherPubkey)) - assert.strictEqual(actualSubscriptionId, subscriptionId) - assert.ok(event.created_at >= timestamp) - - if (event.kind === EventKind.DirectMessage) { - assert.strictEqual( - event.getRecipient(), - parsePublicKey(subscriberPubkey), - ) - assert.strictEqual( - await event.getMessage(subscriberSecret), - message, - ) - } - - done() - }, - ) - - const subscriptionId = subscriber.subscribe([]) - - subscriber.on("eose", async () => { - const event = await createDirectMessage( - { - message, - recipient: subscriberPubkey, - }, - publisherSecret, - ) - publisher.publish(event) - }) - }, - ) - }) - - // Test that an unintended recipient still receives the direct message event, but cannot decrypt it. - it("to unintended recipient", (done) => { - setup( - done, - ({ - publisher, - publisherPubkey, - publisherSecret, - subscriber, - subscriberSecret, - timestamp, - done, - }) => { - const recipientPubkey = - "npub1u2dl3scpzuwyd45flgtm3wcjgv20j4azuzgevdpgtsvvmqzvc63sz327gc" - - // Expect the direct message. - subscriber.on( - "event", - async ({ event, subscriptionId: actualSubscriptionId }, nostr) => { - try { - assert.strictEqual(nostr, subscriber) - assert.strictEqual(event.kind, EventKind.DirectMessage) - assert.strictEqual(event.pubkey, parsePublicKey(publisherPubkey)) - assert.strictEqual(actualSubscriptionId, subscriptionId) - assert.ok(event.created_at >= timestamp) - - if (event.kind === EventKind.DirectMessage) { - assert.strictEqual( - event.getRecipient(), - parsePublicKey(recipientPubkey), - ) - assert.strictEqual( - await event.getMessage(subscriberSecret), - undefined, - ) - } - - done() - } catch (e) { - done(e) - } - }, - ) - - const subscriptionId = subscriber.subscribe([]) - - subscriber.on("eose", async () => { - // TODO No signEvent, do something more convenient - const event = await createDirectMessage( - { - message, - recipient: recipientPubkey, - }, - publisherSecret, - ) - publisher.publish(event) - }) - }, - ) - }) -}) diff --git a/packages/nostr/test/test.internet-identifier.ts b/packages/nostr/test/test.internet-identifier.ts deleted file mode 100644 index b880ae66..00000000 --- a/packages/nostr/test/test.internet-identifier.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { assert } from "chai" -import { defined } from "../src/common" -import { EventKind } from "../src/event" -import { createSetMetadata } from "../src/event/set-metadata" -import { setup } from "./setup" - -describe("internet-identifier", () => { - it("present", (done) => { - setup(done, ({ publisher, publisherSecret, subscriber, done }) => { - subscriber.on("event", async ({ event }) => { - // Assert that the internet identifier can be verified. - assert.strictEqual(event.kind, EventKind.SetMetadata) - if (event.kind === EventKind.SetMetadata) { - const identifier = await event.verifyInternetIdentifier({ - https: false, - }) - assert.ok(identifier) - const { name, relays } = defined(identifier) - assert.strictEqual(name, "bob") - assert.deepStrictEqual(relays, ["ws://example.com"]) - } - done() - }) - - subscriber.subscribe([]) - - // After the subscription event sync is done, publish the test event. - subscriber.on("eose", async () => { - publisher.publish({ - ...(await createSetMetadata( - { - about: "", - name: "", - picture: "", - nip05: "bob@localhost:12647", - }, - publisherSecret, - )), - }) - }) - }) - }) - - it("missing", (done) => { - setup(done, ({ publisher, publisherSecret, subscriber, done }) => { - subscriber.on("event", async ({ event }) => { - // Assert that undefined is returned if the internet identifier is missing. - assert.strictEqual(event.kind, EventKind.SetMetadata) - if (event.kind === EventKind.SetMetadata) { - const identifier = await event.verifyInternetIdentifier({ - https: false, - }) - assert.strictEqual(identifier, undefined) - } - done() - }) - - subscriber.subscribe([]) - - // After the subscription event sync is done, publish the test event. - subscriber.on("eose", async () => { - publisher.publish({ - ...(await createSetMetadata( - { - about: "", - name: "", - picture: "", - }, - publisherSecret, - )), - }) - }) - }) - }) -}) diff --git a/packages/nostr/test/test.ready-state.ts b/packages/nostr/test/test.ready-state.ts deleted file mode 100644 index bcc96d16..00000000 --- a/packages/nostr/test/test.ready-state.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { assert } from "chai" -import { Nostr } from "../src/client" -import { relayUrl } from "./setup" - -describe("ready state", () => { - it("ready state transitions", (done) => { - const nostr = new Nostr() - - nostr.on("error", done) - - nostr.on("open", () => { - assert.strictEqual(nostr.relays[0].readyState, Nostr.OPEN) - nostr.close() - }) - - nostr.on("close", () => { - assert.strictEqual(nostr.relays[0].readyState, Nostr.CLOSED) - done() - }) - - nostr.open(relayUrl) - assert.strictEqual(nostr.relays[0].readyState, Nostr.CONNECTING) - }) -}) diff --git a/packages/nostr/test/test.relay-info.ts b/packages/nostr/test/test.relay-info.ts deleted file mode 100644 index 20fb0d5d..00000000 --- a/packages/nostr/test/test.relay-info.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { assert } from "chai" -import { Nostr } from "../src/client" -import { setup } from "./setup" - -describe("relay info", () => { - it("fetching relay info", (done) => { - setup(done, ({ publisher, done }) => { - assert.strictEqual(publisher.relays.length, 1) - const relay = publisher.relays[0] - assert.strictEqual(relay.readyState, Nostr.OPEN) - if (relay.readyState === Nostr.OPEN) { - assert.strictEqual(relay.info.name, "nostr-rs-relay") - assert.strictEqual(relay.info.description, "nostr-rs-relay description") - assert.strictEqual(relay.info.pubkey, undefined) - assert.strictEqual(relay.info.contact, "mailto:contact@example.com") - assert.ok((relay.info.supported_nips?.length ?? 0) > 0) - assert.strictEqual( - relay.info.software, - "https://git.sr.ht/~gheartsfield/nostr-rs-relay", - ) - assert.strictEqual(relay.info.version, "0.8.8") - } - done() - }) - }) -}) diff --git a/packages/nostr/test/test.set-metadata.ts b/packages/nostr/test/test.set-metadata.ts deleted file mode 100644 index a8b7a767..00000000 --- a/packages/nostr/test/test.set-metadata.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { assert } from "chai" -import { EventKind } from "../src/event" -import { parsePublicKey } from "../src/crypto" -import { setup } from "./setup" -import { createSetMetadata } from "../src/event/set-metadata" - -describe("set metadata", () => { - const name = "bob" - const about = "this is bob" - const picture = "https://example.com/bob.jpg" - - // Test that a set metadata event can be published by one client and received by the other. - it("publish and receive", (done) => { - setup( - done, - ({ - publisher, - publisherSecret, - publisherPubkey, - subscriber, - timestamp, - done, - }) => { - // Expect the test event. - subscriber.on("event", ({ event }) => { - assert.strictEqual(event.kind, EventKind.SetMetadata) - if (event.kind === EventKind.SetMetadata) { - const user = event.getUserMetadata() - assert.strictEqual(event.pubkey, parsePublicKey(publisherPubkey)) - assert.strictEqual(event.created_at, timestamp) - assert.strictEqual(event.tags.length, 0) - assert.strictEqual(user.name, name) - assert.strictEqual(user.about, about) - assert.strictEqual(user.picture, picture) - } - done() - }) - - subscriber.subscribe([]) - - // After the subscription event sync is done, publish the test event. - subscriber.on("eose", async () => { - publisher.publish({ - ...(await createSetMetadata( - { name, about, picture }, - publisherSecret, - )), - created_at: timestamp, - }) - }) - }, - ) - }) -}) diff --git a/packages/nostr/test/test.text-note.ts b/packages/nostr/test/test.text-note.ts deleted file mode 100644 index e3a4b526..00000000 --- a/packages/nostr/test/test.text-note.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { assert } from "chai" -import { EventKind } from "../src/event" -import { parsePublicKey } from "../src/crypto" -import { setup } from "./setup" -import { createTextNote } from "../src/event/text" - -describe("text note", () => { - const note = "hello world" - - // Test that a text note can be published by one client and received by the other. - it("publish and receive", (done) => { - setup( - done, - ({ - publisher, - publisherSecret, - publisherPubkey, - subscriber, - timestamp, - done, - }) => { - // Expect the test event. - subscriber.on( - "event", - ({ event, subscriptionId: actualSubscriptionId }, nostr) => { - assert.strictEqual(nostr, subscriber) - assert.strictEqual(event.kind, EventKind.TextNote) - assert.strictEqual(event.pubkey, parsePublicKey(publisherPubkey)) - assert.strictEqual(event.created_at, timestamp) - assert.strictEqual(event.content, note) - assert.strictEqual(actualSubscriptionId, subscriptionId) - done() - }, - ) - - const subscriptionId = subscriber.subscribe([]) - - // After the subscription event sync is done, publish the test event. - subscriber.on("eose", async (id, nostr) => { - assert.strictEqual(nostr, subscriber) - assert.strictEqual(id, subscriptionId) - - publisher.publish({ - ...(await createTextNote(note, publisherSecret)), - created_at: timestamp, - }) - }) - }, - ) - }) - - // Test that a client interprets an "OK" message after publishing a text note. - it("publish and ok", function (done) { - setup(done, async ({ publisher, publisherSecret, url, done }) => { - const event = await createTextNote(note, publisherSecret) - publisher.on("ok", (params, nostr) => { - assert.strictEqual(nostr, publisher) - assert.strictEqual(params.eventId, event.id) - assert.strictEqual(params.relay.toString(), url.toString()) - assert.strictEqual(params.ok, true) - done() - }) - publisher.publish(event) - }) - }) -}) diff --git a/packages/nostr/webpack.config.js b/packages/nostr/webpack.config.js deleted file mode 100644 index e4f006cc..00000000 --- a/packages/nostr/webpack.config.js +++ /dev/null @@ -1,43 +0,0 @@ -const fs = require("fs") - -const isProduction = process.env.NODE_ENV == "production" - -const entry = { - lib: "./src/index.ts", -} -if (!isProduction) { - for (const file of fs.readdirSync("./test/")) { - if (/.ts$/.test(file)) { - const name = file.replace(/.ts$/, "") - entry[`test/${name}`] = `./test/${file}` - } - } -} - -module.exports = { - mode: process.env.NODE_ENV || "development", - target: "browserslist", - devtool: isProduction ? "source-map" : "eval", - entry, - resolve: { - extensions: [".ts", ".js"], - fallback: { - crypto: false, - }, - }, - module: { - rules: [{ test: /\.ts$/, use: "ts-loader" }], - }, - output: { - filename: "[name].js", - path: `${__dirname}/dist`, - clean: true, - library: { - type: "umd", - name: "Nostr", - }, - }, - optimization: { - usedExports: true, - }, -} diff --git a/packages/shared/package.json b/packages/shared/package.json index 8fa6fd92..db9bf181 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,12 +1,12 @@ { "name": "@snort/shared", - "version": "1.0.4", + "version": "1.0.6", "description": "Shared components for Snort", "main": "dist/index.js", "types": "dist/index.d.ts", "repository": "https://git.v0l.io/Kieran/snort", "author": "Kieran", - "license": "GPL-3.0-or-later", + "license": "MIT", "scripts": { "build": "rm -rf dist && tsc" }, diff --git a/packages/shared/src/external-store.ts b/packages/shared/src/external-store.ts index 08f9b4fd..21d3f035 100644 --- a/packages/shared/src/external-store.ts +++ b/packages/shared/src/external-store.ts @@ -9,7 +9,7 @@ export interface HookFilter { */ export abstract class ExternalStore { #hooks: Array> = []; - #snapshot: Readonly = {} as Readonly; + #snapshot: TSnapshot = {} as TSnapshot; #changed = true; hook(fn: HookFn) { diff --git a/packages/shared/src/feed-cache.ts b/packages/shared/src/feed-cache.ts index c4be82e7..2ba22c1b 100644 --- a/packages/shared/src/feed-cache.ts +++ b/packages/shared/src/feed-cache.ts @@ -15,7 +15,7 @@ export interface KeyedHookFilter { export abstract class FeedCache { #name: string; #hooks: Array = []; - #snapshot: Readonly> = []; + #snapshot: Array = []; #changed = true; #hits = 0; #miss = 0; @@ -37,6 +37,10 @@ export abstract class FeedCache { }, 30_000); } + get name() { + return this.#name; + } + async preload() { const keys = (await this.table?.toCollection().primaryKeys()) ?? []; this.onTable = new Set(keys.map(a => a as string)); @@ -111,7 +115,7 @@ export abstract class FeedCache { this.notifyChange([k]); } - async bulkSet(obj: Array) { + async bulkSet(obj: Array | Readonly>) { if (this.table) { await this.table.bulkPut(obj); obj.forEach(a => this.onTable.add(this.key(a))); diff --git a/packages/shared/src/lnurl.ts b/packages/shared/src/lnurl.ts index 34b3c8fc..dbb27ee8 100644 --- a/packages/shared/src/lnurl.ts +++ b/packages/shared/src/lnurl.ts @@ -92,17 +92,6 @@ export class LNURL { return `${username}@${this.#url.hostname}`; } - /** - * Create a NIP-57 zap tag from this LNURL - */ - getZapTag() { - if (this.isLNAddress) { - return ["zap", this.getLNAddress(), "lud16"]; - } else { - return ["zap", this.#url.toString(), "lud06"]; - } - } - async load() { const rsp = await fetch(this.#url); if (rsp.ok) { diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 6ecfc05c..6a8a01f3 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -43,6 +43,10 @@ export function unixNowMs() { return new Date().getTime(); } +export function jitter(n: number) { + return n * 2 * Math.random() - n; +} + export function deepClone(obj: T) { if ("structuredClone" in window) { return structuredClone(obj); diff --git a/packages/system-react/README.md b/packages/system-react/README.md index 33cd694e..2855a667 100644 --- a/packages/system-react/README.md +++ b/packages/system-react/README.md @@ -6,18 +6,16 @@ Sample: ```js import { useMemo } from "react"; -import { useRequestBuilder, useUserProfile } from "@snort/system-react"; -import { FlatNoteStore, NostrSystem, RequestBuilder, TaggedNostrEvent } from "@snort/system"; +import { SnortContext, useRequestBuilder, useUserProfile } from "@snort/system-react"; +import { NostrSystem, NoteCollection, RequestBuilder, TaggedNostrEvent } from "@snort/system"; -// singleton nostr system class const System = new NostrSystem({}); // some bootstrap relays ["wss://relay.snort.social", "wss://nos.lol"].forEach(r => System.ConnectToRelay(r, { read: true, write: false })); export function Note({ ev }: { ev: TaggedNostrEvent }) { - // get profile from cache or request a profile from relays - const profile = useUserProfile(System, ev.pubkey); + const profile = useUserProfile(ev.pubkey); return (
@@ -35,7 +33,7 @@ export function UserPosts(props: { pubkey: string }) { return rb; }, [props.pubkey]); - const data = useRequestBuilder < FlatNoteStore > (System, FlatNoteStore, sub); + const data = useRequestBuilder(NoteCollection, sub); return ( <> {data.data.map(a => ( @@ -46,6 +44,10 @@ export function UserPosts(props: { pubkey: string }) { } export function MyApp() { - return ; + return ( + + + + ); } ``` diff --git a/packages/system-react/package.json b/packages/system-react/package.json index 09539dc9..fc4f28bb 100644 --- a/packages/system-react/package.json +++ b/packages/system-react/package.json @@ -1,12 +1,12 @@ { "name": "@snort/system-react", - "version": "1.0.12", + "version": "1.0.15", "description": "React hooks for @snort/system", "main": "dist/index.js", "types": "dist/index.d.ts", "repository": "https://git.v0l.io/Kieran/snort", "author": "Kieran", - "license": "GPL-3.0-or-later", + "license": "MIT", "scripts": { "build": "rm -rf dist && tsc" }, @@ -15,8 +15,8 @@ "dist" ], "dependencies": { - "@snort/shared": "^1.0.4", - "@snort/system": "^1.0.17", + "@snort/shared": "^1.0.6", + "@snort/system": "^1.0.21", "react": "^18.2.0" }, "devDependencies": { diff --git a/packages/system-svelte/README.md b/packages/system-svelte/README.md new file mode 100644 index 00000000..d34439da --- /dev/null +++ b/packages/system-svelte/README.md @@ -0,0 +1,3 @@ +## @snort/system-svelte + +Svelte hooks for @snort/system diff --git a/packages/system-svelte/package.json b/packages/system-svelte/package.json new file mode 100644 index 00000000..647246dd --- /dev/null +++ b/packages/system-svelte/package.json @@ -0,0 +1,25 @@ +{ + "name": "@snort/system-svelte", + "version": "1.0.0", + "description": "Svelte functions for @snort/system", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "repository": "https://git.v0l.io/Kieran/snort", + "author": "Kieran", + "license": "MIT", + "scripts": { + "build": "rm -rf dist && tsc" + }, + "files": [ + "src", + "dist" + ], + "dependencies": { + "@snort/shared": "^1.0.6", + "@snort/system": "^1.0.21", + "svelte": "^4.2.0" + }, + "devDependencies": { + "typescript": "^5.2.2" + } +} diff --git a/packages/system-svelte/src/index.ts b/packages/system-svelte/src/index.ts new file mode 100644 index 00000000..1c9d102f --- /dev/null +++ b/packages/system-svelte/src/index.ts @@ -0,0 +1 @@ +export * from "./request-builder"; diff --git a/packages/system-svelte/src/request-builder.ts b/packages/system-svelte/src/request-builder.ts new file mode 100644 index 00000000..7202c58c --- /dev/null +++ b/packages/system-svelte/src/request-builder.ts @@ -0,0 +1,20 @@ +import { type NoteStore, type RequestBuilder, type StoreSnapshot, type SystemInterface } from "@snort/system"; +import { getContext } from "svelte"; + +export function useRequestBuilder(type: new () => T, rb: RequestBuilder) { + const system = getContext("snort") as SystemInterface; + type TSnap = StoreSnapshot>; + return { + subscribe: (set: (value: TSnap) => void) => { + const q = system.Query(type, rb); + q.uncancel(); + const release = q.feed.hook(() => { + set(q.feed.snapshot as TSnap); + }); + return () => { + q.cancel(); + release(); + }; + }, + }; +} diff --git a/packages/nostr/tsconfig.json b/packages/system-svelte/tsconfig.json similarity index 61% rename from packages/nostr/tsconfig.json rename to packages/system-svelte/tsconfig.json index 698a4442..b57c5cd3 100644 --- a/packages/nostr/tsconfig.json +++ b/packages/system-svelte/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": "src", "target": "ES2020", "moduleResolution": "node", "esModuleInterop": true, @@ -8,8 +9,10 @@ "strict": true, "declaration": true, "declarationMap": true, - "sourceMap": true, - "outDir": "dist" + "inlineSourceMap": true, + "outDir": "dist", + "skipLibCheck": true }, - "include": ["src", "test"] + "include": ["src/**/*.ts"], + "files": ["src/index.ts"] } diff --git a/packages/system-wasm/.gitignore b/packages/system-wasm/.gitignore new file mode 100644 index 00000000..d6c6ec7e --- /dev/null +++ b/packages/system-wasm/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +*.txt \ No newline at end of file diff --git a/packages/system-wasm/Cargo.lock b/packages/system-wasm/Cargo.lock new file mode 100644 index 00000000..cc9ae096 --- /dev/null +++ b/packages/system-wasm/Cargo.lock @@ -0,0 +1,986 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" + +[[package]] +name = "argon2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ba4cac0a46bc1d2912652a751c47f2a9f3a7fe89bcae2275d418f5270402f9" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a13b88d2c62ff462f88e4a121f17a82c1af05693a2f192b5c38d14de73c19f6" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb9faaa7c2ef94b2743a21f5a29e6f0010dff4caa69ac8e9d6cf8b6fa74da08" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "rustix" +version = "0.38.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha256" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-wasm" +version = "0.1.0" +dependencies = [ + "argon2", + "console_error_panic_hook", + "criterion", + "hex", + "itertools 0.11.0", + "rand", + "serde", + "serde-wasm-bindgen", + "serde_json", + "sha256", + "wasm-bindgen", + "wasm-bindgen-test", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-bindgen-test" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6e302a7ea94f83a6d09e78e7dc7d9ca7b186bc2829c24a22d0753efd680671" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb993dd8c836930ed130e020e77d9b2e65dd0fbab1b67c790b0f5d80b11a575" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/packages/system-wasm/Cargo.toml b/packages/system-wasm/Cargo.toml new file mode 100644 index 00000000..14e657d1 --- /dev/null +++ b/packages/system-wasm/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "system-wasm" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +argon2 = "0.5.2" +console_error_panic_hook = "0.1.7" +hex = { version = "0.4.3", features = [], default-features = false } +itertools = "0.11.0" +serde = { version = "1.0.188", features = ["derive"], default-features = false } +serde-wasm-bindgen = "0.5.0" +sha256 = { version = "1.4.0", features = [], default-features = false } +wasm-bindgen = "0.2.87" + +[dev-dependencies] +rand = "0.8.5" +wasm-bindgen-test = "0.3.37" +serde_json = "1.0.105" +criterion = { version = "0.5" } + +[[bench]] +name = "basic" +harness = false + +[profile.release] +opt-level = 3 +lto = true + +[package.metadata.wasm-pack.profile.release] +wasm-opt = ["-O3"] diff --git a/packages/system-wasm/README.md b/packages/system-wasm/README.md new file mode 100644 index 00000000..35c32495 --- /dev/null +++ b/packages/system-wasm/README.md @@ -0,0 +1 @@ +# system-wasm diff --git a/packages/system-wasm/benches/basic.rs b/packages/system-wasm/benches/basic.rs new file mode 100644 index 00000000..bdefcbf8 --- /dev/null +++ b/packages/system-wasm/benches/basic.rs @@ -0,0 +1,84 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use rand::prelude::*; +use std::collections::HashSet; +use system_wasm::diff::diff_filter; +use system_wasm::filter::{FlatReqFilter, ReqFilter}; +use system_wasm::{Event, pow}; + +fn random_pubkey(rng: &mut ThreadRng) -> String { + let mut bytes = [0u8; 32]; + rng.fill_bytes(&mut bytes); + bytes.iter().map(|byte| format!("{:02x}", byte)).collect() +} + +fn criterion_benchmark(c: &mut Criterion) { + let mut rng = thread_rng(); + let mut random_pubkeys = HashSet::new(); + for _ in 0..50 { + random_pubkeys.insert(random_pubkey(&mut rng)); + } + let input_authors = ReqFilter { + authors: Some(random_pubkeys.clone()), + kinds: Some(HashSet::from([1, 2, 3])), + ids: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + e_tag: None, + }; + + let input_authors_diff = ReqFilter { + authors: Some(random_pubkeys.clone()), + kinds: Some(HashSet::from([1, 2, 3, 4, 5])), + ids: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + e_tag: None, + }; + + + c.bench_function("expand", |b| { + b.iter(|| { + let _: Vec = (&input_authors).into(); + }) + }); + c.bench_function("diff", |b| { + b.iter(|| { + let prev: Vec = (&input_authors).into(); + let next: Vec = (&input_authors_diff).into(); + let _ = diff_filter(&prev, &next); + }) + }); + c.bench_function("pow", |b| { + b.iter(|| { + let mut ev = Event { + id: None, + kind: 1, + created_at: 1234567, + pubkey: "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed".to_string(), + content: "test".to_owned(), + sig: None, + tags: vec![], + }; + pow::pow(&mut ev, 12); + }) + }); +} + +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/packages/system-wasm/package.json b/packages/system-wasm/package.json new file mode 100644 index 00000000..2d268998 --- /dev/null +++ b/packages/system-wasm/package.json @@ -0,0 +1,17 @@ +{ + "name": "@snort/system-wasm", + "version": "1.0.0", + "packageManager": "yarn@3.6.3", + "author": "Kieran", + "license": "MIT", + "scripts": { + "build": "wasm-pack build --release -t web -s snort && rm -f pkg/.gitignore" + }, + "files": [ + "pkg/system_wasm_bg.wasm", + "pkg/system_wasm.js", + "pkg/system_wasm.d.ts" + ], + "module": "pkg/system_wasm.js", + "types": "pkg/system_wasm.d.ts" +} diff --git a/packages/system-wasm/pkg/README.md b/packages/system-wasm/pkg/README.md new file mode 100644 index 00000000..35c32495 --- /dev/null +++ b/packages/system-wasm/pkg/README.md @@ -0,0 +1 @@ +# system-wasm diff --git a/packages/system-wasm/pkg/package.json b/packages/system-wasm/pkg/package.json new file mode 100644 index 00000000..95566d0d --- /dev/null +++ b/packages/system-wasm/pkg/package.json @@ -0,0 +1,14 @@ +{ + "name": "@snort/system-wasm", + "version": "0.1.0", + "files": [ + "system_wasm_bg.wasm", + "system_wasm.js", + "system_wasm.d.ts" + ], + "module": "system_wasm.js", + "types": "system_wasm.d.ts", + "sideEffects": [ + "./snippets/*" + ] +} diff --git a/packages/system-wasm/pkg/system_wasm.d.ts b/packages/system-wasm/pkg/system_wasm.d.ts new file mode 100644 index 00000000..1ebca4b4 --- /dev/null +++ b/packages/system-wasm/pkg/system_wasm.d.ts @@ -0,0 +1,80 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function diff_filters(prev: any, next: any): any; +/** + * @param {any} val + * @returns {any} + */ +export function expand_filter(val: any): any; +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function get_diff(prev: any, next: any): any; +/** + * @param {any} val + * @returns {any} + */ +export function flat_merge(val: any): any; +/** + * @param {any} val + * @returns {any} + */ +export function compress(val: any): any; +/** + * @param {any} val + * @param {any} target + * @returns {any} + */ +export function pow(val: any, target: any): any; +/** + * @param {any} password + * @param {any} salt + * @returns {any} + */ +export function argon2(password: any, salt: any): any; + +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; + +export interface InitOutput { + readonly memory: WebAssembly.Memory; + readonly diff_filters: (a: number, b: number, c: number) => void; + readonly expand_filter: (a: number, b: number) => void; + readonly get_diff: (a: number, b: number, c: number) => void; + readonly flat_merge: (a: number, b: number) => void; + readonly compress: (a: number, b: number) => void; + readonly pow: (a: number, b: number, c: number) => void; + readonly argon2: (a: number, b: number, c: number) => void; + readonly __wbindgen_malloc: (a: number, b: number) => number; + readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number; + readonly __wbindgen_add_to_stack_pointer: (a: number) => number; + readonly __wbindgen_exn_store: (a: number) => void; + readonly __wbindgen_free: (a: number, b: number, c: number) => void; +} + +export type SyncInitInput = BufferSource | WebAssembly.Module; +/** + * Instantiates the given `module`, which can either be bytes or + * a precompiled `WebAssembly.Module`. + * + * @param {SyncInitInput} module + * + * @returns {InitOutput} + */ +export function initSync(module: SyncInitInput): InitOutput; + +/** + * If `module_or_path` is {RequestInfo} or {URL}, makes a request and + * for everything else, calls `WebAssembly.instantiate` directly. + * + * @param {InitInput | Promise} module_or_path + * + * @returns {Promise} + */ +export default function __wbg_init(module_or_path?: InitInput | Promise): Promise; diff --git a/packages/system-wasm/pkg/system_wasm.js b/packages/system-wasm/pkg/system_wasm.js new file mode 100644 index 00000000..7f7ae5af --- /dev/null +++ b/packages/system-wasm/pkg/system_wasm.js @@ -0,0 +1,678 @@ +let wasm; + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { + return heap[idx]; +} + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +const cachedTextEncoder = + typeof TextEncoder !== "undefined" + ? new TextEncoder("utf-8") + : { + encode: () => { + throw Error("TextEncoder not available"); + }, + }; + +const encodeString = + typeof cachedTextEncoder.encodeInto === "function" + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); + } + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length, + }; + }; + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0() + .subarray(ptr, ptr + buf.length) + .set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7f) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +const cachedTextDecoder = + typeof TextDecoder !== "undefined" + ? new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }) + : { + decode: () => { + throw Error("TextDecoder not available"); + }, + }; + +if (typeof TextDecoder !== "undefined") { + cachedTextDecoder.decode(); +} + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +let cachedFloat64Memory0 = null; + +function getFloat64Memory0() { + if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { + cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); + } + return cachedFloat64Memory0; +} + +let cachedBigInt64Memory0 = null; + +function getBigInt64Memory0() { + if (cachedBigInt64Memory0 === null || cachedBigInt64Memory0.byteLength === 0) { + cachedBigInt64Memory0 = new BigInt64Array(wasm.memory.buffer); + } + return cachedBigInt64Memory0; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == "number" || type == "boolean" || val == null) { + return `${val}`; + } + if (type == "string") { + return `"${val}"`; + } + if (type == "symbol") { + const description = val.description; + if (description == null) { + return "Symbol"; + } else { + return `Symbol(${description})`; + } + } + if (type == "function") { + const name = val.name; + if (typeof name == "string" && name.length > 0) { + return `Function(${name})`; + } else { + return "Function"; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = "["; + if (length > 0) { + debug += debugString(val[0]); + } + for (let i = 1; i < length; i++) { + debug += ", " + debugString(val[i]); + } + debug += "]"; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == "Object") { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return "Object(" + JSON.stringify(val) + ")"; + } catch (_) { + return "Object"; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function diff_filters(prev, next) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.diff_filters(retptr, addHeapObject(prev), addHeapObject(next)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function expand_filter(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.expand_filter(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function get_diff(prev, next) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.get_diff(retptr, addHeapObject(prev), addHeapObject(next)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function flat_merge(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.flat_merge(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function compress(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.compress(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @param {any} target + * @returns {any} + */ +export function pow(val, target) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.pow(retptr, addHeapObject(val), addHeapObject(target)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} password + * @param {any} salt + * @returns {any} + */ +export function argon2(password, salt) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.argon2(retptr, addHeapObject(password), addHeapObject(salt)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +async function __wbg_load(module, imports) { + if (typeof Response === "function" && module instanceof Response) { + if (typeof WebAssembly.instantiateStreaming === "function") { + try { + return await WebAssembly.instantiateStreaming(module, imports); + } catch (e) { + if (module.headers.get("Content-Type") != "application/wasm") { + console.warn( + "`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", + e, + ); + } else { + throw e; + } + } + } + + const bytes = await module.arrayBuffer(); + return await WebAssembly.instantiate(bytes, imports); + } else { + const instance = await WebAssembly.instantiate(module, imports); + + if (instance instanceof WebAssembly.Instance) { + return { instance, module }; + } else { + return instance; + } + } +} + +function __wbg_get_imports() { + const imports = {}; + imports.wbg = {}; + imports.wbg.__wbindgen_object_drop_ref = function (arg0) { + takeObject(arg0); + }; + imports.wbg.__wbindgen_string_get = function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "string" ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_is_object = function (arg0) { + const val = getObject(arg0); + const ret = typeof val === "object" && val !== null; + return ret; + }; + imports.wbg.__wbindgen_is_undefined = function (arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; + imports.wbg.__wbindgen_in = function (arg0, arg1) { + const ret = getObject(arg0) in getObject(arg1); + return ret; + }; + imports.wbg.__wbindgen_is_bigint = function (arg0) { + const ret = typeof getObject(arg0) === "bigint"; + return ret; + }; + imports.wbg.__wbindgen_bigint_from_u64 = function (arg0) { + const ret = BigInt.asUintN(64, arg0); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_jsval_eq = function (arg0, arg1) { + const ret = getObject(arg0) === getObject(arg1); + return ret; + }; + imports.wbg.__wbindgen_error_new = function (arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_object_clone_ref = function (arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_jsval_loose_eq = function (arg0, arg1) { + const ret = getObject(arg0) == getObject(arg1); + return ret; + }; + imports.wbg.__wbindgen_boolean_get = function (arg0) { + const v = getObject(arg0); + const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; + return ret; + }; + imports.wbg.__wbindgen_number_get = function (arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "number" ? obj : undefined; + getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); + }; + imports.wbg.__wbindgen_number_new = function (arg0) { + const ret = arg0; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_string_new = function (arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); + }; + imports.wbg.__wbg_getwithrefkey_5e6d9547403deab8 = function (arg0, arg1) { + const ret = getObject(arg0)[getObject(arg1)]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_841ac57cff3d672b = function (arg0, arg1, arg2) { + getObject(arg0)[takeObject(arg1)] = takeObject(arg2); + }; + imports.wbg.__wbg_get_44be0491f933a435 = function (arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); + }; + imports.wbg.__wbg_length_fff51ee6522a1a18 = function (arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_new_898a68150f225f2e = function () { + const ret = new Array(); + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_function = function (arg0) { + const ret = typeof getObject(arg0) === "function"; + return ret; + }; + imports.wbg.__wbg_next_526fc47e980da008 = function (arg0) { + const ret = getObject(arg0).next; + return addHeapObject(ret); + }; + imports.wbg.__wbg_next_ddb3312ca1c4e32a = function () { + return handleError(function (arg0) { + const ret = getObject(arg0).next(); + return addHeapObject(ret); + }, arguments); + }; + imports.wbg.__wbg_done_5c1f01fb660d73b5 = function (arg0) { + const ret = getObject(arg0).done; + return ret; + }; + imports.wbg.__wbg_value_1695675138684bd5 = function (arg0) { + const ret = getObject(arg0).value; + return addHeapObject(ret); + }; + imports.wbg.__wbg_iterator_97f0c81209c6c35a = function () { + const ret = Symbol.iterator; + return addHeapObject(ret); + }; + imports.wbg.__wbg_get_97b561fb56f034b5 = function () { + return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments); + }; + imports.wbg.__wbg_call_cb65541d95d71282 = function () { + return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments); + }; + imports.wbg.__wbg_new_b51585de1b234aff = function () { + const ret = new Object(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_502d29070ea18557 = function (arg0, arg1, arg2) { + getObject(arg0)[arg1 >>> 0] = takeObject(arg2); + }; + imports.wbg.__wbg_isArray_4c24b343cb13cfb1 = function (arg0) { + const ret = Array.isArray(getObject(arg0)); + return ret; + }; + imports.wbg.__wbg_instanceof_ArrayBuffer_39ac22089b74fddb = function (arg0) { + let result; + try { + result = getObject(arg0) instanceof ArrayBuffer; + } catch { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_isSafeInteger_bb8e18dd21c97288 = function (arg0) { + const ret = Number.isSafeInteger(getObject(arg0)); + return ret; + }; + imports.wbg.__wbg_buffer_085ec1f694018c4f = function (arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); + }; + imports.wbg.__wbg_new_8125e318e6245eed = function (arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); + }; + imports.wbg.__wbg_set_5cf90238115182c3 = function (arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); + }; + imports.wbg.__wbg_length_72e2208bbc0efc61 = function (arg0) { + const ret = getObject(arg0).length; + return ret; + }; + imports.wbg.__wbg_instanceof_Uint8Array_d8d9cb2b8e8ac1d4 = function (arg0) { + let result; + try { + result = getObject(arg0) instanceof Uint8Array; + } catch { + result = false; + } + const ret = result; + return ret; + }; + imports.wbg.__wbg_new_abda76e883ba8a5f = function () { + const ret = new Error(); + return addHeapObject(ret); + }; + imports.wbg.__wbg_stack_658279fe44541cf6 = function (arg0, arg1) { + const ret = getObject(arg1).stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbg_error_f851667af71bcfc6 = function (arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } + }; + imports.wbg.__wbindgen_bigint_get_as_i64 = function (arg0, arg1) { + const v = getObject(arg1); + const ret = typeof v === "bigint" ? v : undefined; + getBigInt64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? BigInt(0) : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); + }; + imports.wbg.__wbindgen_debug_string = function (arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; + }; + imports.wbg.__wbindgen_throw = function (arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); + }; + imports.wbg.__wbindgen_memory = function () { + const ret = wasm.memory; + return addHeapObject(ret); + }; + + return imports; +} + +function __wbg_init_memory(imports, maybe_memory) {} + +function __wbg_finalize_init(instance, module) { + wasm = instance.exports; + __wbg_init.__wbindgen_wasm_module = module; + cachedBigInt64Memory0 = null; + cachedFloat64Memory0 = null; + cachedInt32Memory0 = null; + cachedUint8Memory0 = null; + + return wasm; +} + +function initSync(module) { + if (wasm !== undefined) return wasm; + + const imports = __wbg_get_imports(); + + __wbg_init_memory(imports); + + if (!(module instanceof WebAssembly.Module)) { + module = new WebAssembly.Module(module); + } + + const instance = new WebAssembly.Instance(module, imports); + + return __wbg_finalize_init(instance, module); +} + +async function __wbg_init(input) { + if (wasm !== undefined) return wasm; + + if (typeof input === "undefined") { + input = new URL("system_wasm_bg.wasm", import.meta.url); + } + const imports = __wbg_get_imports(); + + if ( + typeof input === "string" || + (typeof Request === "function" && input instanceof Request) || + (typeof URL === "function" && input instanceof URL) + ) { + input = fetch(input); + } + + __wbg_init_memory(imports); + + const { instance, module } = await __wbg_load(await input, imports); + + return __wbg_finalize_init(instance, module); +} + +export { initSync }; +export default __wbg_init; diff --git a/packages/system-wasm/pkg/system_wasm_bg.js b/packages/system-wasm/pkg/system_wasm_bg.js new file mode 100644 index 00000000..876c8507 --- /dev/null +++ b/packages/system-wasm/pkg/system_wasm_bg.js @@ -0,0 +1,622 @@ +let wasm; +export function __wbg_set_wasm(val) { + wasm = val; +} + +const heap = new Array(128).fill(undefined); + +heap.push(undefined, null, true, false); + +function getObject(idx) { + return heap[idx]; +} + +let heap_next = heap.length; + +function dropObject(idx) { + if (idx < 132) return; + heap[idx] = heap_next; + heap_next = idx; +} + +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} + +let WASM_VECTOR_LEN = 0; + +let cachedUint8Memory0 = null; + +function getUint8Memory0() { + if (cachedUint8Memory0 === null || cachedUint8Memory0.byteLength === 0) { + cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer); + } + return cachedUint8Memory0; +} + +const lTextEncoder = typeof TextEncoder === "undefined" ? (0, module.require)("util").TextEncoder : TextEncoder; + +let cachedTextEncoder = new lTextEncoder("utf-8"); + +const encodeString = + typeof cachedTextEncoder.encodeInto === "function" + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); + } + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length, + }; + }; + +function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0() + .subarray(ptr, ptr + buf.length) + .set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7f) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +function isLikeNone(x) { + return x === undefined || x === null; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; +} + +function addHeapObject(obj) { + if (heap_next === heap.length) heap.push(heap.length + 1); + const idx = heap_next; + heap_next = heap[idx]; + + heap[idx] = obj; + return idx; +} + +const lTextDecoder = typeof TextDecoder === "undefined" ? (0, module.require)("util").TextDecoder : TextDecoder; + +let cachedTextDecoder = new lTextDecoder("utf-8", { ignoreBOM: true, fatal: true }); + +cachedTextDecoder.decode(); + +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +} + +let cachedFloat64Memory0 = null; + +function getFloat64Memory0() { + if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { + cachedFloat64Memory0 = new Float64Array(wasm.memory.buffer); + } + return cachedFloat64Memory0; +} + +let cachedBigInt64Memory0 = null; + +function getBigInt64Memory0() { + if (cachedBigInt64Memory0 === null || cachedBigInt64Memory0.byteLength === 0) { + cachedBigInt64Memory0 = new BigInt64Array(wasm.memory.buffer); + } + return cachedBigInt64Memory0; +} + +function debugString(val) { + // primitive types + const type = typeof val; + if (type == "number" || type == "boolean" || val == null) { + return `${val}`; + } + if (type == "string") { + return `"${val}"`; + } + if (type == "symbol") { + const description = val.description; + if (description == null) { + return "Symbol"; + } else { + return `Symbol(${description})`; + } + } + if (type == "function") { + const name = val.name; + if (typeof name == "string" && name.length > 0) { + return `Function(${name})`; + } else { + return "Function"; + } + } + // objects + if (Array.isArray(val)) { + const length = val.length; + let debug = "["; + if (length > 0) { + debug += debugString(val[0]); + } + for (let i = 1; i < length; i++) { + debug += ", " + debugString(val[i]); + } + debug += "]"; + return debug; + } + // Test for built-in + const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val)); + let className; + if (builtInMatches.length > 1) { + className = builtInMatches[1]; + } else { + // Failed to match the standard '[object ClassName]' + return toString.call(val); + } + if (className == "Object") { + // we're a user defined class or Object + // JSON.stringify avoids problems with cycles, and is generally much + // easier than looping through ownProperties of `val`. + try { + return "Object(" + JSON.stringify(val) + ")"; + } catch (_) { + return "Object"; + } + } + // errors + if (val instanceof Error) { + return `${val.name}: ${val.message}\n${val.stack}`; + } + // TODO we could test for more things here, like `Set`s and `Map`s. + return className; +} +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function diff_filters(prev, next) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.diff_filters(retptr, addHeapObject(prev), addHeapObject(next)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function expand_filter(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.expand_filter(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} prev + * @param {any} next + * @returns {any} + */ +export function get_diff(prev, next) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.get_diff(retptr, addHeapObject(prev), addHeapObject(next)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function flat_merge(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.flat_merge(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @returns {any} + */ +export function compress(val) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.compress(retptr, addHeapObject(val)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} val + * @param {any} target + * @returns {any} + */ +export function pow(val, target) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.pow(retptr, addHeapObject(val), addHeapObject(target)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +/** + * @param {any} password + * @param {any} salt + * @returns {any} + */ +export function argon2(password, salt) { + try { + const retptr = wasm.__wbindgen_add_to_stack_pointer(-16); + wasm.argon2(retptr, addHeapObject(password), addHeapObject(salt)); + var r0 = getInt32Memory0()[retptr / 4 + 0]; + var r1 = getInt32Memory0()[retptr / 4 + 1]; + var r2 = getInt32Memory0()[retptr / 4 + 2]; + if (r2) { + throw takeObject(r1); + } + return takeObject(r0); + } finally { + wasm.__wbindgen_add_to_stack_pointer(16); + } +} + +function handleError(f, args) { + try { + return f.apply(this, args); + } catch (e) { + wasm.__wbindgen_exn_store(addHeapObject(e)); + } +} + +export function __wbindgen_object_drop_ref(arg0) { + takeObject(arg0); +} + +export function __wbindgen_string_get(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "string" ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + var len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; +} + +export function __wbindgen_is_object(arg0) { + const val = getObject(arg0); + const ret = typeof val === "object" && val !== null; + return ret; +} + +export function __wbindgen_is_undefined(arg0) { + const ret = getObject(arg0) === undefined; + return ret; +} + +export function __wbindgen_in(arg0, arg1) { + const ret = getObject(arg0) in getObject(arg1); + return ret; +} + +export function __wbindgen_is_bigint(arg0) { + const ret = typeof getObject(arg0) === "bigint"; + return ret; +} + +export function __wbindgen_bigint_from_u64(arg0) { + const ret = BigInt.asUintN(64, arg0); + return addHeapObject(ret); +} + +export function __wbindgen_jsval_eq(arg0, arg1) { + const ret = getObject(arg0) === getObject(arg1); + return ret; +} + +export function __wbindgen_error_new(arg0, arg1) { + const ret = new Error(getStringFromWasm0(arg0, arg1)); + return addHeapObject(ret); +} + +export function __wbindgen_object_clone_ref(arg0) { + const ret = getObject(arg0); + return addHeapObject(ret); +} + +export function __wbindgen_jsval_loose_eq(arg0, arg1) { + const ret = getObject(arg0) == getObject(arg1); + return ret; +} + +export function __wbindgen_boolean_get(arg0) { + const v = getObject(arg0); + const ret = typeof v === "boolean" ? (v ? 1 : 0) : 2; + return ret; +} + +export function __wbindgen_number_get(arg0, arg1) { + const obj = getObject(arg1); + const ret = typeof obj === "number" ? obj : undefined; + getFloat64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? 0 : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); +} + +export function __wbindgen_number_new(arg0) { + const ret = arg0; + return addHeapObject(ret); +} + +export function __wbindgen_string_new(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); +} + +export function __wbg_getwithrefkey_5e6d9547403deab8(arg0, arg1) { + const ret = getObject(arg0)[getObject(arg1)]; + return addHeapObject(ret); +} + +export function __wbg_set_841ac57cff3d672b(arg0, arg1, arg2) { + getObject(arg0)[takeObject(arg1)] = takeObject(arg2); +} + +export function __wbg_get_44be0491f933a435(arg0, arg1) { + const ret = getObject(arg0)[arg1 >>> 0]; + return addHeapObject(ret); +} + +export function __wbg_length_fff51ee6522a1a18(arg0) { + const ret = getObject(arg0).length; + return ret; +} + +export function __wbg_new_898a68150f225f2e() { + const ret = new Array(); + return addHeapObject(ret); +} + +export function __wbindgen_is_function(arg0) { + const ret = typeof getObject(arg0) === "function"; + return ret; +} + +export function __wbg_next_526fc47e980da008(arg0) { + const ret = getObject(arg0).next; + return addHeapObject(ret); +} + +export function __wbg_next_ddb3312ca1c4e32a() { + return handleError(function (arg0) { + const ret = getObject(arg0).next(); + return addHeapObject(ret); + }, arguments); +} + +export function __wbg_done_5c1f01fb660d73b5(arg0) { + const ret = getObject(arg0).done; + return ret; +} + +export function __wbg_value_1695675138684bd5(arg0) { + const ret = getObject(arg0).value; + return addHeapObject(ret); +} + +export function __wbg_iterator_97f0c81209c6c35a() { + const ret = Symbol.iterator; + return addHeapObject(ret); +} + +export function __wbg_get_97b561fb56f034b5() { + return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments); +} + +export function __wbg_call_cb65541d95d71282() { + return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); + }, arguments); +} + +export function __wbg_new_b51585de1b234aff() { + const ret = new Object(); + return addHeapObject(ret); +} + +export function __wbg_set_502d29070ea18557(arg0, arg1, arg2) { + getObject(arg0)[arg1 >>> 0] = takeObject(arg2); +} + +export function __wbg_isArray_4c24b343cb13cfb1(arg0) { + const ret = Array.isArray(getObject(arg0)); + return ret; +} + +export function __wbg_instanceof_ArrayBuffer_39ac22089b74fddb(arg0) { + let result; + try { + result = getObject(arg0) instanceof ArrayBuffer; + } catch { + result = false; + } + const ret = result; + return ret; +} + +export function __wbg_isSafeInteger_bb8e18dd21c97288(arg0) { + const ret = Number.isSafeInteger(getObject(arg0)); + return ret; +} + +export function __wbg_buffer_085ec1f694018c4f(arg0) { + const ret = getObject(arg0).buffer; + return addHeapObject(ret); +} + +export function __wbg_new_8125e318e6245eed(arg0) { + const ret = new Uint8Array(getObject(arg0)); + return addHeapObject(ret); +} + +export function __wbg_set_5cf90238115182c3(arg0, arg1, arg2) { + getObject(arg0).set(getObject(arg1), arg2 >>> 0); +} + +export function __wbg_length_72e2208bbc0efc61(arg0) { + const ret = getObject(arg0).length; + return ret; +} + +export function __wbg_instanceof_Uint8Array_d8d9cb2b8e8ac1d4(arg0) { + let result; + try { + result = getObject(arg0) instanceof Uint8Array; + } catch { + result = false; + } + const ret = result; + return ret; +} + +export function __wbg_new_abda76e883ba8a5f() { + const ret = new Error(); + return addHeapObject(ret); +} + +export function __wbg_stack_658279fe44541cf6(arg0, arg1) { + const ret = getObject(arg1).stack; + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; +} + +export function __wbg_error_f851667af71bcfc6(arg0, arg1) { + let deferred0_0; + let deferred0_1; + try { + deferred0_0 = arg0; + deferred0_1 = arg1; + console.error(getStringFromWasm0(arg0, arg1)); + } finally { + wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); + } +} + +export function __wbindgen_bigint_get_as_i64(arg0, arg1) { + const v = getObject(arg1); + const ret = typeof v === "bigint" ? v : undefined; + getBigInt64Memory0()[arg0 / 8 + 1] = isLikeNone(ret) ? BigInt(0) : ret; + getInt32Memory0()[arg0 / 4 + 0] = !isLikeNone(ret); +} + +export function __wbindgen_debug_string(arg0, arg1) { + const ret = debugString(getObject(arg1)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; +} + +export function __wbindgen_throw(arg0, arg1) { + throw new Error(getStringFromWasm0(arg0, arg1)); +} + +export function __wbindgen_memory() { + const ret = wasm.memory; + return addHeapObject(ret); +} diff --git a/packages/system-wasm/pkg/system_wasm_bg.wasm b/packages/system-wasm/pkg/system_wasm_bg.wasm new file mode 100644 index 00000000..ce33b120 Binary files /dev/null and b/packages/system-wasm/pkg/system_wasm_bg.wasm differ diff --git a/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts b/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts new file mode 100644 index 00000000..b83a0ee8 --- /dev/null +++ b/packages/system-wasm/pkg/system_wasm_bg.wasm.d.ts @@ -0,0 +1,15 @@ +/* tslint:disable */ +/* eslint-disable */ +export const memory: WebAssembly.Memory; +export function diff_filters(a: number, b: number, c: number): void; +export function expand_filter(a: number, b: number): void; +export function get_diff(a: number, b: number, c: number): void; +export function flat_merge(a: number, b: number): void; +export function compress(a: number, b: number): void; +export function pow(a: number, b: number, c: number): void; +export function argon2(a: number, b: number, c: number): void; +export function __wbindgen_malloc(a: number, b: number): number; +export function __wbindgen_realloc(a: number, b: number, c: number, d: number): number; +export function __wbindgen_add_to_stack_pointer(a: number): number; +export function __wbindgen_exn_store(a: number): void; +export function __wbindgen_free(a: number, b: number, c: number): void; diff --git a/packages/system-wasm/src/diff.rs b/packages/system-wasm/src/diff.rs new file mode 100644 index 00000000..58b2af90 --- /dev/null +++ b/packages/system-wasm/src/diff.rs @@ -0,0 +1,190 @@ +use crate::FlatReqFilter; +use itertools::Itertools; + +pub fn diff_filter(prev: &Vec, next: &Vec) -> Vec { + let mut added: Vec = vec![]; + + for n in next.iter() { + if !prev.iter().contains(&n) { + added.push(n.clone()) + } + } + + added +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn simple_diff_same() { + let prev = vec![FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }]; + let next = vec![FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }]; + + let result = diff_filter(&prev, &next); + assert_eq!(result, vec![]) + } + + #[test] + fn simple_diff_add() { + let prev = vec![FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }]; + let next = vec![ + FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: Some("b".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ]; + + let result = diff_filter(&prev, &next); + assert_eq!( + result, + vec![FlatReqFilter { + id: Some("b".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }] + ) + } + + #[test] + fn simple_diff_replace() { + let prev = vec![FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }]; + let next = vec![FlatReqFilter { + id: Some("b".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }]; + + let result = diff_filter(&prev, &next); + assert_eq!( + result, + vec![FlatReqFilter { + id: Some("b".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }] + ) + } +} diff --git a/packages/system-wasm/src/filter.rs b/packages/system-wasm/src/filter.rs new file mode 100644 index 00000000..b53af32e --- /dev/null +++ b/packages/system-wasm/src/filter.rs @@ -0,0 +1,802 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashSet; +#[cfg(test)] +use std::fmt::Debug; +use std::hash::Hash; +use itertools::Itertools; + +#[derive(Clone)] +enum StringOrNumberEntry<'a> { + String((&'static str, &'a String)), + Number((&'static str, &'a i32)), +} + +#[derive(PartialEq, Clone, Serialize, Deserialize)] +pub struct ReqFilter { + #[serde(rename = "ids", skip_serializing_if = "Option::is_none")] + pub ids: Option>, + #[serde(rename = "authors", skip_serializing_if = "Option::is_none")] + pub authors: Option>, + #[serde(rename = "kinds", skip_serializing_if = "Option::is_none")] + pub kinds: Option>, + #[serde(rename = "#e", skip_serializing_if = "Option::is_none")] + pub e_tag: Option>, + #[serde(rename = "#p", skip_serializing_if = "Option::is_none")] + pub p_tag: Option>, + #[serde(rename = "#t", skip_serializing_if = "Option::is_none")] + pub t_tag: Option>, + #[serde(rename = "#d", skip_serializing_if = "Option::is_none")] + pub d_tag: Option>, + #[serde(rename = "#r", skip_serializing_if = "Option::is_none")] + pub r_tag: Option>, + #[serde(rename = "#a", skip_serializing_if = "Option::is_none")] + pub a_tag: Option>, + #[serde(rename = "#g", skip_serializing_if = "Option::is_none")] + pub g_tag: Option>, + #[serde(rename = "search", skip_serializing_if = "Option::is_none")] + pub search: Option, + #[serde(rename = "since", skip_serializing_if = "Option::is_none")] + pub since: Option, + #[serde(rename = "until", skip_serializing_if = "Option::is_none")] + pub until: Option, + #[serde(rename = "limit", skip_serializing_if = "Option::is_none")] + pub limit: Option, +} + +#[cfg(test)] +impl Debug for ReqFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap().to_owned()) + } +} + +#[derive(PartialEq, PartialOrd, Clone, Serialize, Deserialize)] +pub struct FlatReqFilter { + #[serde(rename = "ids", skip_serializing_if = "Option::is_none")] + pub id: Option, + #[serde(rename = "authors", skip_serializing_if = "Option::is_none")] + pub author: Option, + #[serde(rename = "kinds", skip_serializing_if = "Option::is_none")] + pub kind: Option, + #[serde(rename = "#e", skip_serializing_if = "Option::is_none")] + pub e_tag: Option, + #[serde(rename = "#p", skip_serializing_if = "Option::is_none")] + pub p_tag: Option, + #[serde(rename = "#t", skip_serializing_if = "Option::is_none")] + pub t_tag: Option, + #[serde(rename = "#d", skip_serializing_if = "Option::is_none")] + pub d_tag: Option, + #[serde(rename = "#r", skip_serializing_if = "Option::is_none")] + pub r_tag: Option, + #[serde(rename = "#a", skip_serializing_if = "Option::is_none")] + pub a_tag: Option, + #[serde(rename = "#g", skip_serializing_if = "Option::is_none")] + pub g_tag: Option, + #[serde(rename = "search", skip_serializing_if = "Option::is_none")] + pub search: Option, + #[serde(rename = "since", skip_serializing_if = "Option::is_none")] + pub since: Option, + #[serde(rename = "until", skip_serializing_if = "Option::is_none")] + pub until: Option, + #[serde(rename = "limit", skip_serializing_if = "Option::is_none")] + pub limit: Option, +} + +#[cfg(test)] +impl Debug for FlatReqFilter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap().to_owned()) + } +} + +pub trait Distance { + /// Calculate the distance in terms of similarity for merging + /// + /// The goal of this function is to find 2 filters which are very similar where + /// one filter may have a single property change like so: + /// + /// ```javascript + /// const a = { "kinds": 1, "authors": "a", "since": 99 }; + /// const b = { "kinds": 1, "authors": "b", "since": 99 }; + /// ``` + /// In this case these 2 filters could be merged because their distance is `1` + /// ```javascript + /// const result = { "kinds": [1], "authors": ["a", "b"], "since": 99 }; + /// ``` + fn distance(&self, other: &Self) -> u32; +} + +pub trait CanMerge { + fn can_merge(&self, other: &Self) -> bool; +} + +impl Distance for FlatReqFilter { + fn distance(&self, b: &Self) -> u32 { + let mut ret = 0u32; + + ret += prop_dist(&self.id, &b.id); + ret += prop_dist(&self.kind, &b.kind); + ret += prop_dist(&self.author, &b.author); + ret += prop_dist(&self.e_tag, &b.e_tag); + ret += prop_dist(&self.p_tag, &b.p_tag); + ret += prop_dist(&self.d_tag, &b.d_tag); + ret += prop_dist(&self.r_tag, &b.r_tag); + ret += prop_dist(&self.t_tag, &b.t_tag); + + ret + } +} + +impl CanMerge for FlatReqFilter { + fn can_merge(&self, other: &Self) -> bool { + if self.since != other.since + || self.until != other.until + || self.limit != other.limit + || self.search != other.search + { + return false; + } + + self.distance(other) <= 1 + } +} + +impl From> for ReqFilter { + fn from(value: Vec<&FlatReqFilter>) -> Self { + let ret = ReqFilter { + ids: None, + authors: None, + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + value.iter().fold(ret, |mut acc, x| { + array_prop_append(&x.id, &mut acc.ids); + array_prop_append(&x.author, &mut acc.authors); + array_prop_append(&x.kind, &mut acc.kinds); + array_prop_append(&x.e_tag, &mut acc.e_tag); + array_prop_append(&x.p_tag, &mut acc.p_tag); + array_prop_append(&x.t_tag, &mut acc.t_tag); + array_prop_append(&x.d_tag, &mut acc.d_tag); + array_prop_append(&x.r_tag, &mut acc.r_tag); + array_prop_append(&x.a_tag, &mut acc.a_tag); + array_prop_append(&x.g_tag, &mut acc.g_tag); + acc.search = x.search.to_owned(); + acc.since = x.since; + acc.until = x.until; + acc.limit = x.limit; + + acc + }) + } +} + +impl From> for ReqFilter { + fn from(value: Vec<&ReqFilter>) -> Self { + let ret = ReqFilter { + ids: None, + authors: None, + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + value.iter().fold(ret, |mut acc, x| { + array_prop_append_vec(&x.ids, &mut acc.ids); + array_prop_append_vec(&x.authors, &mut acc.authors); + array_prop_append_vec(&x.kinds, &mut acc.kinds); + array_prop_append_vec(&x.e_tag, &mut acc.e_tag); + array_prop_append_vec(&x.p_tag, &mut acc.p_tag); + array_prop_append_vec(&x.t_tag, &mut acc.t_tag); + array_prop_append_vec(&x.d_tag, &mut acc.d_tag); + array_prop_append_vec(&x.r_tag, &mut acc.r_tag); + array_prop_append_vec(&x.a_tag, &mut acc.a_tag); + array_prop_append_vec(&x.g_tag, &mut acc.g_tag); + acc.search = x.search.to_owned(); + acc.since = x.since; + acc.until = x.until; + acc.limit = x.limit; + + acc + }) + } +} + +impl Into> for &ReqFilter { + fn into(self) -> Vec { + let mut ret: Vec = Vec::new(); + + let mut inputs: Vec> = vec![]; + if let Some(ids) = &self.ids { + let t_ids = ids + .iter() + .map(|z| StringOrNumberEntry::String(("id", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(authors) = &self.authors { + let t_ids = authors + .iter() + .map(|z| StringOrNumberEntry::String(("author", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(kinds) = &self.kinds { + let t_ids = kinds + .iter() + .map(|z| StringOrNumberEntry::Number(("kind", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(e_tags) = &self.e_tag { + let t_ids = e_tags + .iter() + .map(|z| StringOrNumberEntry::String(("e_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(p_tags) = &self.p_tag { + let t_ids = p_tags + .iter() + .map(|z| StringOrNumberEntry::String(("p_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(d_tags) = &self.d_tag { + let t_ids = d_tags + .iter() + .map(|z| StringOrNumberEntry::String(("d_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(t_tags) = &self.t_tag { + let t_ids = t_tags + .iter() + .map(|z| StringOrNumberEntry::String(("t_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(r_tags) = &self.r_tag { + let t_ids = r_tags + .iter() + .map(|z| StringOrNumberEntry::String(("r_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(a_tags) = &self.a_tag { + let t_ids = a_tags + .iter() + .map(|z| StringOrNumberEntry::String(("a_tag", z))) + .collect(); + inputs.push(t_ids); + } + if let Some(g_tags) = &self.g_tag { + let t_ids = g_tags + .iter() + .map(|z| StringOrNumberEntry::String(("g_tag", z))) + .collect(); + inputs.push(t_ids); + } + + for p in inputs.iter().multi_cartesian_product() { + ret.push(FlatReqFilter { + id: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("id") { + return Some((*v).to_string()); + } + } + None + }), + author: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("author") { + return Some((*v).to_string()); + } + } + None + }), + kind: p.iter().find_map(|q| { + if let StringOrNumberEntry::Number((k, v)) = q { + if (*k).eq("kind") { + return Some((*v).clone()); + } + } + None + }), + e_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("e_tag") { + return Some((*v).to_string()); + } + } + None + }), + p_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("p_tag") { + return Some((*v).to_string()); + } + } + None + }), + t_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("t_tag") { + return Some((*v).to_string()); + } + } + None + }), + d_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("d_tag") { + return Some((*v).to_string()); + } + } + None + }), + r_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("r_tag") { + return Some((*v).to_string()); + } + } + None + }), + a_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("a_tag") { + return Some((*v).to_string()); + } + } + None + }), + g_tag: p.iter().find_map(|q| { + if let StringOrNumberEntry::String((k, v)) = q { + if (*k).eq("g_tag") { + return Some((*v).to_string()); + } + } + None + }), + search: self.search.to_owned(), + since: self.since, + until: self.until, + limit: self.limit, + }) + } + ret + } +} + +impl Distance for ReqFilter { + fn distance(&self, b: &Self) -> u32 { + let mut ret = 0u32; + + ret += prop_dist_vec(&self.ids, &b.ids); + ret += prop_dist_vec(&self.kinds, &b.kinds); + ret += prop_dist_vec(&self.authors, &b.authors); + ret += prop_dist_vec(&self.e_tag, &b.e_tag); + ret += prop_dist_vec(&self.p_tag, &b.p_tag); + ret += prop_dist_vec(&self.d_tag, &b.d_tag); + ret += prop_dist_vec(&self.r_tag, &b.r_tag); + ret += prop_dist_vec(&self.t_tag, &b.t_tag); + ret += prop_dist_vec(&self.a_tag, &b.a_tag); + + ret + } +} + +impl CanMerge for ReqFilter { + fn can_merge(&self, other: &Self) -> bool { + if self.since != other.since + || self.until != other.until + || self.limit != other.limit + || self.search != other.search + { + return false; + } + + self.distance(other) <= 1 + } +} + +#[inline(always)] +fn prop_dist(a: &Option, b: &Option) -> u32 { + if (a.is_some() && b.is_none()) || (a.is_none() && b.is_some()) { + return 10; + } else if a.is_some() && a != b { + return 1; + } + 0 +} + +#[inline(always)] +fn prop_dist_vec(a: &Option>, b: &Option>) -> u32 { + if (a.is_some() && b.is_none()) || (a.is_none() && b.is_some()) { + return 10; + } + match (a, b) { + (Some(aa), Some(bb)) => { + if aa.len() != bb.len() { + 1 + } else if aa == bb { + 0 + } else { + 1 + } + } + (None, None) => 0, + _ => panic!("Should not reach here!"), + } +} + +#[inline(always)] +fn array_prop_append(val: &Option, arr: &mut Option>) { + if let Some(ap) = val { + if arr.is_none() { + *arr = Some(HashSet::from([ap.clone()])) + } else { + arr.as_mut().unwrap().insert(ap.clone()); + } + } +} + +#[inline(always)] +fn array_prop_append_vec( + val: &Option>, + arr: &mut Option>, +) { + if let Some(ap) = val { + if arr.is_none() { + *arr = Some(ap.clone()) + } else { + ap.iter().for_each(|v| { + arr.as_mut().unwrap().insert((*v).clone()); + }); + } + } +} + +#[cfg(test)] +mod tests { + use crate::ReqFilter; + use std::collections::HashSet; + use crate::filter::FlatReqFilter; + + #[test] + fn test_expand_filter() { + let input = ReqFilter { + authors: Some(HashSet::from([ + "a".to_owned(), + "b".to_owned(), + "c".to_owned(), + ])), + kinds: Some(HashSet::from([1, 2, 3])), + ids: Some(HashSet::from(["x".to_owned(), "y".to_owned()])), + p_tag: Some(HashSet::from(["a".to_owned()])), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }; + + let output: Vec = (&input).into(); + let expected = vec![ + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(1), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(1), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(2), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(2), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(3), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("a".to_owned()), + kind: Some(3), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(1), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(1), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(2), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(2), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(3), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("b".to_owned()), + kind: Some(3), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(1), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(1), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(2), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(2), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(3), + id: Some("x".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + FlatReqFilter { + author: Some("c".to_owned()), + kind: Some(3), + id: Some("y".to_owned()), + p_tag: Some("a".to_owned()), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: Some(99), + until: None, + limit: Some(10), + e_tag: None, + }, + ]; + assert_eq!(output.len(), expected.len()); + output.iter().for_each(|a| assert!(expected.contains(a))); + } +} diff --git a/packages/system-wasm/src/lib.rs b/packages/system-wasm/src/lib.rs new file mode 100644 index 00000000..1343ddbb --- /dev/null +++ b/packages/system-wasm/src/lib.rs @@ -0,0 +1,201 @@ +extern crate console_error_panic_hook; + +use argon2::{Argon2}; +use serde::{Deserialize, Serialize}; +use crate::filter::{FlatReqFilter, ReqFilter}; +use wasm_bindgen::prelude::*; + +pub mod diff; +pub mod filter; +pub mod merge; +pub mod pow; + +#[derive(PartialEq, Clone, Serialize, Deserialize)] +pub struct Event { + #[serde(rename = "id", skip_serializing_if = "Option::is_none")] + pub id: Option, + pub kind: i32, + pub created_at: u64, + pub pubkey: String, + pub content: String, + #[serde(rename = "sig", skip_serializing_if = "Option::is_none")] + pub sig: Option, + pub tags: Vec>, +} + +#[wasm_bindgen] +pub fn diff_filters(prev: JsValue, next: JsValue) -> Result { + console_error_panic_hook::set_once(); + let prev_parsed: Vec = serde_wasm_bindgen::from_value(prev)?; + let next_parsed: Vec = serde_wasm_bindgen::from_value(next)?; + let result = diff::diff_filter(&prev_parsed, &next_parsed); + Ok(serde_wasm_bindgen::to_value(&result)?) +} + +#[wasm_bindgen] +pub fn expand_filter(val: JsValue) -> Result { + console_error_panic_hook::set_once(); + let parsed: ReqFilter = serde_wasm_bindgen::from_value(val)?; + let result: Vec = (&parsed).into(); + Ok(serde_wasm_bindgen::to_value(&result)?) +} + +#[wasm_bindgen] +pub fn get_diff(prev: JsValue, next: JsValue) -> Result { + console_error_panic_hook::set_once(); + let prev_parsed: Vec = serde_wasm_bindgen::from_value(prev)?; + let next_parsed: Vec = serde_wasm_bindgen::from_value(next)?; + let expanded_prev: Vec = prev_parsed + .iter() + .flat_map(|v| { + let vec: Vec = v.into(); + vec + }) + .collect(); + let expanded_next: Vec = next_parsed + .iter() + .flat_map(|v| { + let vec: Vec = v.into(); + vec + }) + .collect(); + let result = diff::diff_filter(&expanded_prev, &expanded_next); + Ok(serde_wasm_bindgen::to_value(&result)?) +} + +#[wasm_bindgen] +pub fn flat_merge(val: JsValue) -> Result { + console_error_panic_hook::set_once(); + let val_parsed: Vec = serde_wasm_bindgen::from_value(val)?; + let result = merge::merge::(val_parsed.iter().collect()); + Ok(serde_wasm_bindgen::to_value(&result)?) +} + +#[wasm_bindgen] +pub fn compress(val: JsValue) -> Result { + console_error_panic_hook::set_once(); + let val_parsed: Vec = serde_wasm_bindgen::from_value(val)?; + let result = merge::merge::(val_parsed.iter().collect()); + Ok(serde_wasm_bindgen::to_value(&result)?) +} + +#[wasm_bindgen] +pub fn pow(val: JsValue, target: JsValue) -> Result { + console_error_panic_hook::set_once(); + let mut val_parsed: Event = serde_wasm_bindgen::from_value(val)?; + let target_parsed: u8 = serde_wasm_bindgen::from_value(target)?; + pow::pow(&mut val_parsed, target_parsed); + Ok(serde_wasm_bindgen::to_value(&val_parsed)?) +} + +#[wasm_bindgen] +pub fn argon2(password: JsValue, salt: JsValue) -> Result { + console_error_panic_hook::set_once(); + let password_parsed: String = serde_wasm_bindgen::from_value(password)?; + let salt_parsed: String = serde_wasm_bindgen::from_value(salt)?; + let mut key = [0u8; 32]; + Argon2::default().hash_password_into(password_parsed.as_bytes(), salt_parsed.as_bytes(), &mut key).expect("Failed to generate key"); + Ok(serde_wasm_bindgen::to_value(&hex::encode(key))?) +} + +#[cfg(test)] +mod tests { + use super::*; + use itertools::Itertools; + use std::cmp::Ordering; + use std::collections::HashSet; + + #[test] + fn flat_merge_expanded() { + let input = vec![ + ReqFilter { + ids: None, + kinds: Some(HashSet::from([1, 6969, 6])), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + authors: Some(HashSet::from([ + "kieran".to_string(), + "snort".to_string(), + "c".to_string(), + "d".to_string(), + "e".to_string(), + ])), + since: Some(1), + until: Some(100), + search: None, + limit: None, + }, + ReqFilter { + ids: None, + kinds: Some(HashSet::from([4])), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + authors: Some(HashSet::from(["kieran".to_string()])), + limit: None, + }, + ReqFilter { + ids: None, + authors: None, + kinds: Some(HashSet::from([4])), + e_tag: None, + p_tag: Some(HashSet::from(["kieran".to_string()])), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ReqFilter { + ids: None, + kinds: Some(HashSet::from([1000])), + authors: Some(HashSet::from(["snort".to_string()])), + p_tag: Some(HashSet::from(["kieran".to_string()])), + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + e_tag: None, + limit: None, + }, + ]; + + let expanded: Vec = input + .iter() + .flat_map(|v| { + let r: Vec = v.into(); + r + }) + .sorted_by(|_, _| { + if rand::random() { + Ordering::Less + } else { + Ordering::Greater + } + }) + .collect(); + let merged_expanded: Vec = merge::merge(expanded.iter().collect()); + assert_eq!(merged_expanded.len(), input.len()); + assert!(merged_expanded.iter().all(|v| input.contains(v))); + } +} diff --git a/packages/system-wasm/src/merge.rs b/packages/system-wasm/src/merge.rs new file mode 100644 index 00000000..56349ef1 --- /dev/null +++ b/packages/system-wasm/src/merge.rs @@ -0,0 +1,494 @@ +use crate::filter::CanMerge; + +pub fn merge<'a, T, Z>(all: Vec<&'a T>) -> Vec + where + T: CanMerge, + for<'b> Z: CanMerge + From> + From>, +{ + let mut ret: Vec = merge_once(all); + loop { + let last_len = ret.len(); + ret = merge_once(ret.iter().collect()); + if last_len == ret.len() { + break; + } + } + ret +} + +fn merge_once<'a, T, Z>(all: Vec<&'a T>) -> Vec + where + T: CanMerge, + Z: From>, +{ + let mut ret: Vec = vec![]; + if all.is_empty() { + return ret; + } + + let merge_sets: Vec> = vec![vec![all.first().unwrap()]]; + let merge_sets = all.iter().skip(1).fold(merge_sets, |mut acc, x| { + let mut did_match = false; + for y in acc.iter_mut() { + if y.iter().all(|z| z.can_merge(x)) { + y.push(x); + did_match = true; + break; + } + } + if !did_match { + acc.push(vec![x]); + } + acc + }); + + for s in merge_sets { + ret.push(Z::from(s)); + } + + ret +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::filter::{Distance, FlatReqFilter, ReqFilter}; + use std::collections::HashSet; + + #[test] + fn distance() { + let a = FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + let b = FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + let c = FlatReqFilter { + id: Some("c".to_owned()), + author: None, + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + let d = FlatReqFilter { + id: Some("a".to_owned()), + author: None, + kind: Some(1), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + let e = FlatReqFilter { + id: Some("e".to_owned()), + author: None, + kind: Some(1), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }; + assert_eq!(a.distance(&b), 0); + assert_eq!(a.distance(&c), 1); + assert_eq!(a.distance(&d), 10); + assert_eq!(a.distance(&e), 11); + } + + #[test] + fn merge_set() { + let a = FlatReqFilter { + id: Some("0".to_owned()), + author: Some("a".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(10), + }; + let b = FlatReqFilter { + id: Some("0".to_owned()), + author: Some("b".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(10), + }; + + let output = ReqFilter { + ids: Some(HashSet::from(["0".to_owned()])), + authors: Some(HashSet::from(["a".to_owned(), "b".to_owned()])), + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(10), + }; + assert_eq!(ReqFilter::from(vec![&a, &b]), output); + } + + #[test] + fn can_merge_filters() { + let a = FlatReqFilter { + id: Some("0".to_owned()), + author: Some("a".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(10), + }; + let b = FlatReqFilter { + id: Some("0".to_owned()), + author: Some("b".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(10), + }; + let c = FlatReqFilter { + id: Some("0".to_owned()), + author: Some("b".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(100), + }; + assert!(&a.can_merge(&b)); + assert!(!&b.can_merge(&c)); + } + + #[test] + fn flat_merge() { + let input = vec![ + FlatReqFilter { + id: Some("0".to_owned()), + author: Some("a".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: Some("0".to_owned()), + author: Some("b".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: None, + author: None, + kind: Some(1), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: None, + author: None, + kind: Some(2), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: None, + author: None, + kind: Some(2), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: Some("0".to_owned()), + author: Some("c".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: None, + author: Some("c".to_owned()), + kind: Some(1), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + FlatReqFilter { + id: None, + author: Some("c".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(100), + }, + FlatReqFilter { + id: Some("1".to_owned()), + author: Some("c".to_owned()), + kind: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ]; + let output = vec![ + ReqFilter { + ids: Some(HashSet::from(["0".to_owned()])), + authors: Some(HashSet::from([ + "a".to_owned(), + "b".to_owned(), + "c".to_owned(), + ])), + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ReqFilter { + ids: None, + authors: None, + kinds: Some(HashSet::from([1, 2])), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ReqFilter { + ids: None, + authors: Some(HashSet::from(["c".to_owned()])), + kinds: Some(HashSet::from([1])), + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ReqFilter { + ids: None, + authors: Some(HashSet::from(["c".to_owned()])), + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: Some(100), + }, + ReqFilter { + ids: Some(HashSet::from(["1".to_owned()])), + authors: Some(HashSet::from(["c".to_owned()])), + kinds: None, + e_tag: None, + p_tag: None, + t_tag: None, + d_tag: None, + r_tag: None, + a_tag: None, + g_tag: None, + search: None, + since: None, + until: None, + limit: None, + }, + ]; + + assert_eq!( + merge::(input.iter().collect()), + output + ) + } +} diff --git a/packages/system-wasm/src/pow.rs b/packages/system-wasm/src/pow.rs new file mode 100644 index 00000000..73b70743 --- /dev/null +++ b/packages/system-wasm/src/pow.rs @@ -0,0 +1,111 @@ +use crate::Event; + +pub fn pow(ev: &mut Event, target: u8) { + let mut ctr = 0u32; + let mut nonce_tag_idx = ev.tags.iter().position(|x| x[0] == "nonce"); + if nonce_tag_idx.is_none() { + nonce_tag_idx = Some(ev.tags.len()); + ev.tags.push(vec!["nonce".to_owned(), ctr.to_string(), target.to_string()]); + } + loop { + ev.tags[nonce_tag_idx.unwrap()][1] = ctr.to_string(); + + let new_id = make_id(ev); + if count_leading_zeros(&new_id) >= target { + ev.id = Some(new_id); + break; + } + + ctr += 1; + } +} + +fn count_leading_zeros(str: &String) -> u8 { + let mut count = 0; + + for x in hex::decode(str).unwrap() { + if x == 0u8 { + count += 8; + } else { + count += x.leading_zeros(); + break; + } + } + + count as u8 +} + +fn make_id(ev: &Event) -> String { + let mut v = "[0,\"".to_owned(); + v.push_str(&ev.pubkey); + v.push_str("\","); + v.push_str(&ev.created_at.to_string()); + v.push(','); + v.push_str(&ev.kind.to_string()); + v.push_str(",["); + v.push_str(ev.tags.iter().map(|x| { + let mut y = "[".to_owned(); + y.push_str(x.iter().map(|z| ["\"", z, "\""].join("")).collect::>().join(",").as_str()); + y.push(']'); + y + }).collect::>().join(",").as_str()); + v.push_str("],\""); + v.push_str(&ev.content); + v.push_str("\"]"); + sha256::digest(v) +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + use serde_json::json; + use crate::Event; + + #[test] + fn make_id() { + let ev = Event::deserialize(json!({ + "content": "Oh i think it doesnt work until you reload", + "created_at": 1695568849, + "id": "0000051bca8ee62220b34827358dca69284734a2e7420f3c4b814901a531c767", + "kind": 1, + "pubkey": "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed", + "sig": "0c18bfcde49fd42c7faf93b3ecd7caf10f0414c9ee3234fca96ea0bbb1a805cb2767fc067dc1a743420c499b34c232e19b73beb2f1fe47c18a2856c67bdef983", + "tags": [ + [ + "e", + "ad17146f086345a12583b537daabdf49ccc5cd09e2c0b4816c835f397b693e6b", + "wss://nos.lol/", + "root" + ], + [ + "e", + "72759bf1f525e9715f4e6d22381f53dc4d2ab47d7aaac11340e7fced13e10b11", + "wss://nos.lol/", + "reply" + ], + [ + "p", + "63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed" + ], + [ + "p", + "1bc70a0148b3f316da33fe3c89f23e3e71ac4ff998027ec712b905cd24f6a411" + ], + [ + "nonce", + "7403", + "18" + ] + ] + })).ok().unwrap(); + + assert_eq!(super::make_id(&ev), ev.id.unwrap()) + } + + #[test] + fn count_zeros() { + assert_eq!(10u8.leading_zeros(), 4); + assert_eq!(super::count_leading_zeros(&"00".to_owned()), 8); + assert_eq!(super::count_leading_zeros(&"0000051bca8ee62220b34827358dca69284734a2e7420f3c4b814901a531c767".to_owned()), 21) + } +} \ No newline at end of file diff --git a/packages/system-wasm/system-query.iml b/packages/system-wasm/system-query.iml new file mode 100644 index 00000000..75504b26 --- /dev/null +++ b/packages/system-wasm/system-query.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/system/README.md b/packages/system/README.md index 4fc14b90..0758e410 100644 --- a/packages/system/README.md +++ b/packages/system/README.md @@ -7,61 +7,48 @@ Simple example: ```js import { NostrSystem, - EventPublisher, - UserRelaysCache, RequestBuilder, - FlatNoteStore, - StoreSnapshot + StoreSnapshot, + NoteCollection } from "@snort/system" -// Provided in-memory / indexedDb cache for relays -// You can also implement your own with "RelayCache" interface -const RelaysCache = new UserRelaysCache(); - -// example auth handler using NIP-07 -const AuthHandler = async (challenge: string, relay: string) => { - const pub = await EventPublisher.nip7(); - if (pub) { - return await pub.nip42Auth(challenge, relay); - } -} - // Singleton instance to store all connections and access query fetching system -const System = new NostrSystem({ - relayCache: RelaysCache, - authHandler: AuthHandler // can be left undefined if you dont care about NIP-42 Auth -}); +const System = new NostrSystem({}); (async () => { - // connec to one "bootstrap" relay to pull profiles/relay lists from - // also used as a fallback relay when gossip model doesnt know which relays to pick, or "authors" are not provided in the request - await System.ConnectToRelay("wss://relay.snort.social", { read: true, write: false }); + // Setup cache system + await System.Init(); - // ID should be unique to the use case, this is important as all data fetched from this ID will be merged into the same NoteStore - const rb = new RequestBuilder("get-posts"); - rb.withFilter() - .authors(["63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"]) // Kieran pubkey - .kinds([1]) - .limit(10); + // connec to one "bootstrap" relay to pull profiles/relay lists from + // also used as a fallback relay when gossip model doesnt know which relays to pick, or "authors" are not provided in the request + await System.ConnectToRelay("wss://relay.snort.social", { read: true, write: false }); - const q = System.Query(FlatNoteStore, rb); - // basic usage using "onEvent", fired for every event added to the store - q.onEvent = (sub, e) => { - console.debug(sub, e); - } + // ID should be unique to the use case, this is important as all data fetched from this ID will be merged into the same NoteStore + const rb = new RequestBuilder("get-posts"); + rb.withFilter() + .authors(["63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed"]) // Kieran pubkey + .kinds([1]) + .limit(10); - // Hookable type using change notification, limited to every 500ms - const release = q.feed.hook(() => { - // since we use the FlatNoteStore we expect NostrEvent[] - // other stores provide different data, like a single event instead of an array (latest version) - const state = q.feed.snapshot as StoreSnapshot>; + const q = System.Query(NoteCollection, rb); + // basic usage using "onEvent", fired every 100ms + q.feed.onEvent(evs => { + console.log(evs); + // something else.. + }); - // do something with snapshot of store - console.log(`We have ${state.data.length} events now!`) - }); + // Hookable type using change notification, limited to every 500ms + const release = q.feed.hook(() => { + // since we use the NoteCollection we expect NostrEvent[] + // other stores provide different data, like a single event instead of an array (latest version) + const state = q.feed.snapshot as StoreSnapshot>; - // release the hook when its not needed anymore - // these patterns will be managed in @snort/system-react to make it easier to use react or other UI frameworks - // release(); + // do something with snapshot of store + console.log(`We have ${state.data?.length} events now!`); + }); + + // release the hook when its not needed anymore + // these patterns will be managed in @snort/system-react to make it easier to use react or other UI frameworks + release(); })(); ``` diff --git a/packages/system/examples/simple.ts b/packages/system/examples/simple.ts index b42cca11..1c7f6804 100644 --- a/packages/system/examples/simple.ts +++ b/packages/system/examples/simple.ts @@ -1,24 +1,12 @@ -import { NostrSystem, EventPublisher, UserRelaysCache, RequestBuilder, FlatNoteStore, StoreSnapshot } from "../src"; - -// Provided in-memory / indexedDb cache for relays -// You can also implement your own with "RelayCache" interface -const RelaysCache = new UserRelaysCache(); - -// example auth handler using NIP-07 -const AuthHandler = async (challenge: string, relay: string) => { - const pub = await EventPublisher.nip7(); - if (pub) { - return await pub.nip42Auth(challenge, relay); - } -}; +import { NostrSystem, RequestBuilder, FlatNoteStore, StoreSnapshot, NoteCollection } from "../src"; // Singleton instance to store all connections and access query fetching system -const System = new NostrSystem({ - relayCache: RelaysCache, - authHandler: AuthHandler, // can be left undefined if you dont care about NIP-42 Auth -}); +const System = new NostrSystem({}); (async () => { + // Setup cache system + await System.Init(); + // connec to one "bootstrap" relay to pull profiles/relay lists from // also used as a fallback relay when gossip model doesnt know which relays to pick, or "authors" are not provided in the request await System.ConnectToRelay("wss://relay.snort.social", { read: true, write: false }); @@ -30,23 +18,24 @@ const System = new NostrSystem({ .kinds([1]) .limit(10); - const q = System.Query(FlatNoteStore, rb); - // basic usage using "onEvent", fired for every event added to the store - q.onEvent = (sub, e) => { - console.debug(sub, e); - }; + const q = System.Query(NoteCollection, rb); + // basic usage using "onEvent", fired every 100ms + q.feed.onEvent(evs => { + console.log(evs); + // something else.. + }); // Hookable type using change notification, limited to every 500ms const release = q.feed.hook(() => { // since we use the FlatNoteStore we expect NostrEvent[] // other stores provide different data, like a single event instead of an array (latest version) - const state = q.feed.snapshot as StoreSnapshot>; + const state = q.feed.snapshot as StoreSnapshot>; // do something with snapshot of store - console.log(`We have ${state.data.length} events now!`); + console.log(`We have ${state.data?.length} events now!`); }); // release the hook when its not needed anymore // these patterns will be managed in @snort/system-react to make it easier to use react or other UI frameworks - // release(); + release(); })(); diff --git a/packages/system/package.json b/packages/system/package.json index ddccc8e3..b2370968 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,12 +1,12 @@ { "name": "@snort/system", - "version": "1.0.17", + "version": "1.0.21", "description": "Snort nostr system package", "main": "dist/index.js", "types": "dist/index.d.ts", "repository": "https://git.v0l.io/Kieran/snort", "author": "v0l", - "license": "GPL-3.0-or-later", + "license": "MIT", "scripts": { "build": "rm -rf dist && tsc", "test": "jest --runInBand" @@ -20,20 +20,25 @@ "@peculiar/webcrypto": "^1.4.3", "@types/debug": "^4.1.8", "@types/jest": "^29.5.1", + "@types/node": "^20.5.9", "@types/uuid": "^9.0.2", + "@types/ws": "^8.5.5", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "ts-jest": "^29.1.0", + "ts-node": "^10.9.1", "typescript": "^5.2.2" }, "dependencies": { "@noble/curves": "^1.2.0", "@noble/hashes": "^1.3.2", "@scure/base": "^1.1.2", - "@snort/shared": "^1.0.4", + "@snort/shared": "^1.0.6", "@stablelib/xchacha20": "^1.0.1", "debug": "^4.3.4", "dexie": "^3.2.4", - "uuid": "^9.0.0" + "isomorphic-ws": "^5.0.0", + "uuid": "^9.0.0", + "ws": "^8.14.0" } } diff --git a/packages/system/src/cache/events.ts b/packages/system/src/cache/events.ts new file mode 100644 index 00000000..59457cbe --- /dev/null +++ b/packages/system/src/cache/events.ts @@ -0,0 +1,23 @@ +import { NostrEvent } from "nostr"; +import { db } from "."; +import { FeedCache } from "@snort/shared"; + +export class EventsCache extends FeedCache { + constructor() { + super("EventsCache", db.events); + } + + key(of: NostrEvent): string { + return of.id; + } + + override async preload(): Promise { + await super.preload(); + // load everything + await this.buffer([...this.onTable]); + } + + takeSnapshot(): Array { + return [...this.cache.values()]; + } +} diff --git a/packages/system/src/connection.ts b/packages/system/src/connection.ts index 1650a46b..d1f22b19 100644 --- a/packages/system/src/connection.ts +++ b/packages/system/src/connection.ts @@ -1,5 +1,6 @@ import { v4 as uuid } from "uuid"; import debug from "debug"; +import WebSocket from "isomorphic-ws"; import { unwrap, ExternalStore, unixNowMs } from "@snort/shared"; import { DefaultConnectTimeout } from "./const"; @@ -138,7 +139,7 @@ export class Connection extends ExternalStore { this.#sendPendingRaw(); } - OnClose(e: CloseEvent) { + OnClose(e: WebSocket.CloseEvent) { if (this.ReconnectTimer) { clearTimeout(this.ReconnectTimer); this.ReconnectTimer = undefined; @@ -171,10 +172,10 @@ export class Connection extends ExternalStore { this.notifyChange(); } - OnMessage(e: MessageEvent) { + OnMessage(e: WebSocket.MessageEvent) { this.#activity = unixNowMs(); - if (e.data.length > 0) { - const msg = JSON.parse(e.data); + if ((e.data as string).length > 0) { + const msg = JSON.parse(e.data as string); const tag = msg[0]; switch (tag) { case "AUTH": { @@ -221,7 +222,7 @@ export class Connection extends ExternalStore { } } - OnError(e: Event) { + OnError(e: WebSocket.Event) { this.#log("Error: %O", e); this.notifyChange(); } @@ -383,12 +384,12 @@ export class Connection extends ExternalStore { } this.AwaitingAuth.set(challenge, true); const authEvent = await this.Auth(challenge, this.Address); - return new Promise(resolve => { - if (!authEvent) { - authCleanup(); - return Promise.reject("no event"); - } + if (!authEvent) { + authCleanup(); + throw new Error("No auth event"); + } + return await new Promise(resolve => { const t = setTimeout(() => { authCleanup(); resolve(); diff --git a/packages/system/src/encrypted.ts b/packages/system/src/encrypted.ts new file mode 100644 index 00000000..707bb1b3 --- /dev/null +++ b/packages/system/src/encrypted.ts @@ -0,0 +1,69 @@ +import { scryptAsync } from "@noble/hashes/scrypt"; +import { sha256 } from "@noble/hashes/sha256"; +import { hmac } from "@noble/hashes/hmac"; +import { bytesToHex, hexToBytes, randomBytes } from "@noble/hashes/utils"; +import { base64 } from "@scure/base"; +import { streamXOR as xchacha20 } from "@stablelib/xchacha20"; + +export class InvalidPinError extends Error { + constructor() { + super(); + } +} + +/** + * Pin protected data + */ +export class PinEncrypted { + static readonly #opts = { N: 2 ** 20, r: 8, p: 1, dkLen: 32 }; + #decrypted?: Uint8Array; + #encrypted: PinEncryptedPayload; + + constructor(enc: PinEncryptedPayload) { + this.#encrypted = enc; + } + + get value() { + if (!this.#decrypted) throw new Error("Content has not been decrypted yet"); + return bytesToHex(this.#decrypted); + } + + async decrypt(pin: string) { + const key = await scryptAsync(pin, base64.decode(this.#encrypted.salt), PinEncrypted.#opts); + const ciphertext = base64.decode(this.#encrypted.ciphertext); + const nonce = base64.decode(this.#encrypted.iv); + const plaintext = xchacha20(key, nonce, ciphertext, new Uint8Array(32)); + if (plaintext.length !== 32) throw new InvalidPinError(); + const mac = base64.encode(hmac(sha256, key, plaintext)); + if (mac !== this.#encrypted.mac) throw new InvalidPinError(); + this.#decrypted = plaintext; + } + + toPayload() { + return this.#encrypted; + } + + static async create(content: string, pin: string) { + const salt = randomBytes(24); + const nonce = randomBytes(24); + const plaintext = hexToBytes(content); + const key = await scryptAsync(pin, salt, PinEncrypted.#opts); + const mac = base64.encode(hmac(sha256, key, plaintext)); + const ciphertext = xchacha20(key, nonce, plaintext, new Uint8Array(32)); + const ret = new PinEncrypted({ + salt: base64.encode(salt), + ciphertext: base64.encode(ciphertext), + iv: base64.encode(nonce), + mac, + }); + ret.#decrypted = plaintext; + return ret; + } +} + +export interface PinEncryptedPayload { + salt: string; // for KDF + ciphertext: string; + iv: string; + mac: string; +} diff --git a/packages/system/src/event-builder.ts b/packages/system/src/event-builder.ts index cfe88bb6..cfd587cc 100644 --- a/packages/system/src/event-builder.ts +++ b/packages/system/src/event-builder.ts @@ -1,6 +1,6 @@ import { EventKind, HexKey, NostrPrefix, NostrEvent, EventSigner, PowMiner } from "."; import { HashtagRegex, MentionNostrEntityRegex } from "./const"; -import { getPublicKey, unixNow } from "@snort/shared"; +import { getPublicKey, jitter, unixNow } from "@snort/shared"; import { EventExt } from "./event-ext"; import { tryParseNostrLink } from "./nostr-link"; @@ -12,6 +12,12 @@ export class EventBuilder { #tags: Array> = []; #pow?: number; #powMiner?: PowMiner; + #jitter?: number; + + jitter(n: number) { + this.#jitter = n; + return this; + } kind(k: EventKind) { this.#kind = k; @@ -73,8 +79,8 @@ export class EventBuilder { pubkey: this.#pubkey ?? "", content: this.#content ?? "", kind: this.#kind, - created_at: this.#createdAt ?? unixNow(), - tags: this.#tags, + created_at: (this.#createdAt ?? unixNow()) + (this.#jitter ? jitter(this.#jitter) : 0), + tags: this.#tags.sort((a, b) => a[0].localeCompare(b[0])), } as NostrEvent; ev.id = EventExt.createId(ev); return ev; diff --git a/packages/system/src/event-ext.ts b/packages/system/src/event-ext.ts index 9a048387..76feff64 100644 --- a/packages/system/src/event-ext.ts +++ b/packages/system/src/event-ext.ts @@ -94,6 +94,7 @@ export abstract class EventExt { value: tag[1], } as Tag; switch (ret.key) { + case "a": case "e": { ret.relay = tag.length > 2 ? tag[2] : undefined; ret.marker = tag.length > 3 ? tag[3] : undefined; @@ -102,40 +103,53 @@ export abstract class EventExt { } return ret; } - static extractThread(ev: NostrEvent) { - const isThread = ev.tags.some(a => (a[0] === "e" && a[3] !== "mention") || a[0] == "a"); - if (!isThread) { - return undefined; - } + static extractThread(ev: NostrEvent) { const shouldWriteMarkers = ev.kind === EventKind.TextNote; const ret = { mentions: [], pubKeys: [], } as Thread; - const eTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map(a => EventExt.parseTag(a)); - const marked = eTags.some(a => a.marker); - if (!marked) { - ret.root = eTags[0]; - ret.root.marker = shouldWriteMarkers ? "root" : undefined; - if (eTags.length > 1) { - ret.replyTo = eTags[eTags.length - 1]; - ret.replyTo.marker = shouldWriteMarkers ? "reply" : undefined; - } - if (eTags.length > 2) { - ret.mentions = eTags.slice(1, -1); - if (shouldWriteMarkers) { - ret.mentions.forEach(a => (a.marker = "mention")); + const replyTags = ev.tags.filter(a => a[0] === "e" || a[0] === "a").map(a => EventExt.parseTag(a)); + if (replyTags.length > 0) { + const marked = replyTags.some(a => a.marker); + if (!marked) { + ret.root = replyTags[0]; + ret.root.marker = shouldWriteMarkers ? "root" : undefined; + if (replyTags.length > 1) { + ret.replyTo = replyTags[replyTags.length - 1]; + ret.replyTo.marker = shouldWriteMarkers ? "reply" : undefined; } + if (replyTags.length > 2) { + ret.mentions = replyTags.slice(1, -1); + if (shouldWriteMarkers) { + ret.mentions.forEach(a => (a.marker = "mention")); + } + } + } else { + const root = replyTags.find(a => a.marker === "root"); + const reply = replyTags.find(a => a.marker === "reply"); + ret.root = root; + ret.replyTo = reply; + ret.mentions = replyTags.filter(a => a.marker === "mention"); } } else { - const root = eTags.find(a => a.marker === "root"); - const reply = eTags.find(a => a.marker === "reply"); - ret.root = root; - ret.replyTo = reply; - ret.mentions = eTags.filter(a => a.marker === "mention"); + return undefined; } ret.pubKeys = Array.from(new Set(ev.tags.filter(a => a[0] === "p").map(a => a[1]))); return ret; } + + /** + * Assign props if undefined + */ + static fixupEvent(e: NostrEvent) { + e.tags ??= []; + e.created_at ??= 0; + e.content ??= ""; + e.id ??= ""; + e.kind ??= 0; + e.pubkey ??= ""; + e.sig ??= ""; + } } diff --git a/packages/system/src/event-kind.ts b/packages/system/src/event-kind.ts index 72a7e03d..e144a7b5 100644 --- a/packages/system/src/event-kind.ts +++ b/packages/system/src/event-kind.ts @@ -12,6 +12,11 @@ enum EventKind { SimpleChatMessage = 9, // NIP-29 SealedRumor = 13, // NIP-59 ChatRumor = 14, // NIP-24 + PublicChatChannel = 40, // NIP-28 + PublicChatMetadata = 41, // NIP-28 + PublicChatMessage = 42, // NIP-28 + PublicChatMuteMessage = 43, // NIP-28 + PublicChatMuteUser = 44, // NIP-28 SnortSubscriptions = 1000, // NIP-XX Polls = 6969, // NIP-69 GiftWrap = 1059, // NIP-59 @@ -24,7 +29,10 @@ enum EventKind { TagLists = 30002, // NIP-51c Badge = 30009, // NIP-58 ProfileBadges = 30008, // NIP-58 + LongFormTextNote = 30023, // NIP-23 + AppData = 30_078, // NIP-78 LiveEvent = 30311, // NIP-102 + UserStatus = 30315, // NIP-38 ZapstrTrack = 31337, SimpleChatMetadata = 39_000, // NIP-29 ZapRequest = 9734, // NIP 57 diff --git a/packages/system/src/event-publisher.ts b/packages/system/src/event-publisher.ts index 6c3dd0ac..b2bd0ad4 100644 --- a/packages/system/src/event-publisher.ts +++ b/packages/system/src/event-publisher.ts @@ -3,16 +3,20 @@ import * as utils from "@noble/curves/abstract/utils"; import { unwrap, getPublicKey, unixNow } from "@snort/shared"; import { + decodeEncryptionPayload, EventKind, EventSigner, FullRelaySettings, HexKey, Lists, + MessageEncryptorVersion, NostrEvent, + NostrLink, NotSignedNostrEvent, PowMiner, PrivateKeySigner, RelaySettings, + SignerSupports, TaggedNostrEvent, u256, UserMetadata, @@ -22,6 +26,7 @@ import { EventBuilder } from "./event-builder"; import { EventExt } from "./event-ext"; import { findTag } from "./utils"; import { Nip7Signer } from "./impl/nip7"; +import { base64 } from "@scure/base"; type EventBuilderHook = (ev: EventBuilder) => EventBuilder; @@ -57,16 +62,22 @@ export class EventPublisher { return new EventPublisher(signer, signer.getPubKey()); } + supports(t: SignerSupports) { + return this.#signer.supports.includes(t); + } + get pubKey() { return this.#pubKey; } /** - * Apply POW to every event + * Create a copy of this publisher with PoW */ pow(target: number, miner?: PowMiner) { - this.#pow = target; - this.#miner = miner; + const ret = new EventPublisher(this.#signer, this.#pubKey); + ret.#pow = target; + ret.#miner = miner; + return ret; } #eb(k: EventKind) { @@ -180,10 +191,11 @@ export class EventPublisher { const thread = EventExt.extractThread(replyTo); if (thread) { - if (thread.root || thread.replyTo) { - eb.tag(["e", thread.root?.value ?? thread.replyTo?.value ?? "", "", "root"]); + const rootOrReplyAsRoot = thread.root || thread.replyTo; + if (rootOrReplyAsRoot) { + eb.tag([rootOrReplyAsRoot.key, rootOrReplyAsRoot.value ?? "", rootOrReplyAsRoot.relay ?? "", "root"]); } - eb.tag(["e", replyTo.id, replyTo.relays?.[0] ?? "", "reply"]); + eb.tag([...(NostrLink.fromEvent(replyTo).toEventTag() ?? []), "reply"]); eb.tag(["p", replyTo.pubkey]); for (const pk of thread.pubKeys) { @@ -193,7 +205,7 @@ export class EventPublisher { eb.tag(["p", pk]); } } else { - eb.tag(["e", replyTo.id, "", "reply"]); + eb.tag([...(NostrLink.fromEvent(replyTo).toEventTag() ?? []), "reply"]); // dont tag self in replies if (replyTo.pubkey !== this.#pubKey) { eb.tag(["p", replyTo.pubkey]); @@ -262,6 +274,23 @@ export class EventPublisher { return await this.#sign(eb); } + /** + * Generic decryption using NIP-23 payload scheme + */ + async decryptGeneric(content: string, from: string) { + const pl = decodeEncryptionPayload(content); + switch (pl.v) { + case MessageEncryptorVersion.Nip4: { + const nip4Payload = `${base64.encode(pl.ciphertext)}?iv=${base64.encode(pl.nonce)}`; + return await this.#signer.nip4Decrypt(nip4Payload, from); + } + case MessageEncryptorVersion.XChaCha20: { + return await this.#signer.nip44Decrypt(content, from); + } + } + throw new Error("Not supported version"); + } + async decryptDm(note: NostrEvent) { if (note.kind === EventKind.SealedRumor) { const unseal = await this.unsealRumor(note); diff --git a/packages/system/src/gossip-model.ts b/packages/system/src/gossip-model.ts index d931d6b6..caa05792 100644 --- a/packages/system/src/gossip-model.ts +++ b/packages/system/src/gossip-model.ts @@ -1,7 +1,7 @@ import { ReqFilter, UsersRelays } from "."; import { dedupe, unwrap } from "@snort/shared"; import debug from "debug"; -import { FlatReqFilter } from "request-expander"; +import { FlatReqFilter } from "./query-optimizer"; const PickNRelays = 2; @@ -84,7 +84,7 @@ export function splitByWriteRelays(cache: RelayCache, filter: ReqFilter): Array< }, }); } - debug("GOSSIP")("Picked %o", picked); + debug("GOSSIP")("Picked %O => %O", filter, picked); return picked; } @@ -119,7 +119,7 @@ export function splitFlatByWriteRelays(cache: RelayCache, input: Array { return Promise.resolve(); } diff --git a/packages/system/src/index.ts b/packages/system/src/index.ts index 3064fb4c..ea9ff561 100644 --- a/packages/system/src/index.ts +++ b/packages/system/src/index.ts @@ -1,9 +1,12 @@ import { AuthHandler, RelaySettings, ConnectionStateSnapshot } from "./connection"; import { RequestBuilder } from "./request-builder"; -import { NoteStore } from "./note-collection"; +import { NoteStore, NoteStoreSnapshotData } from "./note-collection"; import { Query } from "./query"; -import { NostrEvent, ReqFilter } from "./nostr"; +import { NostrEvent, ReqFilter, TaggedNostrEvent } from "./nostr"; import { ProfileLoaderService } from "./profile-cache"; +import { RelayCache } from "./gossip-model"; +import { QueryOptimizer } from "./query-optimizer"; +import { base64 } from "@scure/base"; export * from "./nostr-system"; export { default as EventKind } from "./event-kind"; @@ -24,6 +27,8 @@ export * from "./signer"; export * from "./text"; export * from "./pow"; export * from "./pow-util"; +export * from "./query-optimizer"; +export * from "./encrypted"; export * from "./impl/nip4"; export * from "./impl/nip44"; @@ -40,14 +45,72 @@ export interface SystemInterface { * Handler function for NIP-42 */ HandleAuth?: AuthHandler; + + /** + * Get a snapshot of the relay connections + */ get Sockets(): Array; + + /** + * Get an active query by ID + * @param id Query ID + */ GetQuery(id: string): Query | undefined; - Query(type: { new (): T }, req: RequestBuilder | null): Query; + + /** + * Open a new query to relays + * @param type Store type + * @param req Request to send to relays + */ + Query(type: { new (): T }, req: RequestBuilder): Query; + + /** + * Fetch data from nostr relays asynchronously + * @param req Request to send to relays + * @param cb A callback which will fire every 100ms when new data is received + */ + Fetch(req: RequestBuilder, cb?: (evs: Array) => void): Promise; + + /** + * Create a new permanent connection to a relay + * @param address Relay URL + * @param options Read/Write settings + */ ConnectToRelay(address: string, options: RelaySettings): Promise; + + /** + * Disconnect permanent relay connection + * @param address Relay URL + */ DisconnectRelay(address: string): void; + + /** + * Send an event to all permanent connections + * @param ev Event to broadcast + */ BroadcastEvent(ev: NostrEvent): void; + + /** + * Connect to a specific relay and send an event and wait for the response + * @param relay Relay URL + * @param ev Event to send + */ WriteOnceToRelay(relay: string, ev: NostrEvent): Promise; + + /** + * Profile cache/loader + */ get ProfileLoader(): ProfileLoaderService; + + /** + * Relay cache for "Gossip" model + */ + get RelayCache(): RelayCache; + + /** + * Query optimizer + */ + get QueryOptimizer(): QueryOptimizer; } export interface SystemSnapshot { @@ -74,3 +137,25 @@ export interface MessageEncryptor { encryptData(plaintext: string, sharedSecet: Uint8Array): Promise | MessageEncryptorPayload; decryptData(payload: MessageEncryptorPayload, sharedSecet: Uint8Array): Promise | string; } + +export function decodeEncryptionPayload(p: string) { + if (p.startsWith("{") && p.endsWith("}")) { + const pj = JSON.parse(p) as { v: number; nonce: string; ciphertext: string }; + return { + v: pj.v, + nonce: base64.decode(pj.nonce), + ciphertext: base64.decode(pj.ciphertext), + } as MessageEncryptorPayload; + } else { + const buf = base64.decode(p); + return { + v: buf[0], + nonce: buf.subarray(1, 25), + ciphertext: buf.subarray(25), + } as MessageEncryptorPayload; + } +} + +export function encodeEncryptionPayload(p: MessageEncryptorPayload) { + return base64.encode(new Uint8Array([p.v, ...p.nonce, ...p.ciphertext])); +} diff --git a/packages/system/src/links.ts b/packages/system/src/links.ts index 5a8a230c..5d8bd366 100644 --- a/packages/system/src/links.ts +++ b/packages/system/src/links.ts @@ -2,7 +2,7 @@ import * as utils from "@noble/curves/abstract/utils"; import { bech32 } from "@scure/base"; import { HexKey } from "./nostr"; -export enum NostrPrefix { +export const enum NostrPrefix { PublicKey = "npub", PrivateKey = "nsec", Note = "note", diff --git a/packages/system/src/nostr-link.ts b/packages/system/src/nostr-link.ts index 54e9ec22..42e56868 100644 --- a/packages/system/src/nostr-link.ts +++ b/packages/system/src/nostr-link.ts @@ -1,32 +1,74 @@ -import { bech32ToHex, hexToBech32 } from "@snort/shared"; -import { NostrPrefix, decodeTLV, TLVEntryType, encodeTLV } from "."; +import { bech32ToHex, hexToBech32, unwrap } from "@snort/shared"; +import { NostrPrefix, decodeTLV, TLVEntryType, encodeTLV, NostrEvent, TaggedNostrEvent } from "."; +import { findTag } from "./utils"; -export interface NostrLink { - type: NostrPrefix; - id: string; - kind?: number; - author?: string; - relays?: Array; - encode(): string; -} +export class NostrLink { + constructor( + readonly type: NostrPrefix, + readonly id: string, + readonly kind?: number, + readonly author?: string, + readonly relays?: Array, + ) {} -export function createNostrLink(prefix: NostrPrefix, id: string, relays?: string[], kind?: number, author?: string) { - return { - type: prefix, - id, - relays, - kind, - author, - encode: () => { - if (prefix === NostrPrefix.Note || prefix === NostrPrefix.PublicKey) { - return hexToBech32(prefix, id); + encode(): string { + if (this.type === NostrPrefix.Note || this.type === NostrPrefix.PrivateKey || this.type === NostrPrefix.PublicKey) { + return hexToBech32(this.type, this.id); + } else { + return encodeTLV(this.type, this.id, this.relays, this.kind, this.author); + } + } + + toEventTag() { + const relayEntry = this.relays ? [this.relays[0]] : []; + if (this.type === NostrPrefix.PublicKey) { + return ["p", this.id]; + } else if (this.type === NostrPrefix.Note || this.type === NostrPrefix.Event) { + return ["e", this.id, ...relayEntry]; + } else if (this.type === NostrPrefix.Address) { + return ["a", `${this.kind}:${this.author}:${this.id}`, ...relayEntry]; + } + } + + matchesEvent(ev: NostrEvent) { + if (this.type === NostrPrefix.Address) { + const dTag = findTag(ev, "d"); + if (dTag && dTag === this.id && unwrap(this.author) === ev.pubkey && unwrap(this.kind) === ev.kind) { + return true; } - if (prefix === NostrPrefix.Address || prefix === NostrPrefix.Event || prefix === NostrPrefix.Profile) { - return encodeTLV(prefix, id, relays, kind, author); + } else if (this.type === NostrPrefix.Event || this.type === NostrPrefix.Note) { + return this.id === ev.id; + } + + return false; + } + + static fromTag(tag: Array) { + const relays = tag.length > 2 ? [tag[2]] : undefined; + switch (tag[0]) { + case "e": { + return new NostrLink(NostrPrefix.Event, tag[1], undefined, undefined, relays); } - return ""; - }, - } as NostrLink; + case "p": { + return new NostrLink(NostrPrefix.Profile, tag[1], undefined, undefined, relays); + } + case "a": { + const [kind, author, dTag] = tag[1].split(":"); + return new NostrLink(NostrPrefix.Address, dTag, Number(kind), author, relays); + } + } + throw new Error(`Unknown tag kind ${tag[0]}`); + } + + static fromEvent(ev: TaggedNostrEvent | NostrEvent) { + const relays = "relays" in ev ? ev.relays : undefined; + + if (ev.kind >= 30_000 && ev.kind < 40_000) { + const dTag = unwrap(findTag(ev, "d")); + return new NostrLink(NostrPrefix.Address, dTag, ev.kind, ev.pubkey, relays); + } + return new NostrLink(NostrPrefix.Event, ev.id, ev.kind, ev.pubkey, relays); + } } export function validateNostrLink(link: string): boolean { @@ -63,19 +105,11 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin if (isPrefix(NostrPrefix.PublicKey)) { const id = bech32ToHex(entity); if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.PublicKey, - id: id, - encode: () => hexToBech32(NostrPrefix.PublicKey, id), - }; + return new NostrLink(NostrPrefix.PublicKey, id); } else if (isPrefix(NostrPrefix.Note)) { const id = bech32ToHex(entity); if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Note, - id: id, - encode: () => hexToBech32(NostrPrefix.Note, id), - }; + return new NostrLink(NostrPrefix.Note, id); } else if (isPrefix(NostrPrefix.Profile) || isPrefix(NostrPrefix.Event) || isPrefix(NostrPrefix.Address)) { const decoded = decodeTLV(entity); @@ -84,45 +118,17 @@ export function parseNostrLink(link: string, prefixHint?: NostrPrefix): NostrLin const author = decoded.find(a => a.type === TLVEntryType.Author)?.value as string; const kind = decoded.find(a => a.type === TLVEntryType.Kind)?.value as number; - const encode = () => { - return entity; // return original - }; if (isPrefix(NostrPrefix.Profile)) { if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Profile, - id, - relays, - kind, - author, - encode, - }; + return new NostrLink(NostrPrefix.Profile, id, kind, author, relays); } else if (isPrefix(NostrPrefix.Event)) { if (id.length !== 64) throw new Error("Invalid nostr link, must contain 32 byte id"); - return { - type: NostrPrefix.Event, - id, - relays, - kind, - author, - encode, - }; + return new NostrLink(NostrPrefix.Event, id, kind, author, relays); } else if (isPrefix(NostrPrefix.Address)) { - return { - type: NostrPrefix.Address, - id, - relays, - kind, - author, - encode, - }; + return new NostrLink(NostrPrefix.Address, id, kind, author, relays); } } else if (prefixHint) { - return { - type: prefixHint, - id: link, - encode: () => hexToBech32(prefixHint, link), - }; + return new NostrLink(prefixHint, link); } throw new Error("Invalid nostr link"); } diff --git a/packages/system/src/nostr-system.ts b/packages/system/src/nostr-system.ts index 7145e9e0..31e20987 100644 --- a/packages/system/src/nostr-system.ts +++ b/packages/system/src/nostr-system.ts @@ -4,8 +4,8 @@ import { unwrap, sanitizeRelayUrl, ExternalStore, FeedCache } from "@snort/share import { NostrEvent, TaggedNostrEvent } from "./nostr"; import { AuthHandler, Connection, RelaySettings, ConnectionStateSnapshot } from "./connection"; import { Query } from "./query"; -import { NoteStore } from "./note-collection"; -import { BuiltRawReqFilter, RequestBuilder } from "./request-builder"; +import { NoteCollection, NoteStore, NoteStoreSnapshotData } from "./note-collection"; +import { BuiltRawReqFilter, RequestBuilder, RequestStrategy } from "./request-builder"; import { RelayMetricHandler } from "./relay-metric-handler"; import { MetadataCache, @@ -19,6 +19,10 @@ import { db, UsersRelays, } from "."; +import { EventsCache } from "./cache/events"; +import { RelayCache } from "./gossip-model"; +import { QueryOptimizer, DefaultQueryOptimizer } from "./query-optimizer"; +import { trimFilters } from "./request-trim"; /** * Manages nostr content retrieval system @@ -66,26 +70,38 @@ export class NostrSystem extends ExternalStore implements System */ #relayMetrics: RelayMetricHandler; + /** + * General events cache + */ + #eventsCache: FeedCache; + + /** + * Query optimizer instance + */ + #queryOptimizer: QueryOptimizer; + constructor(props: { authHandler?: AuthHandler; relayCache?: FeedCache; profileCache?: FeedCache; relayMetrics?: FeedCache; + eventsCache?: FeedCache; + queryOptimizer?: QueryOptimizer; }) { super(); this.#handleAuth = props.authHandler; this.#relayCache = props.relayCache ?? new UserRelaysCache(); this.#profileCache = props.profileCache ?? new UserProfileCache(); this.#relayMetricsCache = props.relayMetrics ?? new RelayMetricCache(); + this.#eventsCache = props.eventsCache ?? new EventsCache(); + this.#queryOptimizer = props.queryOptimizer ?? DefaultQueryOptimizer; this.#profileLoader = new ProfileLoaderService(this, this.#profileCache); this.#relayMetrics = new RelayMetricHandler(this.#relayMetricsCache); this.#cleanup(); } + HandleAuth?: AuthHandler | undefined; - /** - * Profile loader service allows you to request profiles - */ get ProfileLoader() { return this.#profileLoader; } @@ -94,12 +110,25 @@ export class NostrSystem extends ExternalStore implements System return [...this.#sockets.values()].map(a => a.snapshot()); } + get RelayCache(): RelayCache { + return this.#relayCache; + } + + get QueryOptimizer(): QueryOptimizer { + return this.#queryOptimizer; + } + /** * Setup caches */ async Init() { db.ready = await db.isAvailable(); - const t = [this.#relayCache.preload(), this.#profileCache.preload(), this.#relayMetricsCache.preload()]; + const t = [ + this.#relayCache.preload(), + this.#profileCache.preload(), + this.#relayMetricsCache.preload(), + this.#eventsCache.preload(), + ]; await Promise.all(t); } @@ -112,10 +141,10 @@ export class NostrSystem extends ExternalStore implements System if (!this.#sockets.has(addr)) { const c = new Connection(addr, options, this.#handleAuth?.bind(this)); this.#sockets.set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = code => this.OnRelayDisconnect(c, code); - c.OnConnected = r => this.OnRelayConnected(c, r); + c.OnEvent = (s, e) => this.#onEvent(s, e); + c.OnEose = s => this.#onEndOfStoredEvents(c, s); + c.OnDisconnect = code => this.#onRelayDisconnect(c, code); + c.OnConnected = r => this.#onRelayConnected(c, r); await c.Connect(); } else { // update settings if already connected @@ -126,7 +155,7 @@ export class NostrSystem extends ExternalStore implements System } } - OnRelayConnected(c: Connection, wasReconnect: boolean) { + #onRelayConnected(c: Connection, wasReconnect: boolean) { if (wasReconnect) { for (const [, q] of this.Queries) { q.connectionRestored(c); @@ -134,22 +163,22 @@ export class NostrSystem extends ExternalStore implements System } } - OnRelayDisconnect(c: Connection, code: number) { + #onRelayDisconnect(c: Connection, code: number) { this.#relayMetrics.onDisconnect(c, code); for (const [, q] of this.Queries) { q.connectionLost(c.Id); } } - OnEndOfStoredEvents(c: Readonly, sub: string) { + #onEndOfStoredEvents(c: Readonly, sub: string) { for (const [, v] of this.Queries) { v.eose(sub, c); } } - OnEvent(sub: string, ev: TaggedNostrEvent) { + #onEvent(sub: string, ev: TaggedNostrEvent) { for (const [, v] of this.Queries) { - v.onEvent(sub, ev); + v.handleEvent(sub, ev); } } @@ -163,10 +192,10 @@ export class NostrSystem extends ExternalStore implements System if (!this.#sockets.has(addr)) { const c = new Connection(addr, { read: true, write: true }, this.#handleAuth?.bind(this), true); this.#sockets.set(addr, c); - c.OnEvent = (s, e) => this.OnEvent(s, e); - c.OnEose = s => this.OnEndOfStoredEvents(c, s); - c.OnDisconnect = code => this.OnRelayDisconnect(c, code); - c.OnConnected = r => this.OnRelayConnected(c, r); + c.OnEvent = (s, e) => this.#onEvent(s, e); + c.OnEose = s => this.#onEndOfStoredEvents(c, s); + c.OnDisconnect = code => this.#onRelayDisconnect(c, code); + c.OnConnected = r => this.#onRelayConnected(c, r); await c.Connect(); return c; } @@ -190,6 +219,35 @@ export class NostrSystem extends ExternalStore implements System return this.Queries.get(id); } + Fetch(req: RequestBuilder, cb?: (evs: Array) => void) { + const q = this.Query(NoteCollection, req); + return new Promise(resolve => { + let t: ReturnType | undefined; + let tBuf: Array = []; + const releaseOnEvent = cb + ? q.feed.onEvent(evs => { + if (!t) { + tBuf = [...evs]; + t = setTimeout(() => { + t = undefined; + cb(tBuf); + }, 100); + } else { + tBuf.push(...evs); + } + }) + : undefined; + const releaseFeedHook = q.feed.hook(() => { + if (q.progress === 1) { + releaseOnEvent?.(); + releaseFeedHook(); + q.cancel(); + resolve(unwrap(q.feed.snapshot.data)); + } + }); + }); + } + Query(type: { new (): T }, req: RequestBuilder): Query { const existing = this.Queries.get(req.id); if (existing) { @@ -197,9 +255,7 @@ export class NostrSystem extends ExternalStore implements System if (existing.fromInstance === req.instance) { return existing; } - const filters = !req.options?.skipDiff - ? req.buildDiff(this.#relayCache, existing.flatFilters) - : req.build(this.#relayCache); + const filters = !req.options?.skipDiff ? req.buildDiff(this, existing.filters) : req.build(this); if (filters.length === 0 && !!req.options?.skipDiff) { return existing; } else { @@ -212,8 +268,17 @@ export class NostrSystem extends ExternalStore implements System } else { const store = new type(); - const filters = req.build(this.#relayCache); + const filters = req.build(this); const q = new Query(req.id, req.instance, store, req.options?.leaveOpen); + if (filters.some(a => a.filters.some(b => b.ids))) { + const expectIds = new Set(filters.flatMap(a => a.filters).flatMap(a => a.ids ?? [])); + q.feed.onEvent(async evs => { + const toSet = evs.filter(a => expectIds.has(a.id) && this.#eventsCache.getFromCache(a.id) === undefined); + if (toSet.length > 0) { + await this.#eventsCache.bulkSet(toSet); + } + }); + } this.Queries.set(req.id, q); for (const subQ of filters) { this.SendQuery(q, subQ); @@ -224,6 +289,32 @@ export class NostrSystem extends ExternalStore implements System } async SendQuery(q: Query, qSend: BuiltRawReqFilter) { + // trim query of cached ids + for (const f of qSend.filters) { + if (f.ids) { + const cacheResults = await this.#eventsCache.bulkGet(f.ids); + if (cacheResults.length > 0) { + const resultIds = new Set(cacheResults.map(a => a.id)); + f.ids = f.ids.filter(a => !resultIds.has(a)); + q.insertCompletedTrace( + { + filters: [{ ...f, ids: [...resultIds] }], + strategy: RequestStrategy.ExplicitRelays, + relay: qSend.relay, + }, + cacheResults as Array, + ); + } + } + } + + // check for empty filters + const fNew = trimFilters(qSend.filters); + if (fNew.length === 0) { + return; + } + qSend.filters = fNew; + if (qSend.relay) { this.#log("Sending query to %s %O", qSend.relay, qSend); const s = this.#sockets.get(qSend.relay); diff --git a/packages/system/src/note-collection.ts b/packages/system/src/note-collection.ts index 97448dbd..54009ddf 100644 --- a/packages/system/src/note-collection.ts +++ b/packages/system/src/note-collection.ts @@ -20,7 +20,7 @@ export const EmptySnapshot = { }, } as StoreSnapshot; -export type NoteStoreSnapshotData = Readonly> | Readonly; +export type NoteStoreSnapshotData = Array | TaggedNostrEvent; export type NoteStoreHook = () => void; export type NoteStoreHookRelease = () => void; export type OnEventCallback = (e: Readonly>) => void; @@ -134,10 +134,28 @@ export abstract class HookedNoteStore i } } +/** + * A store which doesnt store anything, useful for hooks only + */ +export class NoopStore extends HookedNoteStore> { + override add(ev: readonly TaggedNostrEvent[] | Readonly): void { + this.onChange(Array.isArray(ev) ? ev : [ev]); + } + + override clear(): void { + // nothing to do + } + + protected override takeSnapshot(): TaggedNostrEvent[] | undefined { + // nothing to do + return undefined; + } +} + /** * A simple flat container of events with no duplicates */ -export class FlatNoteStore extends HookedNoteStore>> { +export class FlatNoteStore extends HookedNoteStore> { #events: Array = []; #ids: Set = new Set(); @@ -176,7 +194,7 @@ export class FlatNoteStore extends HookedNoteStore>> { +export class KeyedReplaceableNoteStore extends HookedNoteStore> { #keyFn: (ev: TaggedNostrEvent) => string; #events: Map = new Map(); diff --git a/packages/system/src/pow-util.ts b/packages/system/src/pow-util.ts index aab35f5d..52536e67 100644 --- a/packages/system/src/pow-util.ts +++ b/packages/system/src/pow-util.ts @@ -20,15 +20,7 @@ export function minePow(e: NostrPowEvent, target: number) { e.tags.push(["nonce", ctr.toString(), target.toString()]); } do { - //roll ctr and compute id - const now = Math.floor(new Date().getTime() / 1000); - // reset ctr if timestamp changed, this is not really needed but makes the ctr value smaller - if (now !== e.created_at) { - ctr = 0; - e.created_at = now; - } e.tags[nonceTagIdx][1] = (++ctr).toString(); - e.id = createId(e); } while (countLeadingZeros(e.id) < target); diff --git a/packages/system/src/profile-cache.ts b/packages/system/src/profile-cache.ts index 899bd73c..6118a8ae 100644 --- a/packages/system/src/profile-cache.ts +++ b/packages/system/src/profile-cache.ts @@ -37,13 +37,11 @@ export class ProfileLoaderService { * Request profile metadata for a set of pubkeys */ TrackMetadata(pk: HexKey | Array) { - const bufferNow = []; for (const p of Array.isArray(pk) ? pk : [pk]) { - if (p.length === 64 && this.#wantsMetadata.add(p)) { - bufferNow.push(p); + if (p.length === 64) { + this.#wantsMetadata.add(p); } } - this.#cache.buffer(bufferNow); } /** @@ -64,6 +62,25 @@ export class ProfileLoaderService { } } + async fetchProfile(key: string) { + const existing = this.Cache.get(key); + if (existing) { + return existing; + } else { + return await new Promise((resolve, reject) => { + this.TrackMetadata(key); + const release = this.Cache.hook(() => { + const existing = this.Cache.getFromCache(key); + if (existing) { + resolve(existing); + release(); + this.UntrackMetadata(key); + } + }, key); + }); + } + } + async #FetchMetadata() { const missingFromCache = await this.#cache.buffer([...this.#wantsMetadata]); diff --git a/packages/system/src/query-optimizer/index.ts b/packages/system/src/query-optimizer/index.ts new file mode 100644 index 00000000..21d8b44a --- /dev/null +++ b/packages/system/src/query-optimizer/index.ts @@ -0,0 +1,46 @@ +import { ReqFilter } from "../nostr"; +import { expandFilter } from "./request-expander"; +import { flatMerge, mergeSimilar } from "./request-merger"; +import { diffFilters } from "./request-splitter"; + +export interface FlatReqFilter { + keys: number; + ids?: string; + authors?: string; + kinds?: number; + "#e"?: string; + "#p"?: string; + "#t"?: string; + "#d"?: string; + "#r"?: string; + search?: string; + since?: number; + until?: number; + limit?: number; +} + +export interface QueryOptimizer { + expandFilter(f: ReqFilter): Array; + getDiff(prev: Array, next: Array): Array; + flatMerge(all: Array): Array; + compress(all: Array): Array; +} + +export const DefaultQueryOptimizer = { + expandFilter: (f: ReqFilter) => { + return expandFilter(f); + }, + getDiff: (prev: Array, next: Array) => { + const diff = diffFilters( + prev.flatMap(a => expandFilter(a)), + next.flatMap(a => expandFilter(a)), + ); + return diff.added; + }, + flatMerge: (all: Array) => { + return flatMerge(all); + }, + compress: (all: Array) => { + return mergeSimilar(all); + }, +} as QueryOptimizer; diff --git a/packages/system/src/request-expander.ts b/packages/system/src/query-optimizer/request-expander.ts similarity index 75% rename from packages/system/src/request-expander.ts rename to packages/system/src/query-optimizer/request-expander.ts index 669c55a3..3dfdb5ef 100644 --- a/packages/system/src/request-expander.ts +++ b/packages/system/src/query-optimizer/request-expander.ts @@ -1,20 +1,5 @@ -import { ReqFilter } from "./nostr"; - -export interface FlatReqFilter { - keys: number; - ids?: string; - authors?: string; - kinds?: number; - "#e"?: string; - "#p"?: string; - "#t"?: string; - "#d"?: string; - "#r"?: string; - search?: string; - since?: number; - until?: number; - limit?: number; -} +import { FlatReqFilter } from "."; +import { ReqFilter } from "../nostr"; /** * Expand a filter into its most fine grained form diff --git a/packages/system/src/request-merger.ts b/packages/system/src/query-optimizer/request-merger.ts similarity index 96% rename from packages/system/src/request-merger.ts rename to packages/system/src/query-optimizer/request-merger.ts index a345ef03..5ed3900e 100644 --- a/packages/system/src/request-merger.ts +++ b/packages/system/src/query-optimizer/request-merger.ts @@ -1,6 +1,6 @@ import { distance } from "@snort/shared"; -import { ReqFilter } from "."; -import { FlatReqFilter } from "./request-expander"; +import { ReqFilter } from ".."; +import { FlatReqFilter } from "."; /** * Keys which can change the entire meaning of the filter outside the array types @@ -105,7 +105,7 @@ export function flatMerge(all: Array): Array { function mergeFiltersInSet(filters: Array) { return filters.reduce((acc, a) => { Object.entries(a).forEach(([k, v]) => { - if (k === "keys") return; + if (k === "keys" || v === undefined) return; if (DiscriminatorKeys.includes(k)) { acc[k] = v; } else { diff --git a/packages/system/src/request-splitter.ts b/packages/system/src/query-optimizer/request-splitter.ts similarity index 88% rename from packages/system/src/request-splitter.ts rename to packages/system/src/query-optimizer/request-splitter.ts index 7f51295d..623a2798 100644 --- a/packages/system/src/request-splitter.ts +++ b/packages/system/src/query-optimizer/request-splitter.ts @@ -1,5 +1,5 @@ -import { flatFilterEq } from "./utils"; -import { FlatReqFilter } from "./request-expander"; +import { flatFilterEq } from "../utils"; +import { FlatReqFilter } from "."; export function diffFilters(prev: Array, next: Array, calcRemoved?: boolean) { const added = []; diff --git a/packages/system/src/query.ts b/packages/system/src/query.ts index 0be31a46..46a860e6 100644 --- a/packages/system/src/query.ts +++ b/packages/system/src/query.ts @@ -4,9 +4,7 @@ import { unixNowMs, unwrap } from "@snort/shared"; import { Connection, ReqFilter, Nips, TaggedNostrEvent } from "."; import { NoteStore } from "./note-collection"; -import { flatMerge } from "./request-merger"; import { BuiltRawReqFilter } from "./request-builder"; -import { FlatReqFilter, expandFilter } from "./request-expander"; import { eventMatchesFilter } from "./request-matcher"; /** @@ -19,7 +17,6 @@ class QueryTrace { eose?: number; close?: number; #wasForceClosed = false; - readonly flatFilters: Array; readonly #fnClose: (id: string) => void; readonly #fnProgress: () => void; @@ -34,7 +31,6 @@ class QueryTrace { this.start = unixNowMs(); this.#fnClose = fnClose; this.#fnProgress = fnProgress; - this.flatFilters = filters.flatMap(expandFilter); } sentToRelay() { @@ -166,18 +162,14 @@ export class Query implements QueryBase { * Recompute the complete set of compressed filters from all query traces */ get filters() { - return flatMerge(this.flatFilters); - } - - get flatFilters() { - return this.#tracing.flatMap(a => a.flatFilters); + return this.#tracing.flatMap(a => a.filters); } get feed() { return this.#feed; } - onEvent(sub: string, e: TaggedNostrEvent) { + handleEvent(sub: string, e: TaggedNostrEvent) { for (const t of this.#tracing) { if (t.id === sub) { if (t.filters.some(v => eventMatchesFilter(e, v))) { @@ -205,6 +197,28 @@ export class Query implements QueryBase { this.#stopCheckTraces(); } + /** + * Insert a new trace as a placeholder + */ + insertCompletedTrace(subq: BuiltRawReqFilter, data: Readonly>) { + const qt = new QueryTrace( + subq.relay, + subq.filters, + "", + () => { + // nothing to close + }, + () => { + // nothing to progress + }, + ); + qt.sentToRelay(); + qt.gotEose(); + this.#tracing.push(qt); + this.feed.add(data); + return qt; + } + sendToRelay(c: Connection, subq: BuiltRawReqFilter) { if (!this.#canSendQuery(c, subq)) { return; diff --git a/packages/system/src/request-builder.ts b/packages/system/src/request-builder.ts index 747a97a4..e8ae703f 100644 --- a/packages/system/src/request-builder.ts +++ b/packages/system/src/request-builder.ts @@ -1,12 +1,11 @@ import debug from "debug"; import { v4 as uuid } from "uuid"; -import { appendDedupe, sanitizeRelayUrl, unixNowMs } from "@snort/shared"; +import { appendDedupe, dedupe, sanitizeRelayUrl, unixNowMs, unwrap } from "@snort/shared"; -import { ReqFilter, u256, HexKey, EventKind } from "."; -import { diffFilters } from "./request-splitter"; +import EventKind from "./event-kind"; +import { NostrLink, NostrPrefix, SystemInterface } from "index"; +import { ReqFilter, u256, HexKey } from "./nostr"; import { RelayCache, splitByWriteRelays, splitFlatByWriteRelays } from "./gossip-model"; -import { flatMerge, mergeSimilar } from "./request-merger"; -import { FlatReqFilter, expandFilter } from "./request-expander"; /** * Which strategy is used when building REQ filters @@ -83,6 +82,12 @@ export class RequestBuilder { return ret; } + withBareFilter(f: ReqFilter) { + const ret = new RequestFilterBuilder(f); + this.#builders.push(ret); + return ret; + } + withOptions(opt: RequestBuilderOptions) { this.#options = { ...this.#options, @@ -95,26 +100,25 @@ export class RequestBuilder { return this.#builders.map(f => f.filter); } - build(relays: RelayCache): Array { - const expanded = this.#builders.flatMap(a => a.build(relays, this.id)); - return this.#groupByRelay(expanded); + build(system: SystemInterface): Array { + const expanded = this.#builders.flatMap(a => a.build(system.RelayCache, this.id)); + return this.#groupByRelay(system, expanded); } /** * Detects a change in request from a previous set of filters */ - buildDiff(relays: RelayCache, prev: Array): Array { + buildDiff(system: SystemInterface, prev: Array): Array { const start = unixNowMs(); - const next = this.#builders.flatMap(f => expandFilter(f.filter)); - const diff = diffFilters(prev, next); + const diff = system.QueryOptimizer.getDiff(prev, this.buildRaw()); const ts = unixNowMs() - start; - this.#log("buildDiff %s %d ms", this.id, ts); - if (diff.changed) { - return splitFlatByWriteRelays(relays, diff.added).map(a => { + this.#log("buildDiff %s %d ms +%d", this.id, ts, diff.length); + if (diff.length > 0) { + return splitFlatByWriteRelays(system.RelayCache, diff).map(a => { return { strategy: RequestStrategy.AuthorsRelays, - filters: flatMerge(a.filters), + filters: system.QueryOptimizer.flatMerge(a.filters), relay: a.relay, }; }); @@ -129,7 +133,7 @@ export class RequestBuilder { * @param expanded * @returns */ - #groupByRelay(expanded: Array) { + #groupByRelay(system: SystemInterface, expanded: Array) { const relayMerged = expanded.reduce((acc, v) => { const existing = acc.get(v.relay); if (existing) { @@ -142,7 +146,9 @@ export class RequestBuilder { const filtersSquashed = [...relayMerged.values()].map(a => { return { - filters: mergeSimilar(a.flatMap(b => b.filters)), + filters: system.QueryOptimizer.flatMerge( + a.flatMap(b => b.filters.flatMap(c => system.QueryOptimizer.expandFilter(c))), + ), relay: a[0].relay, strategy: a[0].strategy, } as BuiltRawReqFilter; @@ -156,9 +162,13 @@ export class RequestBuilder { * Builder class for a single request filter */ export class RequestFilterBuilder { - #filter: ReqFilter = {}; + #filter: ReqFilter; #relays = new Set(); + constructor(f?: ReqFilter) { + this.#filter = f ?? {}; + } + get filter() { return { ...this.#filter }; } @@ -209,9 +219,9 @@ export class RequestFilterBuilder { return this; } - tag(key: "e" | "p" | "d" | "t" | "r" | "a" | "g", value?: Array) { + tag(key: "e" | "p" | "d" | "t" | "r" | "a" | "g" | string, value?: Array) { if (!value) return this; - this.#filter[`#${key}`] = appendDedupe(this.#filter[`#${key}`], value); + this.#filter[`#${key}`] = appendDedupe(this.#filter[`#${key}`] as Array, value); return this; } @@ -221,6 +231,36 @@ export class RequestFilterBuilder { return this; } + /** + * Get event from link + */ + link(link: NostrLink) { + if (link.type === NostrPrefix.Address) { + this.tag("d", [link.id]) + .kinds([unwrap(link.kind)]) + .authors([unwrap(link.author)]); + } else { + this.ids([link.id]); + } + link.relays?.forEach(v => this.relay(v)); + return this; + } + + /** + * Get replies to link with e/a tags + */ + replyToLink(links: Array) { + const types = dedupe(links.map(a => a.type)); + if (types.length > 1) throw new Error("Cannot add multiple links of different kinds"); + + const tags = links.map(a => unwrap(a.toEventTag())); + this.tag( + tags[0][0], + tags.map(v => v[1]), + ); + return this; + } + /** * Build/expand this filter into a set of relay specific queries */ @@ -250,7 +290,7 @@ export class RequestFilterBuilder { return [ { - filters: [this.filter], + filters: [this.#filter], relay: "", strategy: RequestStrategy.DefaultRelays, }, diff --git a/packages/system/src/request-trim.ts b/packages/system/src/request-trim.ts new file mode 100644 index 00000000..a1de5f64 --- /dev/null +++ b/packages/system/src/request-trim.ts @@ -0,0 +1,15 @@ +import { ReqFilter } from "nostr"; + +/** + * Remove empty filters, filters which would result in no results + */ +export function trimFilters(filters: Array) { + const fNew = []; + for (const f of filters) { + const ent = Object.entries(f).filter(([, v]) => Array.isArray(v)); + if (ent.every(([, v]) => (v as Array).length > 0)) { + fNew.push(f); + } + } + return fNew; +} diff --git a/packages/system/src/signer.ts b/packages/system/src/signer.ts index d2d25c81..b15bb378 100644 --- a/packages/system/src/signer.ts +++ b/packages/system/src/signer.ts @@ -3,10 +3,12 @@ import { getPublicKey } from "@snort/shared"; import { EventExt } from "./event-ext"; import { Nip4WebCryptoEncryptor } from "./impl/nip4"; import { XChaCha20Encryptor } from "./impl/nip44"; -import { MessageEncryptorPayload, MessageEncryptorVersion } from "./index"; +import { MessageEncryptorVersion, decodeEncryptionPayload, encodeEncryptionPayload } from "./index"; import { NostrEvent } from "./nostr"; import { base64 } from "@scure/base"; +export type SignerSupports = "nip04" | "nip44" | string; + export interface EventSigner { init(): Promise; getPubKey(): Promise | string; @@ -15,6 +17,7 @@ export interface EventSigner { nip44Encrypt(content: string, key: string): Promise; nip44Decrypt(content: string, otherKey: string): Promise; sign(ev: NostrEvent): Promise; + get supports(): Array; } export class PrivateKeySigner implements EventSigner { @@ -30,6 +33,10 @@ export class PrivateKeySigner implements EventSigner { this.#publicKey = getPublicKey(this.#privateKey); } + get supports(): string[] { + return ["nip04", "nip44"]; + } + get privateKey() { return this.#privateKey; } @@ -67,11 +74,11 @@ export class PrivateKeySigner implements EventSigner { const enc = new XChaCha20Encryptor(); const shared = enc.getSharedSecret(this.#privateKey, key); const data = enc.encryptData(content, shared); - return this.#encodePayload(data); + return encodeEncryptionPayload(data); } async nip44Decrypt(content: string, otherKey: string) { - const payload = this.#decodePayload(content); + const payload = decodeEncryptionPayload(content); if (payload.v !== MessageEncryptorVersion.XChaCha20) throw new Error("Invalid payload version"); const enc = new XChaCha20Encryptor(); @@ -79,28 +86,6 @@ export class PrivateKeySigner implements EventSigner { return enc.decryptData(payload, shared); } - #decodePayload(p: string) { - if (p.startsWith("{") && p.endsWith("}")) { - const pj = JSON.parse(p) as { v: number; nonce: string; ciphertext: string }; - return { - v: pj.v, - nonce: base64.decode(pj.nonce), - ciphertext: base64.decode(pj.ciphertext), - } as MessageEncryptorPayload; - } else { - const buf = base64.decode(p); - return { - v: buf[0], - nonce: buf.subarray(1, 25), - ciphertext: buf.subarray(25), - } as MessageEncryptorPayload; - } - } - - #encodePayload(p: MessageEncryptorPayload) { - return base64.encode(new Uint8Array([p.v, ...p.nonce, ...p.ciphertext])); - } - sign(ev: NostrEvent): Promise { EventExt.sign(ev, this.#privateKey); return Promise.resolve(ev); diff --git a/packages/system/src/system-worker.ts b/packages/system/src/system-worker.ts index 2697c0cb..d2322931 100644 --- a/packages/system/src/system-worker.ts +++ b/packages/system/src/system-worker.ts @@ -2,10 +2,12 @@ import { ExternalStore } from "@snort/shared"; import { SystemSnapshot, SystemInterface, ProfileLoaderService } from "."; import { AuthHandler, ConnectionStateSnapshot, RelaySettings } from "./connection"; -import { NostrEvent } from "./nostr"; -import { NoteStore } from "./note-collection"; +import { NostrEvent, TaggedNostrEvent } from "./nostr"; +import { NoteStore, NoteStoreSnapshotData } from "./note-collection"; import { Query } from "./query"; import { RequestBuilder } from "./request-builder"; +import { RelayCache } from "./gossip-model"; +import { QueryOptimizer } from "./query-optimizer"; export class SystemWorker extends ExternalStore implements SystemInterface { #port: MessagePort; @@ -21,10 +23,21 @@ export class SystemWorker extends ExternalStore implements Syste } } + Fetch(req: RequestBuilder, cb?: (evs: Array) => void): Promise { + throw new Error("Method not implemented."); + } + get ProfileLoader(): ProfileLoaderService { throw new Error("Method not implemented."); } + get RelayCache(): RelayCache { + throw new Error("Method not implemented."); + } + + get QueryOptimizer(): QueryOptimizer { + throw new Error("Method not implemented."); + } HandleAuth?: AuthHandler; get Sockets(): ConnectionStateSnapshot[] { diff --git a/packages/system/src/utils.ts b/packages/system/src/utils.ts index ec9a2b35..319e30c0 100644 --- a/packages/system/src/utils.ts +++ b/packages/system/src/utils.ts @@ -1,5 +1,5 @@ import { equalProp } from "@snort/shared"; -import { FlatReqFilter } from "./request-expander"; +import { FlatReqFilter } from "./query-optimizer"; import { NostrEvent, ReqFilter } from "./nostr"; export function findTag(e: NostrEvent, tag: string) { diff --git a/packages/system/src/zaps.ts b/packages/system/src/zaps.ts index 57c3b355..bf9a65b5 100644 --- a/packages/system/src/zaps.ts +++ b/packages/system/src/zaps.ts @@ -4,6 +4,8 @@ import { HexKey, NostrEvent } from "./nostr"; import { findTag } from "./utils"; import { MetadataCache } from "./cache"; +const ParsedZapCache = new Map(); + function getInvoice(zap: NostrEvent): InvoiceDetails | undefined { const bolt11 = findTag(zap, "bolt11"); if (!bolt11) { @@ -13,6 +15,11 @@ function getInvoice(zap: NostrEvent): InvoiceDetails | undefined { } export function parseZap(zapReceipt: NostrEvent, userCache: FeedCache, refNote?: NostrEvent): ParsedZap { + const existing = ParsedZapCache.get(zapReceipt.id); + if (existing) { + return existing; + } + let innerZapJson = findTag(zapReceipt, "description"); if (innerZapJson) { try { @@ -67,7 +74,7 @@ export function parseZap(zapReceipt: NostrEvent, userCache: FeedCache { + system.DisconnectRelay(Relay); + }, 1000); +} + +test().catch(console.error); diff --git a/packages/system/tests/utils.test.ts b/packages/system/tests/utils.test.ts index 779d83a9..d9f27011 100644 --- a/packages/system/tests/utils.test.ts +++ b/packages/system/tests/utils.test.ts @@ -29,7 +29,7 @@ describe("splitByUrl", () => { "https://i.imgur.com/rkqhjeq.png", " Every form of money that could be inflated by way of force or technological advancement has been. ", "https://www.dw.com/de/amtsinhaber-mnangagwa-gewinnt-präsidentenwahl-in-simbabwe/a-66640006?maca=de-rss-de-all-1119-xml-atom", - " and some shit." + " and some shit.", ]; expect(splitByUrl(inputStr)).toEqual(expectedOutput); diff --git a/yarn.lock b/yarn.lock index 4ec6f439..4cb9d1b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,7 +12,7 @@ __metadata: languageName: node linkType: hard -"@ampproject/remapping@npm:^2.2.0": +"@ampproject/remapping@npm:^2.2.0, @ampproject/remapping@npm:^2.2.1": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" dependencies: @@ -1376,7 +1376,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4": version: 7.22.11 resolution: "@babel/runtime@npm:7.22.11" dependencies: @@ -1479,7 +1479,7 @@ __metadata: languageName: node linkType: hard -"@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": +"@eslint-community/regexpp@npm:^4.5.1, @eslint-community/regexpp@npm:^4.6.1": version: 4.8.0 resolution: "@eslint-community/regexpp@npm:4.8.0" checksum: 601e6d033d556e98e8c929905bef335f20d7389762812df4d0f709d9b4d2631610dda975fb272e23b5b68e24a163b3851b114c8080a0a19fb4c141a1eff6305b @@ -1984,7 +1984,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.15": version: 1.4.15 resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 @@ -2507,26 +2507,6 @@ __metadata: languageName: node linkType: hard -"@reduxjs/toolkit@npm:^1.9.1": - version: 1.9.5 - resolution: "@reduxjs/toolkit@npm:1.9.5" - dependencies: - immer: ^9.0.21 - redux: ^4.2.1 - redux-thunk: ^2.4.2 - reselect: ^4.1.8 - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 - react-redux: ^7.2.1 || ^8.0.2 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true - checksum: 54672c5593d05208af577e948a338f23128d3aa01ef056ab0d40bcfa14400cf6566be99e11715388f12c1d7655cdf7c5c6b63cb92eb0fecf996c454a46a3914c - languageName: node - linkType: hard - "@remix-run/router@npm:1.8.0": version: 1.8.0 resolution: "@remix-run/router@npm:1.8.0" @@ -2705,19 +2685,20 @@ __metadata: "@lightninglabs/lnc-web": ^0.2.3-alpha "@noble/curves": ^1.0.0 "@noble/hashes": ^1.2.0 - "@reduxjs/toolkit": ^1.9.1 "@scure/base": ^1.1.1 "@scure/bip32": ^1.3.0 "@scure/bip39": ^1.1.1 "@snort/shared": "workspace:*" "@snort/system": "workspace:*" "@snort/system-react": "workspace:*" + "@snort/system-wasm": "workspace:*" "@szhsin/react-menu": ^3.3.1 "@types/debug": ^4.1.8 "@types/jest": ^29.5.1 "@types/node": ^20.4.1 "@types/react": ^18.0.26 "@types/react-dom": ^18.0.10 + "@types/use-sync-external-store": ^0.0.4 "@types/uuid": ^9.0.2 "@types/webscopeio__react-textarea-autocomplete": ^4.7.2 "@types/webtorrent": ^0.109.3 @@ -2728,6 +2709,7 @@ __metadata: "@webpack-cli/generators": ^3.0.4 "@webscopeio/react-textarea-autocomplete": ^4.9.2 babel-loader: ^9.1.3 + config: ^3.3.9 copy-webpack-plugin: ^11.0.0 css-loader: ^6.7.3 css-minimizer-webpack-plugin: ^5.0.0 @@ -2750,16 +2732,17 @@ __metadata: react-dom: ^18.2.0 react-intersection-observer: ^9.4.1 react-intl: ^6.4.4 - react-redux: ^8.0.5 react-router-dom: ^6.5.0 react-textarea-autosize: ^8.4.0 react-twitter-embed: ^4.0.4 source-map-loader: ^4.0.1 terser-webpack-plugin: ^5.3.9 + tinybench: ^2.5.0 ts-jest: ^29.1.0 ts-loader: ^9.4.4 typescript: ^5.2.2 use-long-press: ^2.0.3 + use-sync-external-store: ^1.2.0 uuid: ^9.0.0 webpack: ^5.88.2 webpack-bundle-analyzer: ^4.8.0 @@ -2773,41 +2756,7 @@ __metadata: languageName: unknown linkType: soft -"@snort/nostr@workspace:packages/nostr": - version: 0.0.0-use.local - resolution: "@snort/nostr@workspace:packages/nostr" - dependencies: - "@noble/curves": ^1.0.0 - "@noble/hashes": ^1.2.0 - "@types/chai": ^4.3.4 - "@types/events": ^3.0.0 - "@types/expect": ^24.3.0 - "@types/express": ^4.17.17 - "@types/mocha": ^10.0.1 - "@types/node": ^20.4.1 - "@types/uuid": ^9.0.1 - "@typescript-eslint/eslint-plugin": ^5.53.0 - "@typescript-eslint/parser": ^5.53.0 - base64-js: ^1.5.1 - bech32: ^2.0.0 - chai: ^4.3.7 - eslint: ^8.34.0 - events: ^3.3.0 - express: ^4.18.2 - isomorphic-ws: ^5.0.0 - mocha: ^10.2.0 - ts-loader: ^9.4.2 - ts-mocha: ^10.0.0 - ts-node: ^10.9.1 - typescript: ^4.9.5 - uuid: ^9.0.0 - webpack: ^5.77.0 - webpack-cli: ^5.0.1 - ws: ^8.12.1 - languageName: unknown - linkType: soft - -"@snort/shared@^1.0.4, @snort/shared@workspace:*, @snort/shared@workspace:packages/shared": +"@snort/shared@^1.0.6, @snort/shared@workspace:*, @snort/shared@workspace:packages/shared": version: 0.0.0-use.local resolution: "@snort/shared@workspace:packages/shared" dependencies: @@ -2826,15 +2775,32 @@ __metadata: version: 0.0.0-use.local resolution: "@snort/system-react@workspace:packages/system-react" dependencies: - "@snort/shared": ^1.0.4 - "@snort/system": ^1.0.17 + "@snort/shared": ^1.0.6 + "@snort/system": ^1.0.21 "@types/react": ^18.2.14 react: ^18.2.0 typescript: ^5.2.2 languageName: unknown linkType: soft -"@snort/system@^1.0.17, @snort/system@workspace:*, @snort/system@workspace:packages/system": +"@snort/system-svelte@workspace:packages/system-svelte": + version: 0.0.0-use.local + resolution: "@snort/system-svelte@workspace:packages/system-svelte" + dependencies: + "@snort/shared": ^1.0.6 + "@snort/system": ^1.0.21 + svelte: ^4.2.0 + typescript: ^5.2.2 + languageName: unknown + linkType: soft + +"@snort/system-wasm@workspace:*, @snort/system-wasm@workspace:packages/system-wasm": + version: 0.0.0-use.local + resolution: "@snort/system-wasm@workspace:packages/system-wasm" + languageName: unknown + linkType: soft + +"@snort/system@^1.0.21, @snort/system@workspace:*, @snort/system@workspace:packages/system": version: 0.0.0-use.local resolution: "@snort/system@workspace:packages/system" dependencies: @@ -2843,18 +2809,23 @@ __metadata: "@noble/hashes": ^1.3.2 "@peculiar/webcrypto": ^1.4.3 "@scure/base": ^1.1.2 - "@snort/shared": ^1.0.4 + "@snort/shared": ^1.0.6 "@stablelib/xchacha20": ^1.0.1 "@types/debug": ^4.1.8 "@types/jest": ^29.5.1 + "@types/node": ^20.5.9 "@types/uuid": ^9.0.2 + "@types/ws": ^8.5.5 debug: ^4.3.4 dexie: ^3.2.4 + isomorphic-ws: ^5.0.0 jest: ^29.5.0 jest-environment-jsdom: ^29.5.0 ts-jest: ^29.1.0 + ts-node: ^10.9.1 typescript: ^5.2.2 uuid: ^9.0.0 + ws: ^8.14.0 languageName: unknown linkType: soft @@ -3173,13 +3144,6 @@ __metadata: languageName: node linkType: hard -"@types/chai@npm:^4.3.4": - version: 4.3.5 - resolution: "@types/chai@npm:4.3.5" - checksum: c8f26a88c6b5b53a3275c7f5ff8f107028e3cbb9ff26795fff5f3d9dea07106a54ce9e2dce5e40347f7c4cc35657900aaf0c83934a25a1ae12e61e0f5516e431 - languageName: node - linkType: hard - "@types/connect-history-api-fallback@npm:^1.3.5": version: 1.5.0 resolution: "@types/connect-history-api-fallback@npm:1.5.0" @@ -3228,7 +3192,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^1.0.0": +"@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.1": version: 1.0.1 resolution: "@types/estree@npm:1.0.1" checksum: e9aa175eacb797216fafce4d41e8202c7a75555bc55232dee0f9903d7171f8f19f0ae7d5191bb1a88cb90e65468be508c0df850a9fb81b4433b293a5a749899d @@ -3242,13 +3206,6 @@ __metadata: languageName: node linkType: hard -"@types/events@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/events@npm:3.0.0" - checksum: 9a424c2da210957d5636e0763e8c9fc3aaeee35bf411284ddec62a56a6abe31de9c7c2e713dabdd8a76ff98b47db2bd52f61310be6609641d6234cc842ecbbe3 - languageName: node - linkType: hard - "@types/expect@npm:^1.20.4": version: 1.20.4 resolution: "@types/expect@npm:1.20.4" @@ -3256,15 +3213,6 @@ __metadata: languageName: node linkType: hard -"@types/expect@npm:^24.3.0": - version: 24.3.0 - resolution: "@types/expect@npm:24.3.0" - dependencies: - expect: "*" - checksum: 8d017b49b1b11fcf1c9bd2807bf7e1d49e34a4cdd6c40a40fbeb21a9d5873eb0d159043c647dce9ebabb589fef7833796ae17fbf7736233a32769797a3db4db5 - languageName: node - linkType: hard - "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.33": version: 4.17.36 resolution: "@types/express-serve-static-core@npm:4.17.36" @@ -3277,7 +3225,7 @@ __metadata: languageName: node linkType: hard -"@types/express@npm:*, @types/express@npm:^4.17.13, @types/express@npm:^4.17.17": +"@types/express@npm:*, @types/express@npm:^4.17.13": version: 4.17.17 resolution: "@types/express@npm:4.17.17" dependencies: @@ -3391,13 +3339,6 @@ __metadata: languageName: node linkType: hard -"@types/json5@npm:^0.0.29": - version: 0.0.29 - resolution: "@types/json5@npm:0.0.29" - checksum: e60b153664572116dfea673c5bda7778dbff150498f44f998e34b5886d8afc47f16799280e4b6e241c0472aef1bc36add771c569c68fc5125fc2ae519a3eb9ac - languageName: node - linkType: hard - "@types/magnet-uri@npm:*": version: 5.1.3 resolution: "@types/magnet-uri@npm:5.1.3" @@ -3428,13 +3369,6 @@ __metadata: languageName: node linkType: hard -"@types/mocha@npm:^10.0.1": - version: 10.0.1 - resolution: "@types/mocha@npm:10.0.1" - checksum: 224ea9fce7b1734ccdb9aa99a622d902a538ce1847bca7fd22c5fb38adcf3ed536f50f48f587085db988a4bb3c2eb68f4b98e1cd6a38bc5547bd3bbbedc54495 - languageName: node - linkType: hard - "@types/ms@npm:*": version: 0.7.31 resolution: "@types/ms@npm:0.7.31" @@ -3463,6 +3397,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^20.5.9": + version: 20.5.9 + resolution: "@types/node@npm:20.5.9" + checksum: 717490e94131722144878b4ca1a963ede1673bb8f2ef78c2f5b50b918df6dc9b35e7f8283e5c2a7a9f137730f7c08dc6228e53d4494a94c9ee16881e6ce6caed + languageName: node + linkType: hard + "@types/normalize-package-data@npm:^2.4.0": version: 2.4.1 resolution: "@types/normalize-package-data@npm:2.4.1" @@ -3554,7 +3495,7 @@ __metadata: languageName: node linkType: hard -"@types/semver@npm:^7.3.12, @types/semver@npm:^7.5.0": +"@types/semver@npm:^7.5.0": version: 7.5.0 resolution: "@types/semver@npm:7.5.0" checksum: 0a64b9b9c7424d9a467658b18dd70d1d781c2d6f033096a6e05762d20ebbad23c1b69b0083b0484722aabf35640b78ccc3de26368bcae1129c87e9df028a22e2 @@ -3630,14 +3571,14 @@ __metadata: languageName: node linkType: hard -"@types/use-sync-external-store@npm:^0.0.3": - version: 0.0.3 - resolution: "@types/use-sync-external-store@npm:0.0.3" - checksum: 161ddb8eec5dbe7279ac971531217e9af6b99f7783213566d2b502e2e2378ea19cf5e5ea4595039d730aa79d3d35c6567d48599f69773a02ffcff1776ec2a44e +"@types/use-sync-external-store@npm:^0.0.4": + version: 0.0.4 + resolution: "@types/use-sync-external-store@npm:0.0.4" + checksum: f8bf56b14f28fda6d0215281d50623d5affd17c549ba46bcfcfbb97c6301a583066c0477856260ae6feadcaa714c46cd45678e76b74da0f6f8b364aec07bd854 languageName: node linkType: hard -"@types/uuid@npm:^9.0.1, @types/uuid@npm:^9.0.2": +"@types/uuid@npm:^9.0.2": version: 9.0.2 resolution: "@types/uuid@npm:9.0.2" checksum: 1754bcf3444e1e3aeadd6e774fc328eb53bc956665e2e8fb6ec127aa8e1f43d9a224c3d22a9a6233dca8dd81a12dc7fed4d84b8876dd5ec82d40f574f7ff8b68 @@ -3700,30 +3641,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^5.53.0": - version: 5.62.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" - dependencies: - "@eslint-community/regexpp": ^4.4.0 - "@typescript-eslint/scope-manager": 5.62.0 - "@typescript-eslint/type-utils": 5.62.0 - "@typescript-eslint/utils": 5.62.0 - debug: ^4.3.4 - graphemer: ^1.4.0 - ignore: ^5.2.0 - natural-compare-lite: ^1.4.0 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependencies: - "@typescript-eslint/parser": ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: fc104b389c768f9fa7d45a48c86d5c1ad522c1d0512943e782a56b1e3096b2cbcc1eea3fcc590647bf0658eef61aac35120a9c6daf979bf629ad2956deb516a1 - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:^6.1.0": version: 6.4.1 resolution: "@typescript-eslint/eslint-plugin@npm:6.4.1" @@ -3749,23 +3666,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/parser@npm:^5.53.0": - version: 5.62.0 - resolution: "@typescript-eslint/parser@npm:5.62.0" - dependencies: - "@typescript-eslint/scope-manager": 5.62.0 - "@typescript-eslint/types": 5.62.0 - "@typescript-eslint/typescript-estree": 5.62.0 - debug: ^4.3.4 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: d168f4c7f21a7a63f47002e2d319bcbb6173597af5c60c1cf2de046b46c76b4930a093619e69faf2d30214c29ab27b54dcf1efc7046a6a6bd6f37f59a990e752 - languageName: node - linkType: hard - "@typescript-eslint/parser@npm:^6.1.0": version: 6.4.1 resolution: "@typescript-eslint/parser@npm:6.4.1" @@ -3784,16 +3684,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/scope-manager@npm:5.62.0" - dependencies: - "@typescript-eslint/types": 5.62.0 - "@typescript-eslint/visitor-keys": 5.62.0 - checksum: 6062d6b797fe1ce4d275bb0d17204c827494af59b5eaf09d8a78cdd39dadddb31074dded4297aaf5d0f839016d601032857698b0e4516c86a41207de606e9573 - languageName: node - linkType: hard - "@typescript-eslint/scope-manager@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/scope-manager@npm:6.4.1" @@ -3804,23 +3694,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/type-utils@npm:5.62.0" - dependencies: - "@typescript-eslint/typescript-estree": 5.62.0 - "@typescript-eslint/utils": 5.62.0 - debug: ^4.3.4 - tsutils: ^3.21.0 - peerDependencies: - eslint: "*" - peerDependenciesMeta: - typescript: - optional: true - checksum: fc41eece5f315dfda14320be0da78d3a971d650ea41300be7196934b9715f3fe1120a80207551eb71d39568275dbbcf359bde540d1ca1439d8be15e9885d2739 - languageName: node - linkType: hard - "@typescript-eslint/type-utils@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/type-utils@npm:6.4.1" @@ -3838,13 +3711,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/types@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/types@npm:5.62.0" - checksum: 48c87117383d1864766486f24de34086155532b070f6264e09d0e6139449270f8a9559cfef3c56d16e3bcfb52d83d42105d61b36743626399c7c2b5e0ac3b670 - languageName: node - linkType: hard - "@typescript-eslint/types@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/types@npm:6.4.1" @@ -3852,24 +3718,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" - dependencies: - "@typescript-eslint/types": 5.62.0 - "@typescript-eslint/visitor-keys": 5.62.0 - debug: ^4.3.4 - globby: ^11.1.0 - is-glob: ^4.0.3 - semver: ^7.3.7 - tsutils: ^3.21.0 - peerDependenciesMeta: - typescript: - optional: true - checksum: 3624520abb5807ed8f57b1197e61c7b1ed770c56dfcaca66372d584ff50175225798bccb701f7ef129d62c5989070e1ee3a0aa2d84e56d9524dcf011a2bb1a52 - languageName: node - linkType: hard - "@typescript-eslint/typescript-estree@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/typescript-estree@npm:6.4.1" @@ -3888,24 +3736,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/utils@npm:5.62.0" - dependencies: - "@eslint-community/eslint-utils": ^4.2.0 - "@types/json-schema": ^7.0.9 - "@types/semver": ^7.3.12 - "@typescript-eslint/scope-manager": 5.62.0 - "@typescript-eslint/types": 5.62.0 - "@typescript-eslint/typescript-estree": 5.62.0 - eslint-scope: ^5.1.1 - semver: ^7.3.7 - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: ee9398c8c5db6d1da09463ca7bf36ed134361e20131ea354b2da16a5fdb6df9ba70c62a388d19f6eebb421af1786dbbd79ba95ddd6ab287324fc171c3e28d931 - languageName: node - linkType: hard - "@typescript-eslint/utils@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/utils@npm:6.4.1" @@ -3923,16 +3753,6 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.62.0": - version: 5.62.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" - dependencies: - "@typescript-eslint/types": 5.62.0 - eslint-visitor-keys: ^3.3.0 - checksum: 976b05d103fe8335bef5c93ad3f76d781e3ce50329c0243ee0f00c0fcfb186c81df50e64bfdd34970148113f8ade90887f53e3c4938183afba830b4ba8e30a35 - languageName: node - linkType: hard - "@typescript-eslint/visitor-keys@npm:6.4.1": version: 6.4.1 resolution: "@typescript-eslint/visitor-keys@npm:6.4.1" @@ -4256,7 +4076,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.1.0, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": +"acorn@npm:^8.0.4, acorn@npm:^8.1.0, acorn@npm:^8.10.0, acorn@npm:^8.4.1, acorn@npm:^8.7.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.10.0 resolution: "acorn@npm:8.10.0" bin: @@ -4351,13 +4171,6 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:4.1.1": - version: 4.1.1 - resolution: "ansi-colors@npm:4.1.1" - checksum: 138d04a51076cb085da0a7e2d000c5c0bb09f6e772ed5c65c53cb118d37f6c5f1637506d7155fb5f330f0abcf6f12fa2e489ac3f8cdab9da393bf1bb4f9a32b0 - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -4482,6 +4295,15 @@ __metadata: languageName: node linkType: hard +"aria-query@npm:^5.3.0": + version: 5.3.0 + resolution: "aria-query@npm:5.3.0" + dependencies: + dequal: ^2.0.3 + checksum: 305bd73c76756117b59aba121d08f413c7ff5e80fa1b98e217a3443fcddb9a232ee790e24e432b59ae7625aebcf4c47cb01c2cac872994f0b426f5bdfcd96ba9 + languageName: node + linkType: hard + "array-buffer-byte-length@npm:^1.0.0": version: 1.0.0 resolution: "array-buffer-byte-length@npm:1.0.0" @@ -4534,13 +4356,6 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^1.0.0": - version: 1.0.1 - resolution: "arrify@npm:1.0.1" - checksum: 745075dd4a4624ff0225c331dacb99be501a515d39bcb7c84d24660314a6ec28e68131b137e6f7e16318170842ce97538cd298fc4cd6b2cc798e0b957f2747e7 - languageName: node - linkType: hard - "arrify@npm:^2.0.1": version: 2.0.1 resolution: "arrify@npm:2.0.1" @@ -4566,13 +4381,6 @@ __metadata: languageName: node linkType: hard -"assertion-error@npm:^1.1.0": - version: 1.1.0 - resolution: "assertion-error@npm:1.1.0" - checksum: fd9429d3a3d4fd61782eb3962ae76b6d08aa7383123fca0596020013b3ebd6647891a85b05ce821c47d1471ed1271f00b0545cf6a4326cf2fc91efcc3b0fbecf - languageName: node - linkType: hard - "async@npm:^3.2.3": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -4612,6 +4420,15 @@ __metadata: languageName: node linkType: hard +"axobject-query@npm:^3.2.1": + version: 3.2.1 + resolution: "axobject-query@npm:3.2.1" + dependencies: + dequal: ^2.0.3 + checksum: a94047e702b57c91680e6a952ec4a1aaa2cfd0d80ead76bc8c954202980d8c51968a6ea18b4d8010e8e2cf95676533d8022a8ebba9abc1dfe25686721df26fd2 + languageName: node + linkType: hard + "babel-jest@npm:^29.6.4": version: 29.6.4 resolution: "babel-jest@npm:29.6.4" @@ -4744,7 +4561,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -4765,13 +4582,6 @@ __metadata: languageName: node linkType: hard -"bech32@npm:^2.0.0": - version: 2.0.0 - resolution: "bech32@npm:2.0.0" - checksum: fa15acb270b59aa496734a01f9155677b478987b773bf701f465858bf1606c6a970085babd43d71ce61895f1baa594cb41a2cd1394bd2c6698f03cc2d811300e - languageName: node - linkType: hard - "before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" @@ -4892,13 +4702,6 @@ __metadata: languageName: node linkType: hard -"browser-stdout@npm:1.3.1": - version: 1.3.1 - resolution: "browser-stdout@npm:1.3.1" - checksum: b717b19b25952dd6af483e368f9bcd6b14b87740c3d226c2977a65e84666ffd67000bddea7d911f111a9b6ddc822b234de42d52ab6507bce4119a4cc003ef7b3 - languageName: node - linkType: hard - "browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9": version: 4.21.10 resolution: "browserslist@npm:4.21.10" @@ -4931,7 +4734,7 @@ __metadata: languageName: node linkType: hard -"buffer-from@npm:^1.0.0, buffer-from@npm:^1.1.0": +"buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" checksum: 0448524a562b37d4d7ed9efd91685a5b77a50672c556ea254ac9a6d30e3403a517d8981f10e565db24e8339413b43c97ca2951f10e399c6125a0d8911f5679bb @@ -5110,7 +4913,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0": +"camelcase@npm:^6.2.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -5136,21 +4939,6 @@ __metadata: languageName: node linkType: hard -"chai@npm:^4.3.7": - version: 4.3.8 - resolution: "chai@npm:4.3.8" - dependencies: - assertion-error: ^1.1.0 - check-error: ^1.0.2 - deep-eql: ^4.1.2 - get-func-name: ^2.0.0 - loupe: ^2.3.1 - pathval: ^1.1.1 - type-detect: ^4.0.5 - checksum: 29e0984ed13308319cadc35437c8ef0a3e271544d226c991bf7e3b6d771bf89707321669e11d05e362bc0ad0bd26585079b989d1032f3c106e3bb95d7f079cce - languageName: node - linkType: hard - "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -5186,14 +4974,7 @@ __metadata: languageName: node linkType: hard -"check-error@npm:^1.0.2": - version: 1.0.2 - resolution: "check-error@npm:1.0.2" - checksum: d9d106504404b8addd1ee3f63f8c0eaa7cd962a1a28eb9c519b1c4a1dc7098be38007fc0060f045ee00f075fbb7a2a4f42abcf61d68323677e11ab98dc16042e - languageName: node - linkType: hard - -"chokidar@npm:3.5.3, chokidar@npm:^3.5.3": +"chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -5288,17 +5069,6 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^7.0.2": - version: 7.0.4 - resolution: "cliui@npm:7.0.4" - dependencies: - string-width: ^4.2.0 - strip-ansi: ^6.0.0 - wrap-ansi: ^7.0.0 - checksum: ce2e8f578a4813806788ac399b9e866297740eecd4ad1823c27fd344d78b22c5f8597d548adbcc46f0573e43e21e751f39446c5a5e804a12aace402b7a315d7f - languageName: node - linkType: hard - "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -5376,6 +5146,19 @@ __metadata: languageName: node linkType: hard +"code-red@npm:^1.0.3": + version: 1.0.4 + resolution: "code-red@npm:1.0.4" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 + "@types/estree": ^1.0.1 + acorn: ^8.10.0 + estree-walker: ^3.0.3 + periscopic: ^3.1.0 + checksum: ca534d9daf0fb50f8180dd5cb2de9f670264584d1132e00743967c626888a253639023ae147687201b50d25e2fa31c2d100908d7a9286721c47e05c0079b64bd + languageName: node + linkType: hard + "collect-v8-coverage@npm:^1.0.0": version: 1.0.2 resolution: "collect-v8-coverage@npm:1.0.2" @@ -5548,6 +5331,15 @@ __metadata: languageName: node linkType: hard +"config@npm:^3.3.9": + version: 3.3.9 + resolution: "config@npm:3.3.9" + dependencies: + json5: ^2.2.3 + checksum: 2c29e40be22274462769670a4b69fcbcad2d3049eb15030073e410d32c892ef29e0c879a3d68ef92ddd572c516e4f65a11bb6458f680a44ceb0f051bcd3d97ff + languageName: node + linkType: hard + "connect-history-api-fallback@npm:^2.0.0": version: 2.0.0 resolution: "connect-history-api-fallback@npm:2.0.0" @@ -5752,7 +5544,7 @@ __metadata: languageName: node linkType: hard -"css-tree@npm:^2.2.1": +"css-tree@npm:^2.2.1, css-tree@npm:^2.3.1": version: 2.3.1 resolution: "css-tree@npm:2.3.1" dependencies: @@ -5928,7 +5720,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5947,13 +5739,6 @@ __metadata: languageName: node linkType: hard -"decamelize@npm:^4.0.0": - version: 4.0.0 - resolution: "decamelize@npm:4.0.0" - checksum: b7d09b82652c39eead4d6678bb578e3bebd848add894b76d0f6b395bc45b2d692fb88d977e7cfb93c4ed6c119b05a1347cef261174916c2e75c0a8ca57da1809 - languageName: node - linkType: hard - "decimal.js@npm:^10.4.2": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -5973,15 +5758,6 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^4.1.2": - version: 4.1.3 - resolution: "deep-eql@npm:4.1.3" - dependencies: - type-detect: ^4.0.0 - checksum: 7f6d30cb41c713973dc07eaadded848b2ab0b835e518a88b91bea72f34e08c4c71d167a722a6f302d3a6108f05afd8e6d7650689a84d5d29ec7fe6220420397f - languageName: node - linkType: hard - "deep-extend@npm:^0.6.0": version: 0.6.0 resolution: "deep-extend@npm:0.6.0" @@ -6073,6 +5849,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 8679b850e1a3d0ebbc46ee780d5df7b478c23f335887464023a631d1b9af051ad4a6595a44220f9ff8ff95a8ddccf019b5ad778a976fd7bbf77383d36f412f90 + languageName: node + linkType: hard + "destroy@npm:1.2.0": version: 1.2.0 resolution: "destroy@npm:1.2.0" @@ -6118,20 +5901,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:5.0.0": - version: 5.0.0 - resolution: "diff@npm:5.0.0" - checksum: f19fe29284b633afdb2725c2a8bb7d25761ea54d321d8e67987ac851c5294be4afeab532bd84531e02583a3fe7f4014aa314a3eda84f5590e7a9e6b371ef3b46 - languageName: node - linkType: hard - -"diff@npm:^3.1.0": - version: 3.5.0 - resolution: "diff@npm:3.5.0" - checksum: 00842950a6551e26ce495bdbce11047e31667deea546527902661f25cc2e73358967ebc78cf86b1a9736ec3e14286433225f9970678155753a6291c3bca5227b - languageName: node - linkType: hard - "diff@npm:^4.0.1": version: 4.0.2 resolution: "diff@npm:4.0.2" @@ -6525,13 +6294,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:4.0.0, escape-string-regexp@npm:^4.0.0": - version: 4.0.0 - resolution: "escape-string-regexp@npm:4.0.0" - checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 - languageName: node - linkType: hard - "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -6546,6 +6308,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 98b48897d93060f2322108bf29db0feba7dd774be96cd069458d1453347b25ce8682ecc39859d4bca2203cc0ab19c237bcc71755eff49a0f8d90beadeeba5cc5 + languageName: node + linkType: hard + "escodegen@npm:^2.0.0": version: 2.1.0 resolution: "escodegen@npm:2.1.0" @@ -6564,7 +6333,7 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:5.1.1, eslint-scope@npm:^5.1.1": +"eslint-scope@npm:5.1.1": version: 5.1.1 resolution: "eslint-scope@npm:5.1.1" dependencies: @@ -6607,7 +6376,7 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.34.0, eslint@npm:^8.48.0": +"eslint@npm:^8.48.0": version: 8.48.0 resolution: "eslint@npm:8.48.0" dependencies: @@ -6714,6 +6483,15 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^3.0.0, estree-walker@npm:^3.0.3": + version: 3.0.3 + resolution: "estree-walker@npm:3.0.3" + dependencies: + "@types/estree": ^1.0.0 + checksum: a65728d5727b71de172c5df323385755a16c0fdab8234dc756c3854cfee343261ddfbb72a809a5660fac8c75d960bb3e21aa898c2d7e9b19bb298482ca58a3af + languageName: node + linkType: hard + "esutils@npm:^2.0.2": version: 2.0.3 resolution: "esutils@npm:2.0.3" @@ -6773,7 +6551,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:*, expect@npm:^29.0.0, expect@npm:^29.6.4": +"expect@npm:^29.0.0, expect@npm:^29.6.4": version: 29.6.4 resolution: "expect@npm:29.6.4" dependencies: @@ -6793,7 +6571,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.3, express@npm:^4.18.2": +"express@npm:^4.17.3": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -6972,16 +6750,6 @@ __metadata: languageName: node linkType: hard -"find-up@npm:5.0.0, find-up@npm:^5.0.0": - version: 5.0.0 - resolution: "find-up@npm:5.0.0" - dependencies: - locate-path: ^6.0.0 - path-exists: ^4.0.0 - checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 - languageName: node - linkType: hard - "find-up@npm:^4.0.0, find-up@npm:^4.1.0": version: 4.1.0 resolution: "find-up@npm:4.1.0" @@ -6992,6 +6760,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: ^6.0.0 + path-exists: ^4.0.0 + checksum: 07955e357348f34660bde7920783204ff5a26ac2cafcaa28bace494027158a97b9f56faaf2d89a6106211a8174db650dd9f503f9c0d526b1202d5554a00b9095 + languageName: node + linkType: hard + "find-up@npm:^6.3.0": version: 6.3.0 resolution: "find-up@npm:6.3.0" @@ -7032,15 +6810,6 @@ __metadata: languageName: node linkType: hard -"flat@npm:^5.0.2": - version: 5.0.2 - resolution: "flat@npm:5.0.2" - bin: - flat: cli.js - checksum: 12a1536ac746db74881316a181499a78ef953632ddd28050b7a3a43c62ef5462e3357c8c29d76072bb635f147f7a9a1f0c02efef6b4be28f8db62ceb3d5c7f5d - languageName: node - linkType: hard - "flatted@npm:^3.2.7": version: 3.2.7 resolution: "flatted@npm:3.2.7" @@ -7238,13 +7007,6 @@ __metadata: languageName: node linkType: hard -"get-func-name@npm:^2.0.0": - version: 2.0.0 - resolution: "get-func-name@npm:2.0.0" - checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3 - languageName: node - linkType: hard - "get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": version: 1.2.1 resolution: "get-intrinsic@npm:1.2.1" @@ -7322,20 +7084,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.2.0": - version: 7.2.0 - resolution: "glob@npm:7.2.0" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 78a8ea942331f08ed2e055cb5b9e40fe6f46f579d7fd3d694f3412fe5db23223d29b7fee1575440202e9a7ff9a72ab106a39fee39934c7bedafe5e5f8ae20134 - languageName: node - linkType: hard - "glob@npm:^10.2.2": version: 10.3.3 resolution: "glob@npm:10.3.3" @@ -7545,7 +7293,7 @@ __metadata: languageName: node linkType: hard -"he@npm:1.2.0, he@npm:^1.2.0": +"he@npm:^1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" bin: @@ -7856,13 +7604,6 @@ __metadata: languageName: node linkType: hard -"immer@npm:^9.0.21": - version: 9.0.21 - resolution: "immer@npm:9.0.21" - checksum: 70e3c274165995352f6936695f0ef4723c52c92c92dd0e9afdfe008175af39fa28e76aafb3a2ca9d57d1fb8f796efc4dd1e1cc36f18d33fa5b74f3dfb0375432 - languageName: node - linkType: hard - "import-fresh@npm:^3.2.1": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -8179,7 +7920,7 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^2.0.0, is-plain-obj@npm:^2.1.0": +"is-plain-obj@npm:^2.0.0": version: 2.1.0 resolution: "is-plain-obj@npm:2.1.0" checksum: cec9100678b0a9fe0248a81743041ed990c2d4c99f893d935545cfbc42876cbe86d207f3b895700c690ad2fa520e568c44afc1605044b535a7820c1d40e38daa @@ -8216,6 +7957,15 @@ __metadata: languageName: node linkType: hard +"is-reference@npm:^3.0.0, is-reference@npm:^3.0.1": + version: 3.0.2 + resolution: "is-reference@npm:3.0.2" + dependencies: + "@types/estree": "*" + checksum: ac3bf5626fe9d0afbd7454760d73c47f16b9f471401b9749721ad3b66f0a39644390382acf88ca9d029c95782c1e2ec65662855e3ba91acf52d82231247a7fd3 + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -8950,17 +8700,6 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" - dependencies: - argparse: ^2.0.1 - bin: - js-yaml: bin/js-yaml.js - checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a - languageName: node - linkType: hard - "js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" @@ -8973,6 +8712,17 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a + languageName: node + linkType: hard + "jsdom@npm:^20.0.0": version: 20.0.3 resolution: "jsdom@npm:20.0.3" @@ -9095,17 +8845,6 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.2": - version: 1.0.2 - resolution: "json5@npm:1.0.2" - dependencies: - minimist: ^1.2.0 - bin: - json5: lib/cli.js - checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 - languageName: node - linkType: hard - "json5@npm:^2.2.0, json5@npm:^2.2.3": version: 2.2.3 resolution: "json5@npm:2.2.3" @@ -9266,6 +9005,13 @@ __metadata: languageName: node linkType: hard +"locate-character@npm:^3.0.0": + version: 3.0.0 + resolution: "locate-character@npm:3.0.0" + checksum: 2d9e9f45e2dce7464c016ed6d81ebc938bc9c656392f7d6858308ab6fdaa57bcd4b6b479291d49e7db4047e3f321ddadbe78355f349b7974b203f19674e277cc + languageName: node + linkType: hard + "locate-path@npm:^5.0.0": version: 5.0.0 resolution: "locate-path@npm:5.0.0" @@ -9335,7 +9081,7 @@ __metadata: languageName: node linkType: hard -"log-symbols@npm:4.1.0, log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": +"log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" dependencies: @@ -9356,15 +9102,6 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^2.3.1": - version: 2.3.6 - resolution: "loupe@npm:2.3.6" - dependencies: - get-func-name: ^2.0.0 - checksum: cc83f1b124a1df7384601d72d8d1f5fe95fd7a8185469fec48bb2e4027e45243949e7a013e8d91051a138451ff0552310c32aa9786e60b6a30d1e801bdc2163f - languageName: node - linkType: hard - "lower-case@npm:^2.0.2": version: 2.0.2 resolution: "lower-case@npm:2.0.2" @@ -9415,6 +9152,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.30.0": + version: 0.30.3 + resolution: "magic-string@npm:0.30.3" + dependencies: + "@jridgewell/sourcemap-codec": ^1.4.15 + checksum: a5a9ddf9bd3bf49a2de1048bf358464f1bda7b3cc1311550f4a0ba8f81a4070e25445d53a5ee28850161336f1bff3cf28aa3320c6b4aeff45ce3e689f300b2f3 + languageName: node + linkType: hard + "make-dir@npm:^4.0.0": version: 4.0.0 resolution: "make-dir@npm:4.0.0" @@ -9674,15 +9420,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:5.0.1": - version: 5.0.1 - resolution: "minimatch@npm:5.0.1" - dependencies: - brace-expansion: ^2.0.1 - checksum: b34b98463da4754bc526b244d680c69d4d6089451ebe512edaf6dd9eeed0279399cfa3edb19233513b8f830bf4bfcad911dddcdf125e75074100d52f724774f0 - languageName: node - linkType: hard - "minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -9719,7 +9456,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": +"minimist@npm:^1.2.5": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -9861,17 +9598,6 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.1": - version: 0.5.6 - resolution: "mkdirp@npm:0.5.6" - dependencies: - minimist: ^1.2.6 - bin: - mkdirp: bin/cmd.js - checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 - languageName: node - linkType: hard - "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -9881,38 +9607,6 @@ __metadata: languageName: node linkType: hard -"mocha@npm:^10.2.0": - version: 10.2.0 - resolution: "mocha@npm:10.2.0" - dependencies: - ansi-colors: 4.1.1 - browser-stdout: 1.3.1 - chokidar: 3.5.3 - debug: 4.3.4 - diff: 5.0.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 7.2.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.0.1 - ms: 2.1.3 - nanoid: 3.3.3 - serialize-javascript: 6.0.0 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.2.1 - yargs: 16.2.0 - yargs-parser: 20.2.4 - yargs-unparser: 2.0.0 - bin: - _mocha: bin/_mocha - mocha: bin/mocha.js - checksum: 406c45eab122ffd6ea2003c2f108b2bc35ba036225eee78e0c784b6fa2c7f34e2b13f1dbacef55a4fdf523255d76e4f22d1b5aacda2394bd11666febec17c719 - languageName: node - linkType: hard - "mrmime@npm:^1.0.0": version: 1.0.1 resolution: "mrmime@npm:1.0.1" @@ -9973,15 +9667,6 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:3.3.3": - version: 3.3.3 - resolution: "nanoid@npm:3.3.3" - bin: - nanoid: bin/nanoid.cjs - checksum: ada019402a07464a694553c61d2dca8a4353645a7d92f2830f0d487fedff403678a0bee5323a46522752b2eab95a0bc3da98b6cccaa7c0c55cd9975130e6d6f0 - languageName: node - linkType: hard - "nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" @@ -10000,13 +9685,6 @@ __metadata: languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -10804,10 +10482,14 @@ __metadata: languageName: node linkType: hard -"pathval@npm:^1.1.1": - version: 1.1.1 - resolution: "pathval@npm:1.1.1" - checksum: 090e3147716647fb7fb5b4b8c8e5b55e5d0a6086d085b6cd23f3d3c01fcf0ff56fd3cc22f2f4a033bd2e46ed55d61ed8379e123b42afe7d531a2a5fc8bb556d6 +"periscopic@npm:^3.1.0": + version: 3.1.0 + resolution: "periscopic@npm:3.1.0" + dependencies: + "@types/estree": ^1.0.0 + estree-walker: ^3.0.0 + is-reference: ^3.0.0 + checksum: 2153244352e58a0d76e7e8d9263e66fe74509495f809af388da20045fb30aa3e93f2f94468dc0b9166ecf206fcfc0d73d2c7641c6fbedc07b1de858b710142cb languageName: node linkType: hard @@ -11277,12 +10959,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^3.0.0": - version: 3.0.2 - resolution: "prettier@npm:3.0.2" +"prettier@npm:^3.0.3": + version: 3.0.3 + resolution: "prettier@npm:3.0.3" bin: prettier: bin/prettier.cjs - checksum: 118b59ddb6c80abe2315ab6d0f4dd1b253be5cfdb20622fa5b65bb1573dcd362e6dd3dcf2711dd3ebfe64aecf7bdc75de8a69dc2422dcd35bdde7610586b677a + checksum: e10b9af02b281f6c617362ebd2571b1d7fc9fb8a3bd17e371754428cda992e5e8d8b7a046e8f7d3e2da1dcd21aa001e2e3c797402ebb6111b5cd19609dd228e0 languageName: node linkType: hard @@ -11574,38 +11256,6 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:^8.0.5": - version: 8.1.2 - resolution: "react-redux@npm:8.1.2" - dependencies: - "@babel/runtime": ^7.12.1 - "@types/hoist-non-react-statics": ^3.3.1 - "@types/use-sync-external-store": ^0.0.3 - hoist-non-react-statics: ^3.3.2 - react-is: ^18.0.0 - use-sync-external-store: ^1.0.0 - peerDependencies: - "@types/react": ^16.8 || ^17.0 || ^18.0 - "@types/react-dom": ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: ">=0.59" - redux: ^4 || ^5.0.0-beta.0 - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - react-dom: - optional: true - react-native: - optional: true - redux: - optional: true - checksum: 4d5976b0f721e4148475871fcabce2fee875cc7f70f9a292f3370d63b38aa1dd474eb303c073c5555f3e69fc732f3bac05303def60304775deb28361e3f4b7cc - languageName: node - linkType: hard - "react-router-dom@npm:^6.5.0": version: 6.15.0 resolution: "react-router-dom@npm:6.15.0" @@ -11823,24 +11473,6 @@ __metadata: languageName: node linkType: hard -"redux-thunk@npm:^2.4.2": - version: 2.4.2 - resolution: "redux-thunk@npm:2.4.2" - peerDependencies: - redux: ^4 - checksum: c7f757f6c383b8ec26152c113e20087818d18ed3edf438aaad43539e9a6b77b427ade755c9595c4a163b6ad3063adf3497e5fe6a36c68884eb1f1cfb6f049a5c - languageName: node - linkType: hard - -"redux@npm:^4.2.1": - version: 4.2.1 - resolution: "redux@npm:4.2.1" - dependencies: - "@babel/runtime": ^7.9.2 - checksum: f63b9060c3a1d930ae775252bb6e579b42415aee7a23c4114e21a0b4ba7ec12f0ec76936c00f546893f06e139819f0e2855e0d55ebfce34ca9c026241a6950dd - languageName: node - linkType: hard - "regenerate-unicode-properties@npm:^10.1.0": version: 10.1.0 resolution: "regenerate-unicode-properties@npm:10.1.0" @@ -11971,13 +11603,6 @@ __metadata: languageName: node linkType: hard -"reselect@npm:^4.1.8": - version: 4.1.8 - resolution: "reselect@npm:4.1.8" - checksum: a4ac87cedab198769a29be92bc221c32da76cfdad6911eda67b4d3e7136dca86208c3b210e31632eae31ebd2cded18596f0dd230d3ccc9e978df22f233b5583e - languageName: node - linkType: hard - "resolve-cwd@npm:^3.0.0": version: 3.0.0 resolution: "resolve-cwd@npm:3.0.0" @@ -12111,7 +11736,7 @@ __metadata: "@cloudflare/workers-types": ^4.20230307.0 "@tauri-apps/cli": ^1.2.3 eslint: ^8.48.0 - prettier: ^3.0.0 + prettier: ^3.0.3 typescript: ^5.2.2 languageName: unknown linkType: soft @@ -12274,7 +11899,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4": +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.1.3, semver@npm:^7.2.1, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -12306,15 +11931,6 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:6.0.0": - version: 6.0.0 - resolution: "serialize-javascript@npm:6.0.0" - dependencies: - randombytes: ^2.1.0 - checksum: 56f90b562a1bdc92e55afb3e657c6397c01a902c588c0fe3d4c490efdcc97dcd2a3074ba12df9e94630f33a5ce5b76a74784a7041294628a6f4306e0ec84bf93 - languageName: node - linkType: hard - "serialize-javascript@npm:^4.0.0": version: 4.0.0 resolution: "serialize-javascript@npm:4.0.0" @@ -12601,7 +12217,7 @@ __metadata: languageName: node linkType: hard -"source-map-support@npm:^0.5.6, source-map-support@npm:~0.5.20": +"source-map-support@npm:~0.5.20": version: 0.5.21 resolution: "source-map-support@npm:0.5.21" dependencies: @@ -12943,7 +12559,7 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 @@ -12962,15 +12578,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:8.1.1, supports-color@npm:^8.0.0": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: ^4.0.0 - checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 - languageName: node - linkType: hard - "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -12989,6 +12596,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^8.0.0": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: ^4.0.0 + checksum: c052193a7e43c6cdc741eb7f378df605636e01ad434badf7324f17fb60c69a880d8d8fcdcb562cf94c2350e57b937d7425ab5b8326c67c2adc48f7c87c1db406 + languageName: node + linkType: hard + "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -12996,6 +12612,27 @@ __metadata: languageName: node linkType: hard +"svelte@npm:^4.2.0": + version: 4.2.0 + resolution: "svelte@npm:4.2.0" + dependencies: + "@ampproject/remapping": ^2.2.1 + "@jridgewell/sourcemap-codec": ^1.4.15 + "@jridgewell/trace-mapping": ^0.3.18 + acorn: ^8.9.0 + aria-query: ^5.3.0 + axobject-query: ^3.2.1 + code-red: ^1.0.3 + css-tree: ^2.3.1 + estree-walker: ^3.0.3 + is-reference: ^3.0.1 + locate-character: ^3.0.0 + magic-string: ^0.30.0 + periscopic: ^3.1.0 + checksum: 2555f222c210a751fff85596adc5a1f0e1ebe7e0499ec7c35b5e8111200271ba0cd0cedeef637dc66f43f4866ad17240f89187bfbaf2ca8fff9a522fc7c4369f + languageName: node + linkType: hard + "svgo@npm:^3.0.2": version: 3.0.2 resolution: "svgo@npm:3.0.2" @@ -13141,6 +12778,13 @@ __metadata: languageName: node linkType: hard +"tinybench@npm:^2.5.0": + version: 2.5.0 + resolution: "tinybench@npm:2.5.0" + checksum: 284bb9428f197ec8b869c543181315e65e41ccfdad3c4b6c916bb1fdae1b5c6785661b0d90cf135b48d833b03cb84dc5357b2d33ec65a1f5971fae0ab2023821 + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -13273,7 +12917,7 @@ __metadata: languageName: node linkType: hard -"ts-loader@npm:^9.4.2, ts-loader@npm:^9.4.4": +"ts-loader@npm:^9.4.4": version: 9.4.4 resolution: "ts-loader@npm:9.4.4" dependencies: @@ -13288,41 +12932,6 @@ __metadata: languageName: node linkType: hard -"ts-mocha@npm:^10.0.0": - version: 10.0.0 - resolution: "ts-mocha@npm:10.0.0" - dependencies: - ts-node: 7.0.1 - tsconfig-paths: ^3.5.0 - peerDependencies: - mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X - dependenciesMeta: - tsconfig-paths: - optional: true - bin: - ts-mocha: bin/ts-mocha - checksum: b11f2a8ceecf195b0db724da429159982fef12e4357088fe900289223587217e8c126ead7929679edd58bf19ad96c5da5911535d26f535386632e18fbff10c40 - languageName: node - linkType: hard - -"ts-node@npm:7.0.1": - version: 7.0.1 - resolution: "ts-node@npm:7.0.1" - dependencies: - arrify: ^1.0.0 - buffer-from: ^1.1.0 - diff: ^3.1.0 - make-error: ^1.1.1 - minimist: ^1.2.0 - mkdirp: ^0.5.1 - source-map-support: ^0.5.6 - yn: ^2.0.0 - bin: - ts-node: dist/bin.js - checksum: 07ed6ea1805361828737a767cfd6c57ea6e267ee8679282afb933610af02405e1a87c1f2aea1d38ed8e66b34fcbf6272b6021ab95d78849105d2e57fc283870b - languageName: node - linkType: hard - "ts-node@npm:^10.9.1": version: 10.9.1 resolution: "ts-node@npm:10.9.1" @@ -13361,25 +12970,6 @@ __metadata: languageName: node linkType: hard -"tsconfig-paths@npm:^3.5.0": - version: 3.14.2 - resolution: "tsconfig-paths@npm:3.14.2" - dependencies: - "@types/json5": ^0.0.29 - json5: ^1.0.2 - minimist: ^1.2.6 - strip-bom: ^3.0.0 - checksum: a6162eaa1aed680537f93621b82399c7856afd10ec299867b13a0675e981acac4e0ec00896860480efc59fc10fd0b16fdc928c0b885865b52be62cadac692447 - languageName: node - linkType: hard - -"tslib@npm:^1.8.1": - version: 1.14.1 - resolution: "tslib@npm:1.14.1" - checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd - languageName: node - linkType: hard - "tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0, tslib@npm:^2.6.1": version: 2.6.2 resolution: "tslib@npm:2.6.2" @@ -13387,17 +12977,6 @@ __metadata: languageName: node linkType: hard -"tsutils@npm:^3.21.0": - version: 3.21.0 - resolution: "tsutils@npm:3.21.0" - dependencies: - tslib: ^1.8.1 - peerDependencies: - typescript: ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - checksum: 1843f4c1b2e0f975e08c4c21caa4af4f7f65a12ac1b81b3b8489366826259323feb3fc7a243123453d2d1a02314205a7634e048d4a8009921da19f99755cdc48 - languageName: node - linkType: hard - "tuf-js@npm:^1.1.7": version: 1.1.7 resolution: "tuf-js@npm:1.1.7" @@ -13418,7 +12997,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:4.0.8, type-detect@npm:^4.0.0, type-detect@npm:^4.0.5": +"type-detect@npm:4.0.8": version: 4.0.8 resolution: "type-detect@npm:4.0.8" checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15 @@ -13527,16 +13106,6 @@ __metadata: languageName: node linkType: hard -"typescript@npm:^4.9.5": - version: 4.9.5 - resolution: "typescript@npm:4.9.5" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db - languageName: node - linkType: hard - "typescript@patch:typescript@^4.7 || 5#~builtin, typescript@patch:typescript@^5.2.2#~builtin": version: 5.2.2 resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin::version=5.2.2&hash=f3b441" @@ -13547,16 +13116,6 @@ __metadata: languageName: node linkType: hard -"typescript@patch:typescript@^4.9.5#~builtin": - version: 4.9.5 - resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=289587" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 1f8f3b6aaea19f0f67cba79057674ba580438a7db55057eb89cc06950483c5d632115c14077f6663ea76fd09fce3c190e6414bb98582ec80aa5a4eaf345d5b68 - languageName: node - linkType: hard - "unbox-primitive@npm:^1.0.2": version: 1.0.2 resolution: "unbox-primitive@npm:1.0.2" @@ -13791,7 +13350,7 @@ __metadata: languageName: node linkType: hard -"use-sync-external-store@npm:^1.0.0": +"use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" peerDependencies: @@ -14026,7 +13585,7 @@ __metadata: languageName: node linkType: hard -"webpack-cli@npm:^5.0.1, webpack-cli@npm:^5.1.4": +"webpack-cli@npm:^5.1.4": version: 5.1.4 resolution: "webpack-cli@npm:5.1.4" dependencies: @@ -14147,7 +13706,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:^5.77.0, webpack@npm:^5.88.2": +"webpack@npm:^5.88.2": version: 5.88.2 resolution: "webpack@npm:5.88.2" dependencies: @@ -14564,13 +14123,6 @@ __metadata: languageName: node linkType: hard -"workerpool@npm:6.2.1": - version: 6.2.1 - resolution: "workerpool@npm:6.2.1" - checksum: c2c6eebbc5225f10f758d599a5c016fa04798bcc44e4c1dffb34050cd361d7be2e97891aa44419e7afe647b1f767b1dc0b85a5e046c409d890163f655028b09d - languageName: node - linkType: hard - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -14636,7 +14188,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.13.0": +"ws@npm:^8.11.0, ws@npm:^8.13.0": version: 8.13.0 resolution: "ws@npm:8.13.0" peerDependencies: @@ -14651,6 +14203,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.14.0": + version: 8.14.0 + resolution: "ws@npm:8.14.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: dd91d055396c42552d8e2d26a0ab10221e73ca356de3db9109e337b8d9df216a0a308ace46a5e0520ed18ffcae3f54c2fa45a96711f94a063c816ef13a30b700 + languageName: node + linkType: hard + "xml-name-validator@npm:^4.0.0": version: 4.0.0 resolution: "xml-name-validator@npm:4.0.0" @@ -14686,20 +14253,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:20.2.4": - version: 20.2.4 - resolution: "yargs-parser@npm:20.2.4" - checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924 - languageName: node - linkType: hard - -"yargs-parser@npm:^20.2.2": - version: 20.2.9 - resolution: "yargs-parser@npm:20.2.9" - checksum: 8bb69015f2b0ff9e17b2c8e6bfe224ab463dd00ca211eece72a4cd8a906224d2703fb8a326d36fdd0e68701e201b2a60ed7cf81ce0fd9b3799f9fe7745977ae3 - languageName: node - linkType: hard - "yargs-parser@npm:^21.0.1, yargs-parser@npm:^21.1.1": version: 21.1.1 resolution: "yargs-parser@npm:21.1.1" @@ -14707,33 +14260,6 @@ __metadata: languageName: node linkType: hard -"yargs-unparser@npm:2.0.0": - version: 2.0.0 - resolution: "yargs-unparser@npm:2.0.0" - dependencies: - camelcase: ^6.0.0 - decamelize: ^4.0.0 - flat: ^5.0.2 - is-plain-obj: ^2.1.0 - checksum: 68f9a542c6927c3768c2f16c28f71b19008710abd6b8f8efbac6dcce26bbb68ab6503bed1d5994bdbc2df9a5c87c161110c1dfe04c6a3fe5c6ad1b0e15d9a8a3 - languageName: node - linkType: hard - -"yargs@npm:16.2.0": - version: 16.2.0 - resolution: "yargs@npm:16.2.0" - dependencies: - cliui: ^7.0.2 - escalade: ^3.1.1 - get-caller-file: ^2.0.5 - require-directory: ^2.1.1 - string-width: ^4.2.0 - y18n: ^5.0.5 - yargs-parser: ^20.2.2 - checksum: b14afbb51e3251a204d81937c86a7e9d4bdbf9a2bcee38226c900d00f522969ab675703bee2a6f99f8e20103f608382936034e64d921b74df82b63c07c5e8f59 - languageName: node - linkType: hard - "yargs@npm:^17.3.1": version: 17.7.2 resolution: "yargs@npm:17.7.2" @@ -14831,13 +14357,6 @@ __metadata: languageName: node linkType: hard -"yn@npm:^2.0.0": - version: 2.0.0 - resolution: "yn@npm:2.0.0" - checksum: 9d49527cb3e9a0948cc057223810bf30607bf04b9ff7666cc1681a6501d660b60d90000c16f9e29311b0f28d8a06222ada565ccdca5f1049cdfefb1908217572 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"