dino-0.5.0/0000775000000000000000000000000014776241610011155 5ustar rootrootdino-0.5.0/.github/0000775000000000000000000000000014776241610012515 5ustar rootrootdino-0.5.0/.github/matchers/0000775000000000000000000000000014776241610014323 5ustar rootrootdino-0.5.0/.github/matchers/gcc-problem-matcher.json0000664000000000000000000000066314776241610021036 0ustar rootroot{ "problemMatcher": [ { "owner": "gcc-problem-matcher", "pattern": [ { "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } ] } ] } dino-0.5.0/.github/matchers/meson-problem-matcher.json0000664000000000000000000000064614776241610021424 0ustar rootroot{ "problemMatcher": [ { "owner": "meson-problem-matcher", "pattern": [ { "regexp": "^(.*?)?:(\\d+)?:(\\d+)?: (WARNING|ERROR):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } ] } ] } dino-0.5.0/.github/matchers/vala-problem-matcher.json0000664000000000000000000000070614776241610021223 0ustar rootroot{ "problemMatcher": [ { "owner": "vala-problem-matcher", "pattern": [ { "regexp": "^(?:../)?(.*?):(\\d+).(\\d+)-\\d+.\\d+:?\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$", "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5 } ] } ] } dino-0.5.0/.github/workflows/0000775000000000000000000000000014776241610014552 5ustar rootrootdino-0.5.0/.github/workflows/build.yml0000664000000000000000000000323014776241610016372 0ustar rootrootname: Build on: [pull_request, push] jobs: build: name: "Build" runs-on: ubuntu-24.04 steps: - name: "Checkout sources" uses: actions/checkout@v4 with: fetch-depth: 0 - name: "Setup matchers" run: | echo '::add-matcher::${{ github.workspace }}/.github/matchers/gcc-problem-matcher.json' echo '::add-matcher::${{ github.workspace }}/.github/matchers/vala-problem-matcher.json' echo '::add-matcher::${{ github.workspace }}/.github/matchers/meson-problem-matcher.json' - name: "Setup dependencies" run: | sudo apt-get update sudo apt-get remove libunwind-14-dev sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libsignal-protocol-c-dev libsoup-3.0-dev libsqlite3-dev libsrtp2-dev libwebrtc-audio-processing-dev meson valac - name: "Configure" run: meson setup build - name: "Build" run: meson compile -C build - name: "Test" run: meson test -C build build-flatpak: name: "Build flatpak" runs-on: ubuntu-24.04 container: image: bilelmoussaoui/flatpak-github-actions:gnome-46 options: --privileged steps: - name: "Checkout sources" uses: actions/checkout@v4 with: fetch-depth: 0 - name: "Build" uses: flathub-infra/flatpak-github-actions/flatpak-builder@master with: manifest-path: im.dino.Dino.json bundle: im.dino.Dino.flatpak dino-0.5.0/.gitignore0000664000000000000000000000010414776241610013140 0ustar rootroot*.o build/ Makefile .vscode/ *.iml .idea .sqlite3 gschemas.compiled dino-0.5.0/LICENSE0000664000000000000000000010451514776241610012170 0ustar rootroot 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 . dino-0.5.0/README.md0000664000000000000000000000372314776241610012441 0ustar rootroot # Dino Dino is an XMPP messaging app for Linux using GTK and Vala. It supports calls, encryption, file transfers, group chats and more. ![screenshot](https://dino.im/img/appdata/screenshot-dino-0.4-main-2244x1644@2.png) Installation ------------ Have a look at the [prebuilt packages](https://github.com/dino/dino/wiki/Distribution-Packages). Build ----- Make sure to install all [dependencies](https://github.com/dino/dino/wiki/Build#dependencies). meson setup build meson compile -C build build/main/dino Resources --------- - Check out the [Dino website](https://dino.im). - Join our XMPP channel at `chat@dino.im`. - The [wiki](https://github.com/dino/dino/wiki) provides additional information. Contribute ---------- - Pull requests are welcome. [These](https://github.com/dino/dino/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) might be good first issues. Please discuss bigger changes in our channel first. - Look at [how to debug](https://github.com/dino/dino/wiki/Debugging) Dino before you report a bug. - Help [translating](https://github.com/dino/dino/wiki/Translations) Dino into your language. - Make a [donation](https://dino.im/#donate). License ------- Dino - XMPP messaging app using GTK/Vala Copyright (C) 2016-2025 Dino contributors 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 . dino-0.5.0/VERSION0000664000000000000000000000001614776241610012222 0ustar rootrootRELEASE 0.5.0 dino-0.5.0/crypto-vala/0000775000000000000000000000000014776241610013416 5ustar rootrootdino-0.5.0/crypto-vala/crypto-vala.deps0000664000000000000000000000002114776241610016525 0ustar rootrootgio-2.0 glib-2.0 dino-0.5.0/crypto-vala/meson.build0000664000000000000000000000152214776241610015560 0ustar rootrootdependencies = [ dep_gio, dep_glib, dep_libgcrypt, dep_libsrtp2, ] sources = files( 'src/cipher.vala', 'src/cipher_converter.vala', 'src/error.vala', 'src/random.vala', 'src/srtp.vala', ) c_args = [ '-DG_LOG_DOMAIN="crypto-vala"', ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] lib_crypto_vala = library('crypto-vala', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath) dep_crypto_vala = declare_dependency(link_with: lib_crypto_vala, include_directories: include_directories('.')) install_data('crypto-vala.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 dino-0.5.0/crypto-vala/src/0000775000000000000000000000000014776241610014205 5ustar rootrootdino-0.5.0/crypto-vala/src/cipher.vala0000664000000000000000000001364714776241610016337 0ustar rootrootnamespace Crypto { public class SymmetricCipher { private GCrypt.Cipher.Cipher cipher; public static bool supports(string algo_name) { GCrypt.Cipher.Algorithm algo; GCrypt.Cipher.Mode mode; GCrypt.Cipher.Flag flags; return parse(algo_name, out algo, out mode, out flags); } private static unowned string mode_to_string(GCrypt.Cipher.Mode mode) { switch (mode) { case GCrypt.Cipher.Mode.ECB: return "ECB"; case GCrypt.Cipher.Mode.CFB: return "CFB"; case GCrypt.Cipher.Mode.CBC: return "CBC"; case GCrypt.Cipher.Mode.STREAM: return "STREAM"; case GCrypt.Cipher.Mode.OFB: return "OFB"; case GCrypt.Cipher.Mode.CTR: return "CTR"; case GCrypt.Cipher.Mode.AESWRAP: return "AESWRAP"; case GCrypt.Cipher.Mode.GCM: return "GCM"; case GCrypt.Cipher.Mode.POLY1305: return "POLY1305"; case GCrypt.Cipher.Mode.OCB: return "OCB"; case GCrypt.Cipher.Mode.CFB8: return "CFB8"; // case GCrypt.Cipher.Mode.XTS: return "XTS"; // Not supported in gcrypt < 1.8 } return "NONE"; } private static GCrypt.Cipher.Mode mode_from_string(string name) { switch (name) { case "ECB": return GCrypt.Cipher.Mode.ECB; case "CFB": return GCrypt.Cipher.Mode.CFB; case "CBC": return GCrypt.Cipher.Mode.CBC; case "STREAM": return GCrypt.Cipher.Mode.STREAM; case "OFB": return GCrypt.Cipher.Mode.OFB; case "CTR": return GCrypt.Cipher.Mode.CTR; case "AESWRAP": return GCrypt.Cipher.Mode.AESWRAP; case "GCM": return GCrypt.Cipher.Mode.GCM; case "POLY1305": return GCrypt.Cipher.Mode.POLY1305; case "OCB": return GCrypt.Cipher.Mode.OCB; case "CFB8": return GCrypt.Cipher.Mode.CFB8; // case "XTS": return GCrypt.Cipher.Mode.XTS; // Not supported in gcrypt < 1.8 } return GCrypt.Cipher.Mode.NONE; } private static string flags_to_string(GCrypt.Cipher.Flag flags) { string? s = null; if ((GCrypt.Cipher.Flag.CBC_MAC & flags) != 0) s = (s == null ? "" : @"$s-") + "MAC"; if ((GCrypt.Cipher.Flag.CBC_CTS & flags) != 0) s = (s == null ? "" : @"$s-") + "CTS"; if ((GCrypt.Cipher.Flag.ENABLE_SYNC & flags) != 0) s = (s == null ? "" : @"$s-") + "SYNC"; if ((GCrypt.Cipher.Flag.SECURE & flags) != 0) s = (s == null ? "" : @"$s-") + "SECURE"; return s ?? "NONE"; } private static GCrypt.Cipher.Flag flag_from_string(string flag_name) { if (flag_name == "SECURE") return GCrypt.Cipher.Flag.SECURE; if (flag_name == "SYNC") return GCrypt.Cipher.Flag.ENABLE_SYNC; if (flag_name == "CTS") return GCrypt.Cipher.Flag.CBC_CTS; if (flag_name == "MAC") return GCrypt.Cipher.Flag.CBC_MAC; return 0; } private static GCrypt.Cipher.Flag flags_from_string(string flag_names) { GCrypt.Cipher.Flag flags = 0; foreach(string flag in flag_names.split("-")) { flags |= flag_from_string(flag); } return flags; } private static bool parse(string algo_name, out GCrypt.Cipher.Algorithm algo, out GCrypt.Cipher.Mode mode, out GCrypt.Cipher.Flag flags) { algo = GCrypt.Cipher.Algorithm.NONE; mode = GCrypt.Cipher.Mode.NONE; flags = 0; string[] algo_parts = algo_name.split("-", 3); algo = GCrypt.Cipher.Algorithm.from_string(algo_parts[0]); if (algo_parts.length >= 2) { mode = mode_from_string(algo_parts[1]); } if (algo_parts.length == 3) { flags |= flags_from_string(algo_parts[2]); } return to_algo_name(algo, mode, flags) == algo_name; } private static string to_algo_name(GCrypt.Cipher.Algorithm algo = GCrypt.Cipher.Algorithm.NONE, GCrypt.Cipher.Mode mode = GCrypt.Cipher.Mode.NONE, GCrypt.Cipher.Flag flags = 0) { if (flags != 0) { return @"$algo-$(mode_to_string(mode))-$(flags_to_string(flags))"; } else if (mode != GCrypt.Cipher.Mode.NONE) { return @"$algo-$(mode_to_string(mode))"; } else { return algo.to_string(); } } public SymmetricCipher(string algo_name) throws Error { GCrypt.Cipher.Algorithm algo; GCrypt.Cipher.Mode mode; GCrypt.Cipher.Flag flags; if (parse(algo_name, out algo, out mode, out flags)) { this.gcrypt(algo, mode, flags); } else { throw new Error.ILLEGAL_ARGUMENTS(@"The algorithm $algo_name is not supported"); } } private SymmetricCipher.gcrypt(GCrypt.Cipher.Algorithm algo, GCrypt.Cipher.Mode mode, GCrypt.Cipher.Flag flags) throws Error { may_throw_gcrypt_error(GCrypt.Cipher.Cipher.open(out this.cipher, algo, mode, flags)); } public void set_key(uint8[] key) throws Error { may_throw_gcrypt_error(cipher.set_key(key)); } public void set_iv(uint8[] iv) throws Error { may_throw_gcrypt_error(cipher.set_iv(iv)); } public void set_counter_vector(uint8[] ctr) throws Error { may_throw_gcrypt_error(cipher.set_counter_vector(ctr)); } public void reset() throws Error { may_throw_gcrypt_error(cipher.reset()); } public uint8[] get_tag(size_t taglen) throws Error { uint8[] tag = new uint8[taglen]; may_throw_gcrypt_error(cipher.get_tag(tag)); return tag; } public void check_tag(uint8[] tag) throws Error { may_throw_gcrypt_error(cipher.check_tag(tag)); } public void encrypt(uint8[] output, uint8[] input) throws Error { may_throw_gcrypt_error(cipher.encrypt(output, input)); } public void decrypt(uint8[] output, uint8[] input) throws Error { may_throw_gcrypt_error(cipher.decrypt(output, input)); } public void sync() throws Error { may_throw_gcrypt_error(cipher.sync()); } } } dino-0.5.0/crypto-vala/src/cipher_converter.vala0000664000000000000000000001040014776241610020406 0ustar rootrootusing GLib; namespace Crypto { public abstract class SymmetricCipherConverter : Converter, Object { internal SymmetricCipher cipher; internal size_t attached_taglen; public abstract ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError; public uint8[] get_tag(size_t taglen) throws Error { return cipher.get_tag(taglen); } public void check_tag(uint8[] tag) throws Error { cipher.check_tag(tag); } public void reset() { try { cipher.reset(); } catch (Crypto.Error e) { warning(@"$(e.domain) error while resetting cipher: $(e.message)"); } } } public class SymmetricCipherEncrypter : SymmetricCipherConverter { public SymmetricCipherEncrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { this.cipher = (owned) cipher; this.attached_taglen = attached_taglen; } public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } if ((flags & ConverterFlags.INPUT_AT_END) != 0 && inbuf.length + attached_taglen > outbuf.length) { throw new IOError.NO_SPACE("CipherConverter needs additional output space to attach tag"); } try { if (inbuf.length > 0) { cipher.encrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; if ((flags & ConverterFlags.INPUT_AT_END) != 0) { if (attached_taglen > 0) { Memory.copy((uint8*)outbuf + inbuf.length, get_tag(attached_taglen), attached_taglen); bytes_written = inbuf.length + attached_taglen; } return ConverterResult.FINISHED; } if ((flags & ConverterFlags.FLUSH) != 0) { return ConverterResult.FLUSHED; } return ConverterResult.CONVERTED; } catch (Crypto.Error e) { throw new IOError.FAILED(@"$(e.domain) error while encrypting: $(e.message)"); } } } public class SymmetricCipherDecrypter : SymmetricCipherConverter { public SymmetricCipherDecrypter(owned SymmetricCipher cipher, size_t attached_taglen = 0) { this.cipher = (owned) cipher; this.attached_taglen = attached_taglen; } public override ConverterResult convert(uint8[] inbuf, uint8[] outbuf, ConverterFlags flags, out size_t bytes_read, out size_t bytes_written) throws IOError { if (inbuf.length > outbuf.length + attached_taglen) { throw new IOError.NO_SPACE("CipherConverter needs at least the size of input as output space"); } if ((flags & ConverterFlags.INPUT_AT_END) != 0 && inbuf.length < attached_taglen) { throw new IOError.PARTIAL_INPUT("CipherConverter needs additional input to read tag"); } else if ((flags & ConverterFlags.INPUT_AT_END) == 0 && inbuf.length < attached_taglen + 1) { throw new IOError.PARTIAL_INPUT("CipherConverter needs additional input to make sure to not accidentally read tag"); } try { inbuf.length -= (int) attached_taglen; if (inbuf.length > 0) { cipher.decrypt(outbuf, inbuf); } bytes_read = inbuf.length; bytes_written = inbuf.length; inbuf.length += (int) attached_taglen; if ((flags & ConverterFlags.INPUT_AT_END) != 0) { if (attached_taglen > 0) { check_tag(inbuf[(inbuf.length - attached_taglen):inbuf.length]); bytes_read = inbuf.length; } return ConverterResult.FINISHED; } if ((flags & ConverterFlags.FLUSH) != 0) { return ConverterResult.FLUSHED; } return ConverterResult.CONVERTED; } catch (Crypto.Error e) { throw new IOError.FAILED(@"$(e.domain) error while decrypting: $(e.message)"); } } } } dino-0.5.0/crypto-vala/src/error.vala0000664000000000000000000000043014776241610016200 0ustar rootrootnamespace Crypto { public errordomain Error { ILLEGAL_ARGUMENTS, GCRYPT, AUTHENTICATION_FAILED, UNKNOWN } internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error { if (((int)e) != 0) { throw new Crypto.Error.GCRYPT(e.to_string()); } } }dino-0.5.0/crypto-vala/src/random.vala0000664000000000000000000000015214776241610016330 0ustar rootrootnamespace Crypto { public static void randomize(uint8[] buffer) { GCrypt.Random.randomize(buffer); } }dino-0.5.0/crypto-vala/src/srtp.vala0000664000000000000000000001044514776241610016046 0ustar rootrootusing Srtp; namespace Crypto.Srtp { public const string AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80"; public const string AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32"; public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80"; public class Session { public bool has_encrypt { get; private set; default = false; } public bool has_decrypt { get; private set; default = false; } private Context encrypt_context; private Context decrypt_context; static construct { init(); install_log_handler(log); } private static void log(LogLevel level, string msg) { print(@"SRTP[$level]: $msg\n"); } public Session() { Context.create(out encrypt_context, null); Context.create(out decrypt_context, null); } public uint8[] encrypt_rtp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = encrypt_context.protect(buf, ref buf_use); if (res != ErrorStatus.ok) { throw new Error.UNKNOWN(@"SRTP encrypt failed: $res"); } buf.length = buf_use; return buf; } public uint8[] decrypt_rtp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = decrypt_context.unprotect(buf, ref buf_use); switch (res) { case ErrorStatus.auth_fail: throw new Error.AUTHENTICATION_FAILED("SRTP packet failed the message authentication check"); case ErrorStatus.ok: break; default: throw new Error.UNKNOWN(@"SRTP decrypt failed: $res"); } uint8[] ret = new uint8[buf_use]; GLib.Memory.copy(ret, buf, buf_use); return ret; } public uint8[] encrypt_rtcp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN + 4]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = encrypt_context.protect_rtcp(buf, ref buf_use); if (res != ErrorStatus.ok) { throw new Error.UNKNOWN(@"SRTCP encrypt failed: $res"); } buf.length = buf_use; return buf; } public uint8[] decrypt_rtcp(uint8[] data) throws Error { uint8[] buf = new uint8[data.length]; Memory.copy(buf, data, data.length); int buf_use = data.length; ErrorStatus res = decrypt_context.unprotect_rtcp(buf, ref buf_use); switch (res) { case ErrorStatus.auth_fail: throw new Error.AUTHENTICATION_FAILED("SRTCP packet failed the message authentication check"); case ErrorStatus.ok: break; default: throw new Error.UNKNOWN(@"SRTCP decrypt failed: $res"); } uint8[] ret = new uint8[buf_use]; GLib.Memory.copy(ret, buf, buf_use); return ret; } private Policy create_policy(string profile) { Policy policy = Policy(); switch (profile) { case AES_CM_128_HMAC_SHA1_80: policy.rtp.set_aes_cm_128_hmac_sha1_80(); policy.rtcp.set_aes_cm_128_hmac_sha1_80(); break; } return policy; } public void set_encryption_key(string profile, uint8[] key, uint8[] salt) { Policy policy = create_policy(profile); policy.ssrc.type = SsrcType.any_outbound; policy.key = new uint8[key.length + salt.length]; Memory.copy(policy.key, key, key.length); Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); policy.next = null; encrypt_context.add_stream(ref policy); has_encrypt = true; } public void set_decryption_key(string profile, uint8[] key, uint8[] salt) { Policy policy = create_policy(profile); policy.ssrc.type = SsrcType.any_inbound; policy.key = new uint8[key.length + salt.length]; Memory.copy(policy.key, key, key.length); Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); policy.next = null; decrypt_context.add_stream(ref policy); has_decrypt = true; } } }dino-0.5.0/crypto-vala/vapi/0000775000000000000000000000000014776241610014355 5ustar rootrootdino-0.5.0/crypto-vala/vapi/libgcrypt.vapi0000664000000000000000000004650414776241610017246 0ustar rootroot/* gcrypt.vapi * * Copyright: * 2008 Jiqing Qiang * 2008, 2010, 2012-2013 Evan Nemerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jiqing Qiang * Evan Nemerson */ [CCode (cheader_filename = "gcrypt.h", lower_case_cprefix = "gcry_")] namespace GCrypt { [CCode (cname = "gpg_err_source_t", cprefix = "GPG_ERR_SOURCE_")] public enum ErrorSource { UNKNOWN, GCRYPT, GPG, GPGSM, GPGAGENT, PINENTRY, SCD, GPGME, KEYBOX, KSBA, DIRMNGR, GSTI, ANY, USER_1, USER_2, USER_3, USER_4, /* This is one more than the largest allowed entry. */ DIM } [CCode (cname = "gpg_err_code_t", cprefix = "GPG_ERR_")] public enum ErrorCode { NO_ERROR, GENERAL, UNKNOWN_PACKET, UNKNOWN_VERSION, PUBKEY_ALGO, DIGEST_ALGO, BAD_PUBKEY, BAD_SECKEY, BAD_SIGNATURE, NO_PUBKEY, CHECKSUM, BAD_PASSPHRASE, CIPHER_ALGO, KEYRING_OPEN, INV_PACKET, INV_ARMOR, NO_USER_ID, NO_SECKEY, WRONG_SECKEY, BAD_KEY, COMPR_ALGO, NO_PRIME, NO_ENCODING_METHOD, NO_ENCRYPTION_SCHEME, NO_SIGNATURE_SCHEME, INV_ATTR, NO_VALUE, NOT_FOUND, VALUE_NOT_FOUND, SYNTAX, BAD_MPI, INV_PASSPHRASE, SIG_CLASS, RESOURCE_LIMIT, INV_KEYRING, TRUSTDB, BAD_CERT, INV_USER_ID, UNEXPECTED, TIME_CONFLICT, KEYSERVER, WRONG_PUBKEY_ALGO, TRIBUTE_TO_D_A, WEAK_KEY, INV_KEYLEN, INV_ARG, BAD_URI, INV_URI, NETWORK, UNKNOWN_HOST, SELFTEST_FAILED, NOT_ENCRYPTED, NOT_PROCESSED, UNUSABLE_PUBKEY, UNUSABLE_SECKEY, INV_VALUE, BAD_CERT_CHAIN, MISSING_CERT, NO_DATA, BUG, NOT_SUPPORTED, INV_OP, TIMEOUT, INTERNAL, EOF_GCRYPT, INV_OBJ, TOO_SHORT, TOO_LARGE, NO_OBJ, NOT_IMPLEMENTED, CONFLICT, INV_CIPHER_MODE, INV_FLAG, INV_HANDLE, TRUNCATED, INCOMPLETE_LINE, INV_RESPONSE, NO_AGENT, AGENT, INV_DATA, ASSUAN_SERVER_FAULT, ASSUAN, INV_SESSION_KEY, INV_SEXP, UNSUPPORTED_ALGORITHM, NO_PIN_ENTRY, PIN_ENTRY, BAD_PIN, INV_NAME, BAD_DATA, INV_PARAMETER, WRONG_CARD, NO_DIRMNGR, DIRMNGR, CERT_REVOKED, NO_CRL_KNOWN, CRL_TOO_OLD, LINE_TOO_LONG, NOT_TRUSTED, CANCELED, BAD_CA_CERT, CERT_EXPIRED, CERT_TOO_YOUNG, UNSUPPORTED_CERT, UNKNOWN_SEXP, UNSUPPORTED_PROTECTION, CORRUPTED_PROTECTION, AMBIGUOUS_NAME, CARD, CARD_RESET, CARD_REMOVED, INV_CARD, CARD_NOT_PRESENT, NO_PKCS15_APP, NOT_CONFIRMED, CONFIGURATION, NO_POLICY_MATCH, INV_INDEX, INV_ID, NO_SCDAEMON, SCDAEMON, UNSUPPORTED_PROTOCOL, BAD_PIN_METHOD, CARD_NOT_INITIALIZED, UNSUPPORTED_OPERATION, WRONG_KEY_USAGE, NOTHING_FOUND, WRONG_BLOB_TYPE, MISSING_VALUE, HARDWARE, PIN_BLOCKED, USE_CONDITIONS, PIN_NOT_SYNCED, INV_CRL, BAD_BER, INV_BER, ELEMENT_NOT_FOUND, IDENTIFIER_NOT_FOUND, INV_TAG, INV_LENGTH, INV_KEYINFO, UNEXPECTED_TAG, NOT_DER_ENCODED, NO_CMS_OBJ, INV_CMS_OBJ, UNKNOWN_CMS_OBJ, UNSUPPORTED_CMS_OBJ, UNSUPPORTED_ENCODING, UNSUPPORTED_CMS_VERSION, UNKNOWN_ALGORITHM, INV_ENGINE, PUBKEY_NOT_TRUSTED, DECRYPT_FAILED, KEY_EXPIRED, SIG_EXPIRED, ENCODING_PROBLEM, INV_STATE, DUP_VALUE, MISSING_ACTION, MODULE_NOT_FOUND, INV_OID_STRING, INV_TIME, INV_CRL_OBJ, UNSUPPORTED_CRL_VERSION, INV_CERT_OBJ, UNKNOWN_NAME, LOCALE_PROBLEM, NOT_LOCKED, PROTOCOL_VIOLATION, INV_MAC, INV_REQUEST, UNKNOWN_EXTN, UNKNOWN_CRIT_EXTN, LOCKED, UNKNOWN_OPTION, UNKNOWN_COMMAND, BUFFER_TOO_SHORT, SEXP_INV_LEN_SPEC, SEXP_STRING_TOO_LONG, SEXP_UNMATCHED_PAREN, SEXP_NOT_CANONICAL, SEXP_BAD_CHARACTER, SEXP_BAD_QUOTATION, SEXP_ZERO_PREFIX, SEXP_NESTED_DH, SEXP_UNMATCHED_DH, SEXP_UNEXPECTED_PUNC, SEXP_BAD_HEX_CHAR, SEXP_ODD_HEX_NUMBERS, SEXP_BAD_OCT_CHAR, ASS_GENERAL, ASS_ACCEPT_FAILED, ASS_CONNECT_FAILED, ASS_INV_RESPONSE, ASS_INV_VALUE, ASS_INCOMPLETE_LINE, ASS_LINE_TOO_LONG, ASS_NESTED_COMMANDS, ASS_NO_DATA_CB, ASS_NO_INQUIRE_CB, ASS_NOT_A_SERVER, ASS_NOT_A_CLIENT, ASS_SERVER_START, ASS_READ_ERROR, ASS_WRITE_ERROR, ASS_TOO_MUCH_DATA, ASS_UNEXPECTED_CMD, ASS_UNKNOWN_CMD, ASS_SYNTAX, ASS_CANCELED, ASS_NO_INPUT, ASS_NO_OUTPUT, ASS_PARAMETER, ASS_UNKNOWN_INQUIRE, USER_1, USER_2, USER_3, USER_4, USER_5, USER_6, USER_7, USER_8, USER_9, USER_10, USER_11, USER_12, USER_13, USER_14, USER_15, USER_16, MISSING_ERRNO, UNKNOWN_ERRNO, EOF, E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EAUTH, EBACKGROUND, EBADE, EBADF, EBADFD, EBADMSG, EBADR, EBADRPC, EBADRQC, EBADSLT, EBFONT, EBUSY, ECANCELED, ECHILD, ECHRNG, ECOMM, ECONNABORTED, ECONNREFUSED, ECONNRESET, ED, EDEADLK, EDEADLOCK, EDESTADDRREQ, EDIED, EDOM, EDOTDOT, EDQUOT, EEXIST, EFAULT, EFBIG, EFTYPE, EGRATUITOUS, EGREGIOUS, EHOSTDOWN, EHOSTUNREACH, EIDRM, EIEIO, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, EISNAM, EL2HLT, EL2NSYNC, EL3HLT, EL3RST, ELIBACC, ELIBBAD, ELIBEXEC, ELIBMAX, ELIBSCN, ELNRNG, ELOOP, EMEDIUMTYPE, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, ENAVAIL, ENEEDAUTH, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOANO, ENOBUFS, ENOCSI, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEDIUM, ENOMEM, ENOMSG, ENONET, ENOPKG, ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTBLK, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTNAM, ENOTSOCK, ENOTSUP, ENOTTY, ENOTUNIQ, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPFNOSUPPORT, EPIPE, EPROCLIM, EPROCUNAVAIL, EPROGMISMATCH, EPROGUNAVAIL, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EREMCHG, EREMOTE, EREMOTEIO, ERESTART, EROFS, ERPCMISMATCH, ESHUTDOWN, ESOCKTNOSUPPORT, ESPIPE, ESRCH, ESRMNT, ESTALE, ESTRPIPE, ETIME, ETIMEDOUT, ETOOMANYREFS, ETXTBSY, EUCLEAN, EUNATCH, EUSERS, EWOULDBLOCK, EXDEV, EXFULL, /* This is one more than the largest allowed entry. */ CODE_DIM } [CCode (cname = "gcry_error_t", cprefix = "gpg_err_")] public struct Error : uint { [CCode (cname = "gcry_err_make")] public Error (ErrorSource source, ErrorCode code); [CCode (cname = "gcry_err_make_from_errno")] public Error.from_errno (ErrorSource source, int err); public ErrorCode code (); public ErrorSource source (); [CCode (cname = "gcry_strerror")] public unowned string to_string (); [CCode (cname = "gcry_strsource")] public unowned string source_to_string (); } [CCode (cname = "enum gcry_ctl_cmds", cprefix = "GCRYCTL_")] public enum ControlCommand { SET_KEY, SET_IV, CFB_SYNC, RESET, FINALIZE, GET_KEYLEN, GET_BLKLEN, TEST_ALGO, IS_SECURE, GET_ASNOID, ENABLE_ALGO, DISABLE_ALGO, DUMP_RANDOM_STATS, DUMP_SECMEM_STATS, GET_ALGO_NPKEY, GET_ALGO_NSKEY, GET_ALGO_NSIGN, GET_ALGO_NENCR, SET_VERBOSITY, SET_DEBUG_FLAGS, CLEAR_DEBUG_FLAGS, USE_SECURE_RNDPOOL, DUMP_MEMORY_STATS, INIT_SECMEM, TERM_SECMEM, DISABLE_SECMEM_WARN, SUSPEND_SECMEM_WARN, RESUME_SECMEM_WARN, DROP_PRIVS, ENABLE_M_GUARD, START_DUMP, STOP_DUMP, GET_ALGO_USAGE, IS_ALGO_ENABLED, DISABLE_INTERNAL_LOCKING, DISABLE_SECMEM, INITIALIZATION_FINISHED, INITIALIZATION_FINISHED_P, ANY_INITIALIZATION_P, SET_CBC_CTS, SET_CBC_MAC, SET_CTR, ENABLE_QUICK_RANDOM, SET_RANDOM_SEED_FILE, UPDATE_RANDOM_SEED_FILE, SET_THREAD_CBS, FAST_POLL } public Error control (ControlCommand cmd, ...); [CCode (lower_case_cname = "cipher_")] namespace Cipher { [CCode (cname = "enum gcry_cipher_algos", cprefix = "GCRY_CIPHER_")] public enum Algorithm { NONE, IDEA, 3DES, CAST5, BLOWFISH, SAFER_SK128, DES_SK, AES, AES128, RIJNDAEL, RIJNDAEL128, AES192, RIJNDAEL192, AES256, RIJNDAEL256, TWOFISH, TWOFISH128, ARCFOUR, DES, SERPENT128, SERPENT192, SERPENT256, RFC2268_40, RFC2268_128, SEED, CAMELLIA128, CAMELLIA192, CAMELLIA256, SALSA20, SALSA20R12, GOST28147, CHACHA20; [CCode (cname = "gcry_cipher_algo_info")] public Error info (ControlCommand what, ref uchar[] buffer); [CCode (cname = "gcry_cipher_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_cipher_map_name")] public static Algorithm from_string (string name); [CCode (cname = "gcry_cipher_map_oid")] public static Algorithm from_oid (string oid); } [CCode (cname = "enum gcry_cipher_modes", cprefix = "GCRY_CIPHER_MODE_")] public enum Mode { NONE, /* No mode specified */ ECB, /* Electronic Codebook */ CFB, /* Cipher Feedback */ CBC, /* Cipher Block Chaining */ STREAM, /* Used with stream ciphers */ OFB, /* Output Feedback */ CTR, /* Counter */ AESWRAP, /* AES-WRAP algorithm */ CCM, /* Counter with CBC-MAC */ GCM, /* Galois/Counter Mode */ POLY1305, /* Poly1305 based AEAD mode */ OCB, /* OCB3 mode */ CFB8, /* Cipher Feedback /* Poly1305 based AEAD mode. */ XTS; /* XTS mode */ public unowned string to_string () { switch (this) { case ECB: return "ECB"; case CFB: return "CFB"; case CBC: return "CBC"; case STREAM: return "STREAM"; case OFB: return "OFB"; case CTR: return "CTR"; case AESWRAP: return "AESWRAP"; case GCM: return "GCM"; case POLY1305: return "POLY1305"; case OCB: return "OCB"; case CFB8: return "CFB8"; case XTS: return "XTS"; } return "NONE"; } public static Mode from_string (string name) { switch (name) { case "ECB": return ECB; case "CFB": return CFB; case "CBC": return CBC; case "STREAM": return STREAM; case "OFB": return OFB; case "CTR": return CTR; case "AESWRAP": return AESWRAP; case "GCM": return GCM; case "POLY1305": return POLY1305; case "OCB": return OCB; case "CFB8": return CFB8; case "XTS": return XTS; } return NONE; } } [CCode (cname = "enum gcry_cipher_flags", cprefix = "GCRY_CIPHER_")] public enum Flag { SECURE, /* Allocate in secure memory. */ ENABLE_SYNC, /* Enable CFB sync mode. */ CBC_CTS, /* Enable CBC cipher text stealing (CTS). */ CBC_MAC /* Enable CBC message auth. code (MAC). */ } [CCode (cname = "gcry_cipher_hd_t", lower_case_cprefix = "gcry_cipher_", free_function = "gcry_cipher_close")] [SimpleType] public struct Cipher { public static Error open (out Cipher cipher, Algorithm algo, Mode mode, Flag flags); public void close (); [CCode (cname = "gcry_cipher_ctl")] public Error control (ControlCommand cmd, uchar[] buffer); public Error info (ControlCommand what, ref uchar[] buffer); public Error encrypt (uchar[] out_buffer, uchar[] in_buffer); public Error decrypt (uchar[] out_buffer, uchar[] in_buffer); [CCode (cname = "gcry_cipher_setkey")] public Error set_key (uchar[] key_data); [CCode (cname = "gcry_cipher_setiv")] public Error set_iv (uchar[] iv_data); [CCode (cname = "gcry_cipher_setctr")] public Error set_counter_vector (uchar[] counter_vector); [CCode (cname = "gcry_cipher_gettag")] public Error get_tag(uchar[] out_buffer); [CCode (cname = "gcry_cipher_checktag")] public Error check_tag(uchar[] in_buffer); public Error reset (); public Error sync (); } } [Compact, CCode (cname = "struct gcry_md_handle", cprefix = "gcry_md_", free_function = "gcry_md_close")] public class Hash { [CCode (cname = "enum gcry_md_algos", cprefix = "GCRY_MD_")] public enum Algorithm { NONE, SHA1, RMD160, MD5, MD4, MD2, TIGER, TIGER1, TIGER2, HAVAL, SHA224, SHA256, SHA384, SHA512, SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256, CRC32, CRC32_RFC1510, CRC24_RFC2440, WHIRLPOOL, GOSTR3411_94, STRIBOG256, STRIBOG512; [CCode (cname = "gcry_md_get_algo_dlen")] public size_t get_digest_length (); [CCode (cname = "gcry_md_algo_info")] public Error info (ControlCommand what, ref uchar[] buffer); [CCode (cname = "gcry_md_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_md_map_name")] public static Algorithm from_string (string name); [CCode (cname = "gcry_md_test_algo")] public Error is_available (); [CCode (cname = "gcry_md_get_asnoid")] public Error get_oid (uchar[] buffer); } [CCode (cname = "enum gcry_md_flags", cprefix = "GCRY_MD_FLAG_")] public enum Flag { SECURE, HMAC, BUGEMU1 } public static Error open (out Hash hash, Algorithm algo, Flag flag); public void close (); public Error enable (Algorithm algo); [CCode (instance_pos = -1)] public Error copy (out Hash dst); public void reset (); [CCode (cname = "enum gcry_md_ctl")] public Error control (ControlCommand cmd, uchar[] buffer); public void write (uchar[] buffer); [CCode (array_length = false)] public unowned uchar[] read (Algorithm algo); public static void hash_buffer (Algorithm algo, [CCode (array_length = false)] uchar[] digest, uchar[] buffer); public Algorithm get_algo (); public bool is_enabled (Algorithm algo); public bool is_secure (); public Error info (ControlCommand what, uchar[] buffer); [CCode (cname = "gcry_md_setkey")] public Error set_key (uchar[] key_data); public void putc (char c); public void final (); public static Error list (ref Algorithm[] algos); } namespace Random { [CCode (cname = "gcry_random_level_t")] public enum Level { [CCode (cname = "GCRY_WEAK_RANDOM")] WEAK, [CCode (cname = "GCRY_STRONG_RANDOM")] STRONG, [CCode (cname = "GCRY_VERY_STRONG_RANDOM")] VERY_STRONG } [CCode (cname = "gcry_randomize")] public static void randomize (uchar[] buffer, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_fast_random_poll")] public static Error poll (); [CCode (cname = "gcry_random_bytes", array_length = false)] public static uchar[] random_bytes (size_t nbytes, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_random_bytes_secure")] public static uchar[] random_bytes_secure (size_t nbytes, Level level = Level.VERY_STRONG); [CCode (cname = "gcry_create_nonce")] public static void nonce (uchar[] buffer); } [Compact, CCode (cname = "struct gcry_mpi", cprefix = "gcry_mpi_", free_function = "gcry_mpi_release")] public class MPI { [CCode (cname = "enum gcry_mpi_format", cprefix = "GCRYMPI_FMT_")] public enum Format { NONE, STD, PGP, SSH, HEX, USG } [CCode (cname = "enum gcry_mpi_flag", cprefix = "GCRYMPI_FLAG_")] public enum Flag { SECURE, OPAQUE } public MPI (uint nbits); [CCode (cname = "gcry_mpi_snew")] public MPI.secure (uint nbits); public MPI copy (); public void set (MPI u); public void set_ui (ulong u); public void swap (); public int cmp (MPI v); public int cmp_ui (ulong v); public static Error scan (out MPI ret, MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nscanned); [CCode (instance_pos = -1)] public Error print (MPI.Format format, [CCode (array_length = false)] uchar[] buffer, size_t buflen, out size_t nwritter); [CCode (instance_pos = -1)] public Error aprint (MPI.Format format, out uchar[] buffer); public void add (MPI u, MPI v); public void add_ui (MPI u, ulong v); public void addm (MPI u, MPI v, MPI m); public void sub (MPI u, MPI v); public void sub_ui (MPI u, MPI v); public void subm (MPI u, MPI v, MPI m); public void mul (MPI u, MPI v); public void mul_ui (MPI u, ulong v); public void mulm (MPI u, MPI v, MPI m); public void mul_2exp (MPI u, ulong cnt); public void div (MPI q, MPI r, MPI dividend, MPI divisor, int round); public void mod (MPI dividend, MPI divisor); public void powm (MPI b, MPI e, MPI m); public int gcd (MPI a, MPI b); public int invm (MPI a, MPI m); public uint get_nbits (); public int test_bit (uint n); public void set_bit (uint n); public void clear_bit (uint n); public void set_highbit (uint n); public void clear_highbit (uint n); public void rshift (MPI a, uint n); public void lshift (MPI a, uint n); public void set_flag (MPI.Flag flag); public void clear_flag (MPI.Flag flag); public int get_flag (MPI.Flag flag); } [Compact, CCode (cname = "struct gcry_sexp", free_function = "gcry_sexp_release")] public class SExp { [CCode (cprefix = "GCRYSEXP_FMT_")] public enum Format { DEFAULT, CANON, BASE64, ADVANCED } public static Error @new (out SExp retsexp, void * buffer, size_t length, int autodetect); public static Error create (out SExp retsexp, void * buffer, size_t length, int autodetect, GLib.DestroyNotify free_function); public static Error sscan (out SExp retsexp, out size_t erroff, char[] buffer); public static Error build (out SExp retsexp, out size_t erroff, string format, ...); public size_t sprint (Format mode, char[] buffer); public static size_t canon_len (uchar[] buffer, out size_t erroff, out int errcode); public SExp find_token (string token, size_t token_length = 0); public int length (); public SExp? nth (int number); public SExp? car (); public SExp? cdr (); public unowned char[] nth_data (int number); public gcry_string nth_string (int number); public MPI nth_mpi (int number, MPI.Format mpifmt); } [CCode (cname = "char", free_function = "gcry_free")] public class gcry_string : string { } [CCode (lower_case_cprefix = "gcry_pk_")] namespace PublicKey { [CCode (cname = "enum gcry_pk_algos")] public enum Algorithm { RSA, ELG_E, DSA, ELG, ECDSA; [CCode (cname = "gcry_pk_algo_name")] public unowned string to_string (); [CCode (cname = "gcry_pk_map_name")] public static Algorithm map_name (string name); } public static Error encrypt (out SExp ciphertext, SExp data, SExp pkey); public static Error decrypt (out SExp plaintext, SExp data, SExp skey); public static Error sign (out SExp signature, SExp data, SExp skey); public static Error verify (SExp signature, SExp data, SExp pkey); public static Error testkey (SExp key); public static Error genkey (out SExp r_key, SExp s_params); public static uint get_nbits (SExp key); } [CCode (lower_case_cprefix = "gcry_kdf_")] namespace KeyDerivation { [CCode (cname = "gcry_kdf_algos", cprefix = "GCRY_KDF_", has_type_id = false)] public enum Algorithm { NONE, SIMPLE_S2K, SALTED_S2K, ITERSALTED_S2K, PBKDF1, PBKDF2, SCRYPT } public GCrypt.Error derive ([CCode (type = "const void*", array_length_type = "size_t")] uint8[] passphrasse, GCrypt.KeyDerivation.Algorithm algo, GCrypt.Hash.Algorithm subalgo, [CCode (type = "const void*", array_length_type = "size_t")] uint8[] salt, ulong iterations, [CCode (type = "void*", array_length_type = "size_t", array_length_pos = 5.5)] uint8[] keybuffer); } } dino-0.5.0/crypto-vala/vapi/libsrtp2.vapi0000664000000000000000000001030114776241610016772 0ustar rootroot[CCode (cheader_filename = "srtp2/srtp.h")] namespace Srtp { public const uint MAX_TRAILER_LEN; public static ErrorStatus init(); public static ErrorStatus shutdown(); [Compact] [CCode (cname = "srtp_ctx_t", cprefix = "srtp_", free_function = "srtp_dealloc")] public class Context { public static ErrorStatus create(out Context session, Policy? policy); public ErrorStatus protect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); public ErrorStatus unprotect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); public ErrorStatus protect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); public ErrorStatus unprotect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); public ErrorStatus add_stream(ref Policy policy); public ErrorStatus update_stream(ref Policy policy); public ErrorStatus remove_stream(uint ssrc); public ErrorStatus update(ref Policy policy); } [CCode (cname = "srtp_ssrc_t")] public struct Ssrc { public SsrcType type; public uint value; } [CCode (cname = "srtp_ssrc_type_t", cprefix = "ssrc_")] public enum SsrcType { undefined, specific, any_inbound, any_outbound } [CCode (cname = "srtp_policy_t", destroy_function = "")] public struct Policy { public Ssrc ssrc; public CryptoPolicy rtp; public CryptoPolicy rtcp; [CCode (array_length = false)] public uint8[] key; public ulong num_master_keys; public ulong window_size; [CCode (ctype = "int")] public bool allow_repeat_tx; [CCode (array_length_cname = "enc_xtn_hdr_count")] public int[] enc_xtn_hdr; public Policy* next; } [CCode (cname = "srtp_crypto_policy_t")] public struct CryptoPolicy { public CipherType cipher_type; public int cipher_key_len; public AuthType auth_type; public int auth_key_len; public int auth_tag_len; public SecurityServices sec_serv; public void set_aes_cm_128_hmac_sha1_80(); public void set_aes_cm_128_hmac_sha1_32(); public void set_aes_cm_128_null_auth(); public void set_aes_cm_192_hmac_sha1_32(); public void set_aes_cm_192_hmac_sha1_80(); public void set_aes_cm_192_null_auth(); public void set_aes_cm_256_hmac_sha1_32(); public void set_aes_cm_256_hmac_sha1_80(); public void set_aes_cm_256_null_auth(); public void set_aes_gcm_128_16_auth(); public void set_aes_gcm_128_8_auth(); public void set_aes_gcm_128_8_only_auth(); public void set_aes_gcm_256_16_auth(); public void set_aes_gcm_256_8_auth(); public void set_aes_gcm_256_8_only_auth(); public void set_null_cipher_hmac_null(); public void set_null_cipher_hmac_sha1_80(); public void set_rtp_default(); public void set_rtcp_default(); public void set_from_profile_for_rtp(Profile profile); public void set_from_profile_for_rtcp(Profile profile); } [CCode (cname = "srtp_profile_t", cprefix = "srtp_profile_")] public enum Profile { reserved, aes128_cm_sha1_80, aes128_cm_sha1_32, null_sha1_80, null_sha1_32, aead_aes_128_gcm, aead_aes_256_gcm } [CCode (cname = "srtp_cipher_type_id_t")] public struct CipherType : uint32 {} [CCode (cname = "srtp_auth_type_id_t")] public struct AuthType : uint32 {} [CCode (cname = "srtp_sec_serv_t", cprefix = "sec_serv_")] public enum SecurityServices { none, conf, auth, conf_and_auth; } [CCode (cname = "srtp_err_status_t", cprefix = "srtp_err_status_", has_type_id = false)] public enum ErrorStatus { ok, fail, bad_param, alloc_fail, dealloc_fail, init_fail, terminus, auth_fail, cipher_fail, replay_fail, replay_old, algo_fail, no_such_op, no_ctx, cant_check, key_expired, socket_err, signal_err, nonce_bad, read_fail, write_fail, parse_err, encode_err, semaphore_err, pfkey_err, bad_mki, pkt_idx_old, pkt_idx_adv } [CCode (cname = "srtp_log_level_t", cprefix = "srtp_log_level_", has_type_id = false)] public enum LogLevel { error, warning, info, debug } [CCode (cname = "srtp_log_handler_func_t")] public delegate void LogHandler(LogLevel level, string msg); public static ErrorStatus install_log_handler(LogHandler func); }dino-0.5.0/dino.doap0000664000000000000000000012045314776241610012760 0ustar rootroot Dino dino Modern XMPP chat client 現代化的 XMPP 用戶端聊天軟件 现代 XMPP 聊天客户端 Modern XMPP Sohbet İstemcisi Modern XMPP-chattklient Klient Modern Fjalosjesh XMPP Современный XMPP клиент Client XMPP de discuții modern Cliente de Chat XMPP Moderno Moderno cliente de chat XMPP Nowoczesny komunikator XMPP Client XMPP modèrn Modernen XMPP-chatcliënt Een moderne XMPP-chatclient Moderne XMPP-sludreklient Šiuolaikinė XMPP pokalbių kliento programa Modernen XMPP Chat Client 現代的な XMPP チャット クライアント Client di chat moderno per XMPP Un modern client de conversationes XMPP Aplikasi chat XMPP modern Modern XMPP csevegőprogram Cliente moderno para conversas XMPP Client de clavardage XMPP moderne کلاینت نوین گپ XMPP XMPP txat bezero modernoa Un cliente de XMPP moderno Moderna XMPP-Retebabililo Modernes XMPP-Chat-Programm Moderní XMPP klient Client de xat XMPP modern تطبيق حديث للدردشة عبر XMPP Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind. It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications. Dino fetches history from the server and synchronizes messages with other devices. Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。 它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已讀回條和正在輸入提示。 Dino 從伺服器取得訊息並與其他裝置同步。 Dino 是一个现代的开源聊天桌面客户端。它致力于提供一个清爽又可靠的 Jabber/XMPP 体验,同时又保护您的隐私。 它支持 OMEMO 和 OpenPGP 端对端加密并允许配置隐私相关的特性比如已读回执和输入提醒。 Dino 从服务器获取消息并和其他设备同步。 Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır. OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve "okundu" bilgisi, "yazıyor..." bildirimi gibi gizlilikle alakalı özelliklerin ayarlanabilmesini sağlar. Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara senkronize eder. Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser efter din integritet. Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter konfigurering av funktioner med integritetspåverkan som läsbekräftelser och skriftaviseringar. Dino hämtar historik från servern och synkroniserar meddelanden med andra enheter. Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj. Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime shtypjeje. Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje të tjera. Dino - это современный клиент для чатов с открытым исходным кодом, направленный на надёжное и приватное использование Jabber/XMPP на персональных компьютерах. Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет настраивать такие функции, как уведомления о прочтении и наборе сообщений. Dino загружает историю с сервера и синхронизирует сообщения с другими устройствами. Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, ținând cont de confidențialitatea dumneavoastră. Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, și permite configurarea caracteristicilor legate de confidențialitate precum trimiterea notificărilor de primire și tastare. Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu celelalte dispozitive. Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em oferecer uma experiência Jabber/XMPP transparente e confiável levando em consideração a sua privacidade. Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, permite configurar funcionalidades relativas à privacidade como notificações de leitura, recebimento e escrita. Dino obtém o histórico do servidor e sincroniza mensagens com outros dispositivos. Dino é um moderno chat de código aberto para desktop. Ele é focado em prover uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua privacidade. Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar privacidade—características relacionadas às notificações de leitura, recebimento e escrita. Dino obtém o histórico do servidor e sincroniza mensagens com outros aparelhos. Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej obsłudze sieci Jabber/XMPP dbając o twoją prywatność. Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o pisaniu czy odczytaniu wiadomości. Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi urządzeniami. Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte vòstra confidencialitat. Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de configurar de foncionalitats ligadas amb la confidencialitat coma los acusats de lectura e las notificacions d’escritura. Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres periferics. Dino is een moderne, vrije chattoepassing voor uw bureaublad. Ze biedt een eenvoudige en betrouwbare Jabber/XMPP-ervaring, met uw privacy in het achterhoofd. Ze ondersteunt eind-tot-eind-versleuteling met OMEMO en OpenPGP, en laat u toe privacygerelateerde functies, gelijk leesbevestigingen en typmeldingen, in te stellen. Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten. Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het achterhoofd. Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-typenmeldingen, in te stellen. Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten. Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om personvernet. Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og skrivevarsling. Dino henter historikk fra tjeneren og synkroniserer meldinger med andre enheter. Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą nepamirštant apie jūsų privatumą. Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie žinučių skaitymą ir rašymą. Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais įrenginiais. Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op Privatsphär. Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen. Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat anere Geräter. Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プライバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供を第一に考えて開発されています。 OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送信や入力通知などのプライバシー関連の設定も可能です。 Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。 Dino è un client di chat per il desktop, moderno e open-source. Si concentra nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente la tua privacy. Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di configurare le funzioni relative alla privacy come le ricevute di lettura e le notifiche di digitazione. Dino recupera la cronologia dal server e sincronizza i messaggi con gli altri dispositivi. Dino es un modern cliente de conversationes con fonte apert. It foca se sur provider un nett e fidibil experientie de Jabber/XMPP con un attention a confidentialitá. It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar sensitiv functiones quam confirmation de lectada e notificationes de tippada. Dino obtene li diarium del servitore e sincronisa missages inter altri apparates. Dino adalah aplikasi chat open source modern untuk PC. Menyediakan pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda. Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan pengaturan fitur terkait privasi seperti tanda pesan dibaca dan pemberitahuan pengetikan. Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan perangkat lain. A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, miközben a magánszféra megőrzését is fontosnak tartja. Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint például az olvasási visszaigazolást és a gépelési értesítéseket. A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az üzeneteket a többi eszközzel. Dino é un cliente moderno e de código aberto para o escritorio. Orientado a fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e seguranza presentes. Suporta o cifrado de punto-a-punto con OMEMO e OpenPGP e permite configurar trazos orientados á privacidade tales coma confirmación de lectura e notificacións de escritura. Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros dispositivos. Dino est un client de clavardage libre et moderne pour le bureau. Il se concentre sur la fourniture d’une expérience XMPP simple et fiable tout en ayant toujours à l’esprit votre confidentialité. Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et permet de configurer les fonctions liées à la confidentialité telles que les accusés de réception et les notifications d’écriture. Dino récupère l’historique du serveur et synchronise les messages avec les autres clients. Dino on nykyaikainen avoimen lähdekoodin jutteluohjelma työpöydälle. Se keskittyy tarjoamaan selkeän ja luotettavan Jabber/XMPP-kokemuksen unohtamatta yksityisyyttäsi. Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja kirjoitusilmoitusten asetusten määrittämisen. Dino hakee historian palvelimelta ja synkronisoi viestit muiden laitteiden kanssa. دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد. از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و اعلان در حال نوشتن بودن. دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی می‌کند. Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan hartzeaz gain. Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta idazketa jakinarazpenak bezala. Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak sinkronizatzen ditu. Dino es un cliente de mensajería moderno y libre para escritorio y móvil. Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo la privacidad en mente. Soporta encriptación fin-a-fin a través de OMEMO y OpenPGP y permite configurar las características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura. Dino recupera el historial de mensajes desde el servidor y sincroniza los mensajes con otros dispositivos. Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian privatecon. Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado. Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj aparatoj. Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen Schwerpunkt auf Privatsphäre. Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und enthält Privatsphäre-Einstellungen zu Lesebestätigungen und Tippbenachrichtigungen. Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit anderen Geräten. Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na zachování vašeho soukromí. Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení a oznámení o psaní. Dino načítá historii ze serveru a synchronizuje zprávy s ostatními zařízeními. Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en compte la vostra privacitat. Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar funcionalitats relacionades amb la privacitat com per exemple rebuts de lectura i notificacions d'escriptura. Dino recupera l'historial del servidor i sincronitza els missatges amb altres dispositius. إنّ Dino برنامج عصري ومفتوح المصدر للدردشة صُمّم لسطح المكتب. ويُركّز علي تقديم تجربة نظيفة وموثوق منها لجابر/XMPP مع أخذ خصوصيتكم بعين الإعتبار. وهو يدعم التشفير بواسطة OMEMO و OpenPGP يسمح بإعداد ميزات الخصوصية كالإيصالات المقروءة والإخطارات عند الكتابة. يقوم Dino بجلب السِجلّ مِن السيرفر ثم يُزامِن الرسائل مع الأجهزة الأخرى. Vala Linux FreeBSD complete 0.1 complete 0.1 complete 0.1 partial 0.1 complete For use with XEP-0261 0.1 deprecated Migrating to XEP-0402 if supported by server 0.1 complete 0.1 partial Only for viewing avatars 0.1 partial For use with XEP-0313 0.1 partial 0.1 partial For use with XEP-0260 0.1 complete For file transfers using XEP-0363 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 deprecated 0.1 Only to fetch Avatars from other users complete 0.1 partial 0.1 partial 0.3 partial 0.3 complete 0.3 complete 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 partial 0.3 complete 0.1 complete 0.1 partial 0.1 1.0 complete 0.1 partial No support for sending 0.3 complete 1.0.3 0.1 complete 1.0 0.1 partial 0.2.0 0.3 complete 1.0.1 0.1 partial 1.0.2 0.3 partial 1.1.2 0.3 partial 1.0 0.1 partial 0.3 complete 1.2.0 0.2 partial 0.1 complete 1.0.0 0.3 complete 0.1 partial 0.1 complete 0.3.1 0.3 complete 0.1 complete 0.1 partial 0.3 0.5 complete 1.1.0 0.1 partial Only for outgoing messages 0.1 complete 0.3.0 0.1 partial 0.1.2 0.1 complete 1.0.0 0.5 partial 1.1.1 0.1 partial 0.3.0 0.5 complete 0.1 complete 0.1 complete 1.2.0 0.1 complete 0.2 complete 0.2.0 0.4 complete 0.3.0 partial 0.2.1 complete 0.1.1 0.4 partial 0.2.0 0.5 partial 0.3.0 0.5 complete 0.1.2 partial No support for embedded thumbnails 0.1 complete 0.2.0 0.4 partial 0.1.0 0.5 partial 0.1.0 dino-0.5.0/dino.doap.in0000664000000000000000000005264214776241610013371 0ustar rootroot Dino dino Modern XMPP chat client Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind. It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications. Dino fetches history from the server and synchronizes messages with other devices. Vala Linux FreeBSD complete 0.1 complete 0.1 complete 0.1 partial 0.1 complete For use with XEP-0261 0.1 deprecated Migrating to XEP-0402 if supported by server 0.1 complete 0.1 partial Only for viewing avatars 0.1 partial For use with XEP-0313 0.1 partial 0.1 partial For use with XEP-0260 0.1 complete For file transfers using XEP-0363 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 deprecated 0.1 Only to fetch Avatars from other users complete 0.1 partial 0.1 partial 0.3 partial 0.3 complete 0.3 complete 0.1 complete 0.1 complete 0.1 complete 0.1 complete 0.1 partial 0.3 complete 0.1 complete 0.1 partial 0.1 1.0 complete 0.1 partial No support for sending 0.3 complete 1.0.3 0.1 complete 1.0 0.1 partial 0.2.0 0.3 complete 1.0.1 0.1 partial 1.0.2 0.3 partial 1.1.2 0.3 partial 1.0 0.1 partial 0.3 complete 1.2.0 0.2 partial 0.1 complete 1.0.0 0.3 complete 0.1 partial 0.1 complete 0.3.1 0.3 complete 0.1 complete 0.1 partial 0.3 0.5 complete 1.1.0 0.1 partial Only for outgoing messages 0.1 complete 0.3.0 0.1 partial 0.1.2 0.1 complete 1.0.0 0.5 partial 1.1.1 0.1 partial 0.3.0 0.5 complete 0.1 complete 0.1 complete 1.2.0 0.1 complete 0.2 complete 0.2.0 0.4 complete 0.3.0 partial 0.2.1 complete 0.1.1 0.4 partial 0.2.0 0.5 partial 0.3.0 0.5 complete 0.1.2 partial No support for embedded thumbnails 0.1 complete 0.2.0 0.4 partial 0.1.0 0.5 partial 0.1.0 dino-0.5.0/im.dino.Dino.json0000664000000000000000000000571314776241610014303 0ustar rootroot{ "id": "im.dino.Dino", "runtime": "org.gnome.Platform", "runtime-version": "48", "sdk": "org.gnome.Sdk", "command": "dino", "finish-args": [ "--share=ipc", "--socket=fallback-x11", "--socket=wayland", "--socket=pulseaudio", "--socket=gpg-agent", "--filesystem=xdg-run/pipewire-0", "--share=network", "--device=dri", "--talk-name=org.freedesktop.Notifications" ], "modules": [ { "name": "protobuf", "buildsystem": "cmake-ninja", "cleanup": [ "*" ], "config-opts": [ "-Dprotobuf_BUILD_TESTS=OFF", "-Dprotobuf_BUILD_LIBUPB=OFF" ], "sources": [ { "type": "git", "url": "https://github.com/protocolbuffers/protobuf.git", "tag": "v29.4" } ] }, { "name": "libprotobuf-c", "buildsystem": "autotools", "config-opts": [ "CFLAGS=-fPIC" ], "post-install": [ "rm /app/lib/*.so" ], "cleanup": [ "*" ], "sources": [ { "type": "git", "url": "https://github.com/protobuf-c/protobuf-c.git", "tag": "v1.5.1" } ] }, { "name": "libomemo-c", "buildsystem": "meson", "cleanup": [ "/lib/pkgconfig", "/include" ], "config-opts": [ "-Dtests=false", "-Ddefault_library=static" ], "sources": [ { "type": "git", "url": "https://github.com/dino/libomemo-c.git", "tag": "v0.5.1" } ] }, { "name": "qrencode", "buildsystem": "cmake-ninja", "cleanup": [ "*" ], "config-opts": [ "-DCMAKE_C_FLAGS=-fPIC" ], "sources": [ { "type": "archive", "url": "https://fukuchi.org/works/qrencode/qrencode-4.1.1.tar.gz", "sha512": "209bb656ae3f391b03c7b3ceb03e34f7320b0105babf48b619e7a299528b8828449e0e7696f0b5db0d99170a81709d0518e34835229a748701e7df784e58a9ce" } ] }, { "name": "dino", "buildsystem": "meson", "builddir": true, "cleanup": [ "/include", "/share/vala" ], "sources": [ { "type": "dir", "path": "." } ] } ] } dino-0.5.0/libdino/0000775000000000000000000000000014776241610012575 5ustar rootrootdino-0.5.0/libdino/dino.deps0000664000000000000000000000007414776241610014404 0ustar rootrootgdk-pixbuf-2.0 gee-0.8 glib-2.0 gmodule-2.0 qlite xmpp-vala dino-0.5.0/libdino/meson.build0000664000000000000000000000706014776241610014742 0ustar rootroot# version_vala dot_git = meson.current_source_dir() / '../.git' version_file = meson.current_source_dir() / '../VERSION' command = [prog_python, files('version.py'), version_file, '--git-repo', meson.current_source_dir()] if prog_git.found() command += ['--git', prog_git] endif version_vala = vcs_tag(command: command, input: 'src/version.vala.in', output: 'version.vala', replace_string: '%VERSION%') # libdino dependencies = [ dep_gdk_pixbuf, dep_gee, dep_gio, dep_glib, dep_gmodule, dep_qlite, dep_xmpp_vala ] sources = files( 'src/application.vala', 'src/dbus/login1.vala', 'src/dbus/notifications.vala', 'src/dbus/upower.vala', 'src/entity/account.vala', 'src/entity/call.vala', 'src/entity/conversation.vala', 'src/entity/encryption.vala', 'src/entity/file_transfer.vala', 'src/entity/message.vala', 'src/entity/settings.vala', 'src/plugin/interfaces.vala', 'src/plugin/loader.vala', 'src/plugin/registry.vala', 'src/service/avatar_manager.vala', 'src/service/blocking_manager.vala', 'src/service/call_store.vala', 'src/service/call_state.vala', 'src/service/call_peer_state.vala', 'src/service/calls.vala', 'src/service/chat_interaction.vala', 'src/service/connection_manager.vala', 'src/service/contact_model.vala', 'src/service/content_item_store.vala', 'src/service/conversation_manager.vala', 'src/service/counterpart_interaction_manager.vala', 'src/service/database.vala', 'src/service/entity_capabilities_storage.vala', 'src/service/entity_info.vala', 'src/service/fallback_body.vala', 'src/service/file_manager.vala', 'src/service/file_transfer_storage.vala', 'src/service/history_sync.vala', 'src/service/jingle_file_transfers.vala', 'src/service/message_correction.vala', 'src/service/message_processor.vala', 'src/service/message_storage.vala', 'src/service/module_manager.vala', 'src/service/muc_manager.vala', 'src/service/notification_events.vala', 'src/service/presence_manager.vala', 'src/service/replies.vala', 'src/service/reactions.vala', 'src/service/registration.vala', 'src/service/roster_manager.vala', 'src/service/search_processor.vala', 'src/service/sfs_metadata.vala', 'src/service/stateless_file_sharing.vala', 'src/service/stream_interactor.vala', 'src/service/util.vala', 'src/util/display_name.vala', 'src/util/limit_input_stream.vala', 'src/util/send_message.vala', 'src/util/util.vala', 'src/util/weak_map.vala', 'src/util/weak_timeout.vala', ) sources += [version_vala] c_args = [ '-DDINO_SYSTEM_LIBDIR_NAME="@0@"'.format(get_option('prefix') / get_option('libdir')), '-DDINO_SYSTEM_PLUGIN_DIR="@0@"'.format(get_option('prefix') / get_option('libdir') / get_option('plugindir')), '-DG_LOG_DOMAIN="libdino"', ] vala_args = [] if meson.get_compiler('vala').version().version_compare('=0.56.11') vala_args += ['-D', 'VALA_0_56_11'] endif lib_dino = library('libdino', sources, c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, name_prefix: '', version: '0.0', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath) dep_dino = declare_dependency(link_with: lib_dino, include_directories: include_directories('.', 'src')) install_data('dino.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 install_headers('src/dino_i18n.h') dino-0.5.0/libdino/src/0000775000000000000000000000000014776241610013364 5ustar rootrootdino-0.5.0/libdino/src/application.vala0000664000000000000000000001402314776241610016534 0ustar rootrootusing Dino.Entities; namespace Dino { public string get_version() { return VERSION; } public string get_short_version() { if (!VERSION.contains("~")) return VERSION; return VERSION.split("~")[0] + "+"; } public interface Application : GLib.Application { public abstract Database db { get; set; } public abstract Dino.Entities.Settings settings { get; set; } public abstract StreamInteractor stream_interactor { get; set; } public abstract Plugins.Registry plugin_registry { get; set; } public abstract SearchPathGenerator? search_path_generator { get; set; } internal static string print_xmpp; private const OptionEntry[] options = { { "print-xmpp", 0, 0, OptionArg.STRING, ref print_xmpp, "Print XMPP stanzas identified by DESC to stderr", "DESC" }, { null } }; public abstract void handle_uri(string jid, string query, Gee.Map options); public void init() throws Error { if (DirUtils.create_with_parents(get_storage_dir(), 0700) == -1) { throw new Error(-1, 0, "Could not create storage dir \"%s\": %s", get_storage_dir(), FileUtils.error_from_errno(errno).to_string()); } this.db = new Database(Path.build_filename(get_storage_dir(), "dino.db")); this.settings = new Dino.Entities.Settings.from_db(db); this.stream_interactor = new StreamInteractor(db); MessageProcessor.start(stream_interactor, db); MessageStorage.start(stream_interactor, db); PresenceManager.start(stream_interactor); CounterpartInteractionManager.start(stream_interactor); BlockingManager.start(stream_interactor); Calls.start(stream_interactor, db); ConversationManager.start(stream_interactor, db); MucManager.start(stream_interactor); AvatarManager.start(stream_interactor, db); RosterManager.start(stream_interactor, db); FileManager.start(stream_interactor, db); CallStore.start(stream_interactor, db); ContentItemStore.start(stream_interactor, db); ChatInteraction.start(stream_interactor); NotificationEvents.start(stream_interactor); SearchProcessor.start(stream_interactor, db); Register.start(stream_interactor, db); EntityInfo.start(stream_interactor, db); MessageCorrection.start(stream_interactor, db); FileTransferStorage.start(stream_interactor, db); Reactions.start(stream_interactor, db); Replies.start(stream_interactor, db); FallbackBody.start(stream_interactor, db); ContactModels.start(stream_interactor); StatelessFileSharing.start(stream_interactor, db); create_actions(); startup.connect(() => { stream_interactor.connection_manager.log_options = print_xmpp; restore(); }); shutdown.connect(() => { stream_interactor.connection_manager.make_offline_all(); }); open.connect((files, hint) => { if (files.length != 1) { warning("Can't handle more than one URI at once."); return; } File file = files[0]; if (!file.has_uri_scheme("xmpp")) { warning("xmpp:-URI expected"); return; } string uri = file.get_uri(); if (!uri.contains(":")) { warning("Invalid URI"); return; } string r = uri.split(":", 2)[1]; string[] m = r.split("?", 2); string jid = m[0]; while (jid[0] == '/') { jid = jid.substring(1); } jid = Uri.unescape_string(jid); try { jid = new Xmpp.Jid(jid).to_string(); } catch (Xmpp.InvalidJidError e) { warning("Received invalid jid in xmpp:-URI: %s", e.message); } string query = "message"; Gee.Map options = new Gee.HashMap(); if (m.length == 2) { string[] cmds = m[1].split(";"); query = cmds[0]; for (int i = 1; i < cmds.length; ++i) { string[] opt = cmds[i].split("=", 2); options[Uri.unescape_string(opt[0])] = opt.length == 2 ? Uri.unescape_string(opt[1]) : ""; } } activate(); handle_uri(jid, query, options); }); add_main_option_entries(options); } public static string get_storage_dir() { return Path.build_filename(Environment.get_user_data_dir(), "dino"); } public static unowned Application get_default() { return (Dino.Application) GLib.Application.get_default(); } public void create_actions() { SimpleAction accept_subscription_action = new SimpleAction("accept-subscription", VariantType.INT32); accept_subscription_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; stream_interactor.get_module(PresenceManager.IDENTITY).approve_subscription(conversation.account, conversation.counterpart); stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(conversation.account, conversation.counterpart); }); add_action(accept_subscription_action); } protected void add_connection(Account account) { if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) hold(); stream_interactor.connect_account(account); } protected void remove_connection(Account account) { if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) release(); stream_interactor.disconnect_account.begin(account); } private void restore() { foreach (Account account in db.get_accounts()) { if (account.enabled) add_connection(account); } } } } dino-0.5.0/libdino/src/dbus/0000775000000000000000000000000014776241610014321 5ustar rootrootdino-0.5.0/libdino/src/dbus/login1.vala0000664000000000000000000000065214776241610016362 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.login1.Manager")] public interface Login1Manager : Object { public signal void PrepareForSleep(bool suspend); } public static async Login1Manager? get_login1() { try { return yield Bus.get_proxy(BusType.SYSTEM, "org.freedesktop.login1", "/org/freedesktop/login1"); } catch (IOError e) { stderr.printf("%s\n", e.message); } return null; } }dino-0.5.0/libdino/src/dbus/notifications.vala0000664000000000000000000000237514776241610020046 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.Notifications")] public interface DBusNotifications : GLib.Object { public signal void action_invoked(uint32 key, string action_key); public signal void notification_closed (uint32 id, uint32 reason); public abstract async uint32 notify(string app_name, uint32 replaces_id, string app_icon, string summary, string body, string[] actions, HashTable hints, int32 expire_timeout) throws DBusError, IOError; public abstract async void get_capabilities(out string[] capabilities) throws Error; public abstract async void close_notification(uint id) throws DBusError, IOError; public abstract async void get_server_information(out string name, out string vendor, out string version, out string spec_version) throws DBusError, IOError; } public static async DBusNotifications? get_notifications_dbus() { try { return yield Bus.get_proxy(BusType.SESSION, "org.freedesktop.Notifications", "/org/freedesktop/Notifications"); } catch (IOError e) { warning("Couldn't get org.freedesktop.Notifications DBus instance: %s\n", e.message); } return null; } }dino-0.5.0/libdino/src/dbus/upower.vala0000664000000000000000000000067514776241610016517 0ustar rootrootnamespace Dino { [DBus (name = "org.freedesktop.UPower")] public interface UPower : Object { public signal void Sleeping(); public signal void Resuming(); } public static UPower? get_upower() { UPower? upower = null; try { upower = Bus.get_proxy_sync(BusType.SYSTEM, "org.freedesktop.UPower", "/org/freedesktop/UPower"); } catch (IOError e) { stderr.printf ("%s\n", e.message); } return upower; } }dino-0.5.0/libdino/src/dino_i18n.h0000664000000000000000000000040514776241610015324 0ustar rootroot#ifndef __DINO_I18N_H__ #define __DINO_I18N_H__ #include #define dino_gettext(MsgId) ((char *) dgettext (GETTEXT_PACKAGE, MsgId)) #define dino_ngettext(MsgId, MsgIdPlural, Int) ((char *) dngettext (GETTEXT_PACKAGE, MsgId, MsgIdPlural, Int)) #endifdino-0.5.0/libdino/src/entity/0000775000000000000000000000000014776241610014700 5ustar rootrootdino-0.5.0/libdino/src/entity/account.vala0000664000000000000000000000723214776241610017205 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Entities { public class Account : Object { public int id { get; set; } public string localpart { get { return full_jid.localpart; } } public string domainpart { get { return full_jid.domainpart; } } public string resourcepart { get { return full_jid.resourcepart; } private set { full_jid.resourcepart = value; } } public Jid bare_jid { owned get { return full_jid.bare_jid; } } public Jid full_jid { get; private set; } public string? password { get; set; } public string display_name { owned get { return (alias != null && alias.length > 0) ? alias.dup() : bare_jid.to_string(); } } public string? alias { get; set; } public bool enabled { get; set; default = false; } public string? roster_version { get; set; } private Database? db; public Account(Jid bare_jid, string password) { this.id = -1; try { this.full_jid = bare_jid.with_resource(get_random_resource()); } catch (InvalidJidError e) { error("Auto-generated resource was invalid (%s)", e.message); } this.password = password; } public Account.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.account.id]; full_jid = new Jid(row[db.account.bare_jid]).with_resource(row[db.account.resourcepart]); password = row[db.account.password]; alias = row[db.account.alias]; enabled = row[db.account.enabled]; roster_version = row[db.account.roster_version]; notify.connect(on_update); } public void persist(Database db) { if (id > 0) return; this.db = db; id = (int) db.account.insert() .value(db.account.bare_jid, bare_jid.to_string()) .value(db.account.resourcepart, resourcepart) .value(db.account.password, password) .value(db.account.alias, alias) .value(db.account.enabled, enabled) .value(db.account.roster_version, roster_version) .perform(); notify.connect(on_update); } public void remove() { db.account.delete().with(db.account.bare_jid, "=", bare_jid.to_string()).perform(); notify.disconnect(on_update); id = -1; db = null; } public void set_random_resource() { this.resourcepart = get_random_resource(); } private static string get_random_resource() { return "dino." + Random.next_int().to_string("%x"); } public bool equals(Account acc) { return equals_func(this, acc); } public static bool equals_func(Account acc1, Account acc2) { return acc1.bare_jid.to_string() == acc2.bare_jid.to_string(); } public static uint hash_func(Account acc) { return acc.bare_jid.to_string().hash(); } private void on_update(Object o, ParamSpec sp) { var update = db.account.update().with(db.account.id, "=", id); switch (sp.name) { case "bare-jid": update.set(db.account.bare_jid, bare_jid.to_string()); break; case "resourcepart": update.set(db.account.resourcepart, resourcepart); break; case "password": update.set(db.account.password, password); break; case "alias": update.set(db.account.alias, alias); break; case "enabled": update.set(db.account.enabled, enabled); break; case "roster-version": update.set(db.account.roster_version, roster_version); break; } update.perform(); } } } dino-0.5.0/libdino/src/entity/call.vala0000664000000000000000000001523214776241610016463 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class Call : Object { public const bool DIRECTION_OUTGOING = true; public const bool DIRECTION_INCOMING = false; public enum State { RINGING, ESTABLISHING, IN_PROGRESS, OTHER_DEVICE, ENDED, DECLINED, MISSED, FAILED } public int id { get; set; default=-1; } public Account account { get; set; } public Jid counterpart { get; set; } public Gee.List counterparts = new Gee.ArrayList(Jid.equals_bare_func); public Jid ourpart { get; set; } public Jid proposer { get { return direction == DIRECTION_OUTGOING ? ourpart : counterpart; } } public bool direction { get; set; } public DateTime time { get; set; } public DateTime local_time { get; set; } public DateTime end_time { get; set; } public Encryption encryption { get; set; default=Encryption.NONE; } public State state { get; set; } private Database? db; public Call.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.call.id]; account = db.get_account_by_id(row[db.call.account_id]); string our_resource = row[db.call.our_resource]; if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.call.direction]; time = new DateTime.from_unix_utc(row[db.call.time]); local_time = new DateTime.from_unix_utc(row[db.call.local_time]); end_time = new DateTime.from_unix_utc(row[db.call.end_time]); encryption = (Encryption) row[db.call.encryption]; state = (State) row[db.call.state]; Qlite.QueryBuilder counterparts_select = db.call_counterpart.select().with(db.call_counterpart.call_id, "=", id); foreach (Qlite.Row counterparts_row in counterparts_select) { Jid peer = db.get_jid_by_id(counterparts_row[db.call_counterpart.jid_id]); if (!counterparts.contains(peer)) { // Legacy: The first peer is also in the `call` table. Don't add twice. counterparts.add(peer); } } counterpart = db.get_jid_by_id(row[db.call.counterpart_id]); string counterpart_resource = row[db.call.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); if (counterparts.is_empty) { counterparts.add(counterpart); } notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.call.insert() .value(db.call.account_id, account.id) .value(db.call.our_resource, ourpart.resourcepart) .value(db.call.direction, direction) .value(db.call.time, (long) time.to_unix()) .value(db.call.local_time, (long) local_time.to_unix()) .value(db.call.encryption, encryption) .value(db.call.state, State.ENDED); // No point in persisting states that can't survive a restart if (end_time != null) { builder.value(db.call.end_time, (long) end_time.to_unix()); } else { builder.value(db.call.end_time, (long) local_time.to_unix()); } if (counterpart != null) { builder.value(db.call.counterpart_id, db.get_jid_id(counterpart)) .value(db.call.counterpart_resource, counterpart.resourcepart); } id = (int) builder.perform(); foreach (Jid peer in counterparts) { db.call_counterpart.insert() .value(db.call_counterpart.call_id, id) .value(db.call_counterpart.jid_id, db.get_jid_id(peer)) .value(db.call_counterpart.resource, peer.resourcepart) .perform(); } notify.connect(on_update); } public void add_peer(Jid peer) { if (counterparts.contains(peer)) return; counterparts.add(peer); if (db != null) { db.call_counterpart.insert() .value(db.call_counterpart.call_id, id) .value(db.call_counterpart.jid_id, db.get_jid_id(peer)) .value(db.call_counterpart.resource, peer.resourcepart) .perform(); } } public bool equals(Call c) { return equals_func(this, c); } public static bool equals_func(Call c1, Call c2) { if (c1.id == c2.id) { return true; } return false; } public static uint hash_func(Call call) { return (uint)call.id; } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.call.update().with(db.call.id, "=", id); switch (sp.name) { case "counterpart": update_builder.set(db.call.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.call.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.call.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.call.direction, direction); break; case "time": update_builder.set(db.call.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.call.local_time, (long) local_time.to_unix()); break; case "end-time": update_builder.set(db.call.end_time, (long) end_time.to_unix()); break; case "encryption": update_builder.set(db.call.encryption, encryption); break; case "state": // No point in persisting states that can't survive a restart if (state == State.RINGING || state == State.ESTABLISHING || state == State.IN_PROGRESS) return; update_builder.set(db.call.state, state); break; } update_builder.perform(); } } } dino-0.5.0/libdino/src/entity/conversation.vala0000664000000000000000000002116614776241610020265 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class Conversation : Object { public signal void object_updated(Conversation conversation); public enum Type { CHAT, GROUPCHAT, GROUPCHAT_PM; public bool is_muc_semantic() { return this == GROUPCHAT || this == GROUPCHAT_PM; } } public int id { get; set; } public Type type_ { get; set; } public Account account { get; private set; } public Jid counterpart { get; private set; } public string? nickname { get; set; } public bool active { get; set; default = false; } public DateTime active_last_changed { get; private set; } private DateTime? _last_active; public DateTime? last_active { get { return _last_active; } set { if (_last_active == null || (value != null && value.difference(_last_active) > 0)) { _last_active = value; } } } public Encryption encryption { get; set; default = Encryption.UNKNOWN; } public Message? read_up_to { get; set; } public int read_up_to_item { get; set; default=-1; } public enum NotifySetting { DEFAULT, ON, OFF, HIGHLIGHT } public NotifySetting notify_setting { get; set; default = NotifySetting.DEFAULT; } public enum Setting { DEFAULT, ON, OFF } public Setting send_typing { get; set; default = Setting.DEFAULT; } public Setting send_marker { get; set; default = Setting.DEFAULT; } public int pinned { get; set; default = 0; } private Database? db; public Conversation(Jid jid, Account account, Type type) { this.account = account; this.counterpart = jid; this.type_ = type; } public Conversation.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.conversation.id]; type_ = (Conversation.Type) row[db.conversation.type_]; account = db.get_account_by_id(row[db.conversation.account_id]); string? resource = row[db.conversation.resource]; counterpart = db.get_jid_by_id(row[db.conversation.jid_id]); if (type_ == Conversation.Type.GROUPCHAT_PM) counterpart = counterpart.with_resource(resource); nickname = type_ == Conversation.Type.GROUPCHAT ? resource : null; active = row[db.conversation.active]; active_last_changed = new DateTime.from_unix_utc(row[db.conversation.active_last_changed]); int64? last_active = row[db.conversation.last_active]; if (last_active != null) this.last_active = new DateTime.from_unix_utc(last_active); encryption = (Encryption) row[db.conversation.encryption]; int? read_up_to = row[db.conversation.read_up_to]; if (read_up_to != null) this.read_up_to = db.get_message_by_id(read_up_to); read_up_to_item = row[db.conversation.read_up_to_item]; notify_setting = (NotifySetting) row[db.conversation.notification]; send_typing = (Setting) row[db.conversation.send_typing]; send_marker = (Setting) row[db.conversation.send_marker]; pinned = row[db.conversation.pinned]; notify.connect(on_update); } public void persist(Database db) { this.db = db; this.active_last_changed = new DateTime.now_utc(); var insert = db.conversation.insert() .value(db.conversation.account_id, account.id) .value(db.conversation.jid_id, db.get_jid_id(counterpart)) .value(db.conversation.type_, type_) .value(db.conversation.encryption, encryption) .value(db.conversation.active, active) .value(db.conversation.active_last_changed, (long) active_last_changed.to_unix()) .value(db.conversation.notification, notify_setting) .value(db.conversation.send_typing, send_typing) .value(db.conversation.send_marker, send_marker) .value(db.conversation.pinned, pinned); if (read_up_to != null) { insert.value(db.conversation.read_up_to, read_up_to.id); } if (read_up_to_item != -1) { insert.value(db.conversation.read_up_to_item, read_up_to_item); } if (nickname != null) { insert.value(db.conversation.resource, nickname); } if (counterpart.is_full()) { insert.value(db.conversation.resource, counterpart.resourcepart); } if (last_active != null) { insert.value(db.conversation.last_active, (long) last_active.to_unix()); } id = (int) insert.perform(); notify.connect(on_update); } public NotifySetting get_notification_setting(StreamInteractor stream_interactor) { return notify_setting != NotifySetting.DEFAULT ? notify_setting : get_notification_default_setting(stream_interactor); } public NotifySetting get_notification_default_setting(StreamInteractor stream_interactor) { if (!Application.get_default().settings.notifications) return NotifySetting.OFF; if (type_ == Type.GROUPCHAT) { if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(this.account, this.counterpart)) { return NotifySetting.ON; } else { return NotifySetting.HIGHLIGHT; } } return NotifySetting.ON; } public Setting get_send_typing_setting(StreamInteractor stream_interactor) { if (send_typing != Setting.DEFAULT) return send_typing; if (stream_interactor.get_module(MucManager.IDENTITY).is_public_room(this.account, this.counterpart)) return Setting.OFF; return Application.get_default().settings.send_typing ? Setting.ON : Setting.OFF; } public Setting get_send_marker_setting(StreamInteractor stream_interactor) { if (send_marker != Setting.DEFAULT) return send_marker; if (stream_interactor.get_module(MucManager.IDENTITY).is_public_room(this.account, this.counterpart)) return Setting.OFF; return Application.get_default().settings.send_marker ? Setting.ON : Setting.OFF; } public bool equals(Conversation? conversation) { if (conversation == null) return false; return equals_func(this, conversation); } public static bool equals_func(Conversation conversation1, Conversation conversation2) { return conversation1.counterpart.equals(conversation2.counterpart) && conversation1.account.equals(conversation2.account) && conversation1.type_ == conversation2.type_; } public static uint hash_func(Conversation conversation) { return conversation.counterpart.to_string().hash() ^ conversation.account.bare_jid.to_string().hash(); } private void on_update(Object o, ParamSpec sp) { var update = db.conversation.update().with(db.conversation.id, "=", id); switch (sp.name) { case "type-": update.set(db.conversation.type_, type_); break; case "encryption": update.set(db.conversation.encryption, encryption); break; case "read-up-to": if (read_up_to != null) { update.set(db.conversation.read_up_to, read_up_to.id); } else { update.set_null(db.conversation.read_up_to); } break; case "read-up-to-item": if (read_up_to_item != -1) { update.set(db.conversation.read_up_to_item, read_up_to_item); } else { update.set_null(db.conversation.read_up_to_item); } break; case "nickname": update.set(db.conversation.resource, nickname); break; case "active": update.set(db.conversation.active, active); update.set(db.conversation.active_last_changed, (long) new DateTime.now_utc().to_unix()); break; case "last-active": if (last_active != null) { update.set(db.conversation.last_active, (long) last_active.to_unix()); } else { update.set_null(db.conversation.last_active); } break; case "notify-setting": update.set(db.conversation.notification, notify_setting); break; case "send-typing": update.set(db.conversation.send_typing, send_typing); break; case "send-marker": update.set(db.conversation.send_marker, send_marker); break; case "pinned": update.set(db.conversation.pinned, pinned); break; } update.perform(); } } } dino-0.5.0/libdino/src/entity/encryption.vala0000664000000000000000000000035014776241610017735 0ustar rootrootnamespace Dino.Entities { public enum Encryption { NONE, PGP, OMEMO, DTLS_SRTP, SRTP, UNKNOWN; public bool is_some() { return this != NONE; } } }dino-0.5.0/libdino/src/entity/file_transfer.vala0000664000000000000000000003157314776241610020401 0ustar rootrootusing Xmpp; namespace Dino.Entities { public class FileTransfer : Object { public signal void sources_changed(); public const bool DIRECTION_SENT = true; public const bool DIRECTION_RECEIVED = false; public enum State { COMPLETE, IN_PROGRESS, NOT_STARTED, FAILED } public int id { get; set; default=-1; } public string? file_sharing_id { get; set; } public Account account { get; set; } public Jid counterpart { get; set; } public Jid ourpart { get; set; } public Jid? from { get { return direction == DIRECTION_SENT ? ourpart : counterpart; } } public Jid? to { get { return direction == DIRECTION_SENT ? counterpart : ourpart; } } public bool direction { get; set; } public DateTime time { get; set; } public DateTime? local_time { get; set; } public Encryption encryption { get; set; default=Encryption.NONE; } private InputStream? input_stream_ = null; public InputStream input_stream { get { if (input_stream_ == null) { File file = File.new_for_path(Path.build_filename(storage_dir, path ?? file_name)); try { input_stream_ = file.read(); } catch (Error e) { } } return input_stream_; } set { input_stream_ = value; } } private string file_name_; public string file_name { get { return file_name_; } set { file_name_ = Path.get_basename(value); if (file_name_ == Path.DIR_SEPARATOR_S || file_name_ == ".") { file_name_ = "unknown filename"; } else if (file_name_.has_prefix(".")) { file_name_ = "_" + file_name_; } } } private string? server_file_name_ = null; public string server_file_name { get { return server_file_name_ ?? file_name; } set { server_file_name_ = value; } } public string path { get; set; } public string? mime_type { get; set; } public int64 size { get; set; } public State state { get; set; default=State.NOT_STARTED; } public int provider { get; set; } public string info { get; set; } public Cancellable cancellable { get; default=new Cancellable(); } // This value is not persisted public int64 transferred_bytes { get; set; } public Xep.FileMetadataElement.FileMetadata file_metadata { owned get { return new Xep.FileMetadataElement.FileMetadata() { name = this.file_name, mime_type = this.mime_type, size = this.size, desc = this.desc, date = this.modification_date, width = this.width, height = this.height, length = this.length, hashes = this.hashes, thumbnails = this.thumbnails }; } set { this.file_name = value.name; this.mime_type = value.mime_type; this.size = value.size; this.desc = value.desc; this.modification_date = value.date; this.width = value.width; this.height = value.height; this.length = value.length; this.hashes = value.hashes; this.thumbnails = value.thumbnails; } } public string? desc { get; set; } public DateTime? modification_date { get; set; } public int width { get; set; default=-1; } public int height { get; set; default=-1; } public int64 length { get; set; default=-1; } public Gee.List hashes = new Gee.ArrayList(); public Gee.List sfs_sources = new Gee.ArrayList(Xep.StatelessFileSharing.Source.equals_func); public Gee.List thumbnails = new Gee.ArrayList(); private Database? db; private string storage_dir; public FileTransfer.from_row(Database db, Qlite.Row row, string storage_dir) throws InvalidJidError { this.db = db; this.storage_dir = storage_dir; id = row[db.file_transfer.id]; file_sharing_id = row[db.file_transfer.file_sharing_id]; account = db.get_account_by_id(row[db.file_transfer.account_id]); // TODO don’t have to generate acc new counterpart = db.get_jid_by_id(row[db.file_transfer.counterpart_id]); string counterpart_resource = row[db.file_transfer.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); string our_resource = row[db.file_transfer.our_resource]; if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.file_transfer.direction]; time = new DateTime.from_unix_utc(row[db.file_transfer.time]); local_time = new DateTime.from_unix_utc(row[db.file_transfer.local_time]); encryption = (Encryption) row[db.file_transfer.encryption]; file_name = row[db.file_transfer.file_name]; path = row[db.file_transfer.path]; mime_type = row[db.file_transfer.mime_type]; size = (int64) row[db.file_transfer.size]; state = (State) row[db.file_transfer.state]; provider = row[db.file_transfer.provider]; info = row[db.file_transfer.info]; modification_date = new DateTime.from_unix_utc(row[db.file_transfer.modification_date]); width = row[db.file_transfer.width]; height = row[db.file_transfer.height]; length = (int64) row[db.file_transfer.length]; // TODO put those into the initial query foreach(var hash_row in db.file_hashes.select().with(db.file_hashes.id, "=", id)) { Xep.CryptographicHashes.Hash hash = new Xep.CryptographicHashes.Hash(); hash.algo = hash_row[db.file_hashes.algo]; hash.val = hash_row[db.file_hashes.value]; hashes.add(hash); } foreach(var thumbnail_row in db.file_thumbnails.select().with(db.file_thumbnails.id, "=", id)) { Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail(); thumbnail.uri = thumbnail_row[db.file_thumbnails.uri]; thumbnail.media_type = thumbnail_row[db.file_thumbnails.mime_type]; thumbnail.width = thumbnail_row[db.file_thumbnails.width]; thumbnail.height = thumbnail_row[db.file_thumbnails.height]; thumbnails.add(thumbnail); } foreach(Qlite.Row source_row in db.sfs_sources.select().with(db.sfs_sources.file_transfer_id, "=", id)) { if (source_row[db.sfs_sources.type] == "http") { sfs_sources.add(new Xep.StatelessFileSharing.HttpSource() { url=source_row[db.sfs_sources.data] }); } } notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.file_transfer.insert() .value(db.file_transfer.account_id, account.id) .value(db.file_transfer.counterpart_id, db.get_jid_id(counterpart)) .value(db.file_transfer.counterpart_resource, counterpart.resourcepart) .value(db.file_transfer.our_resource, ourpart.resourcepart) .value(db.file_transfer.direction, direction) .value(db.file_transfer.time, (long) time.to_unix()) .value(db.file_transfer.local_time, (long) local_time.to_unix()) .value(db.file_transfer.encryption, encryption) .value(db.file_transfer.file_name, file_name) .value(db.file_transfer.size, (long) size) .value(db.file_transfer.state, state) .value(db.file_transfer.provider, provider) .value(db.file_transfer.info, info); if (file_sharing_id != null) builder.value(db.file_transfer.file_sharing_id, file_sharing_id); if (path != null) builder.value(db.file_transfer.path, path); if (mime_type != null) builder.value(db.file_transfer.mime_type, mime_type); if (path != null) builder.value(db.file_transfer.path, path); if (modification_date != null) builder.value(db.file_transfer.modification_date, (long) modification_date.to_unix()); if (width != -1) builder.value(db.file_transfer.width, width); if (height != -1) builder.value(db.file_transfer.height, height); if (length != -1) builder.value(db.file_transfer.length, (long) length); id = (int) builder.perform(); foreach (Xep.CryptographicHashes.Hash hash in hashes) { db.file_hashes.insert() .value(db.file_hashes.id, id) .value(db.file_hashes.algo, hash.algo) .value(db.file_hashes.value, hash.val) .perform(); } foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in thumbnails) { db.file_thumbnails.insert() .value(db.file_thumbnails.id, id) .value(db.file_thumbnails.uri, thumbnail.uri) .value(db.file_thumbnails.mime_type, thumbnail.media_type) .value(db.file_thumbnails.width, thumbnail.width) .value(db.file_thumbnails.height, thumbnail.height) .perform(); } foreach (Xep.StatelessFileSharing.Source source in sfs_sources) { add_sfs_source(source); } notify.connect(on_update); } public void add_sfs_source(Xep.StatelessFileSharing.Source source) { if (sfs_sources.contains(source)) return; // Don't add the same source twice. Might happen due to MAM and lacking deduplication. sfs_sources.add(source); Xep.StatelessFileSharing.HttpSource? http_source = source as Xep.StatelessFileSharing.HttpSource; if (http_source != null) { db.sfs_sources.insert() .value(db.sfs_sources.file_transfer_id, id) .value(db.sfs_sources.type, "http") .value(db.sfs_sources.data, http_source.url) .perform(); } sources_changed(); } public File? get_file() { if (path == null) return null; return File.new_for_path(Path.build_filename(Dino.get_storage_dir(), "files", path)); } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.file_transfer.update().with(db.file_transfer.id, "=", id); switch (sp.name) { case "file-sharing-id": update_builder.set(db.file_transfer.file_sharing_id, file_sharing_id); break; case "counterpart": update_builder.set(db.file_transfer.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.file_transfer.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.file_transfer.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.file_transfer.direction, direction); break; case "time": update_builder.set(db.file_transfer.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.file_transfer.local_time, (long) local_time.to_unix()); break; case "encryption": update_builder.set(db.file_transfer.encryption, encryption); break; case "file-name": update_builder.set(db.file_transfer.file_name, file_name); break; case "path": update_builder.set(db.file_transfer.path, path); break; case "mime-type": update_builder.set(db.file_transfer.mime_type, mime_type); break; case "size": update_builder.set(db.file_transfer.size, (long) size); break; case "state": if (state == State.IN_PROGRESS) return; update_builder.set(db.file_transfer.state, state); break; case "provider": update_builder.set(db.file_transfer.provider, provider); break; case "info": update_builder.set(db.file_transfer.info, info); break; case "modification-date": update_builder.set(db.file_transfer.modification_date, (long) modification_date.to_unix()); break; case "width": update_builder.set(db.file_transfer.width, width); break; case "height": update_builder.set(db.file_transfer.height, height); break; case "length": update_builder.set(db.file_transfer.length, (long) length); break; } update_builder.perform(); } } } dino-0.5.0/libdino/src/entity/message.vala0000664000000000000000000002755114776241610017203 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Entities { public class Message : Object { public const bool DIRECTION_SENT = true; public const bool DIRECTION_RECEIVED = false; public enum Marked { NONE, RECEIVED, READ, ACKNOWLEDGED, UNSENT, WONTSEND, SENDING, SENT, ERROR } public static Marked[] MARKED_RECEIVED = new Marked[] { Marked.READ, Marked.RECEIVED, Marked.ACKNOWLEDGED }; public enum Type { ERROR, CHAT, GROUPCHAT, GROUPCHAT_PM, UNKNOWN; public bool is_muc_semantic() { return this == GROUPCHAT || this == GROUPCHAT_PM; } } public int id { get; set; default = -1; } public Account account { get; set; } public Jid? counterpart { get; set; } public Jid? ourpart { get; set; } public Jid? from { get { return direction == DIRECTION_SENT ? ourpart : counterpart; } } public Jid? to { get { return direction == DIRECTION_SENT ? counterpart : ourpart; } } public bool direction { get; set; } public Jid? real_jid { get; set; } public Type type_ { get; set; default = Type.UNKNOWN; } private string? body_; public string? body { get { return body_; } set { body_ = value != null ? value.make_valid() : null; } } public string? stanza_id { get; set; } public string? server_id { get; set; } public DateTime? time { get; set; } /** UTC **/ public DateTime? local_time { get; set; } public Encryption encryption { get; set; default = Encryption.NONE; } private Marked marked_ = Marked.NONE; public Marked marked { get { return marked_; } set { if (value == Marked.RECEIVED && marked == Marked.READ) return; marked_ = value; } } public string? edit_to = null; public int quoted_item_id { get; private set; default=0; } private Gee.List fallbacks = null; private Gee.List markups = null; private Database? db; public Message(string? body) { this.body = body; } public Message.from_row(Database db, Qlite.Row row) throws InvalidJidError { this.db = db; id = row[db.message.id]; account = db.get_account_by_id(row[db.message.account_id]); stanza_id = row[db.message.stanza_id]; server_id = row[db.message.server_id]; type_ = (Message.Type) row[db.message.type_]; counterpart = db.get_jid_by_id(row[db.message.counterpart_id]); string counterpart_resource = row[db.message.counterpart_resource]; if (counterpart_resource != null) counterpart = counterpart.with_resource(counterpart_resource); string our_resource = row[db.message.our_resource]; if (type_ == Type.GROUPCHAT && our_resource != null) { ourpart = counterpart.with_resource(our_resource); } else if (our_resource != null) { ourpart = account.bare_jid.with_resource(our_resource); } else { ourpart = account.bare_jid; } direction = row[db.message.direction]; time = new DateTime.from_unix_utc(row[db.message.time]); local_time = new DateTime.from_unix_utc(row[db.message.local_time]); body = row[db.message.body]; marked = (Message.Marked) row[db.message.marked]; encryption = (Encryption) row[db.message.encryption]; string? real_jid_str = row[db.real_jid.real_jid]; if (real_jid_str != null) real_jid = new Jid(real_jid_str); edit_to = row[db.message_correction.to_stanza_id]; quoted_item_id = row[db.reply.quoted_content_item_id]; notify.connect(on_update); } public void persist(Database db) { if (id != -1) return; this.db = db; Qlite.InsertBuilder builder = db.message.insert() .value(db.message.account_id, account.id) .value(db.message.counterpart_id, db.get_jid_id(counterpart)) .value(db.message.counterpart_resource, counterpart.resourcepart) .value(db.message.our_resource, ourpart.resourcepart) .value(db.message.direction, direction) .value(db.message.type_, type_) .value(db.message.time, (long) time.to_unix()) .value(db.message.local_time, (long) local_time.to_unix()) .value(db.message.body, body) .value(db.message.encryption, encryption) .value(db.message.marked, marked); if (stanza_id != null) builder.value(db.message.stanza_id, stanza_id); if (server_id != null) builder.value(db.message.server_id, server_id); id = (int) builder.perform(); if (real_jid != null) { db.real_jid.insert() .value(db.real_jid.message_id, id) .value(db.real_jid.real_jid, real_jid.to_string()) .perform(); } notify.connect(on_update); } public void set_quoted_item(int quoted_content_item_id) { if (id == -1) { warning("Message needs to be persisted before setting quoted item"); return; } this.quoted_item_id = quoted_content_item_id; db.reply.upsert() .value(db.reply.message_id, id, true) .value(db.reply.quoted_content_item_id, quoted_content_item_id) .value_null(db.reply.quoted_message_stanza_id) .value_null(db.reply.quoted_message_from) .perform(); } public Gee.List get_fallbacks() { if (fallbacks != null) return fallbacks; fetch_body_meta(); return fallbacks; } public Gee.List get_markups() { if (markups != null) return markups; fetch_body_meta(); return markups; } public void persist_markups(Gee.List markups, int message_id) { this.markups = markups; foreach (var span in markups) { foreach (var ty in span.types) { db.body_meta.insert() .value(db.body_meta.info_type, Xep.MessageMarkup.NS_URI) .value(db.body_meta.message_id, message_id) .value(db.body_meta.info, Xep.MessageMarkup.span_type_to_str(ty)) .value(db.body_meta.from_char, span.start_char) .value(db.body_meta.to_char, span.end_char) .perform(); } } } private void fetch_body_meta() { var fallbacks_by_ns = new HashMap>(); var markups = new ArrayList(); foreach (Qlite.Row row in db.body_meta.select().with(db.body_meta.message_id, "=", id)) { switch (row[db.body_meta.info_type]) { case Xep.FallbackIndication.NS_URI: string ns_uri = row[db.body_meta.info]; if (!fallbacks_by_ns.has_key(ns_uri)) { fallbacks_by_ns[ns_uri] = new ArrayList(); } fallbacks_by_ns[ns_uri].add(new Xep.FallbackIndication.FallbackLocation(row[db.body_meta.from_char], row[db.body_meta.to_char])); break; case Xep.MessageMarkup.NS_URI: var types = new ArrayList(); types.add(Xep.MessageMarkup.str_to_span_type(row[db.body_meta.info])); markups.add(new Xep.MessageMarkup.Span() { types=types, start_char=row[db.body_meta.from_char], end_char=row[db.body_meta.to_char] }); break; } } var fallbacks = new ArrayList(); foreach (string ns_uri in fallbacks_by_ns.keys) { fallbacks.add(new Xep.FallbackIndication.Fallback(ns_uri, fallbacks_by_ns[ns_uri].to_array())); } this.fallbacks = fallbacks; this.markups = markups; } public void set_fallbacks(Gee.List fallbacks) { if (id == -1) { warning("Message needs to be persisted before setting fallbacks"); return; } this.fallbacks = fallbacks; foreach (var fallback in fallbacks) { foreach (var location in fallback.locations) { db.body_meta.insert() .value(db.body_meta.message_id, id) .value(db.body_meta.info_type, Xep.FallbackIndication.NS_URI) .value(db.body_meta.info, fallback.ns_uri) .value(db.body_meta.from_char, location.from_char) .value(db.body_meta.to_char, location.to_char) .perform(); } } } public void set_type_string(string type) { switch (type) { case Xmpp.MessageStanza.TYPE_CHAT: type_ = Type.CHAT; break; case Xmpp.MessageStanza.TYPE_GROUPCHAT: type_ = Type.GROUPCHAT; break; } } public new string get_type_string() { switch (type_) { case Type.CHAT: return Xmpp.MessageStanza.TYPE_CHAT; case Type.GROUPCHAT: return Xmpp.MessageStanza.TYPE_GROUPCHAT; default: return Xmpp.MessageStanza.TYPE_NORMAL; } } public bool equals(Message? m) { if (m == null) return false; return equals_func(this, m); } public static bool equals_func(Message m1, Message m2) { if (m1.stanza_id == m2.stanza_id && m1.body == m2.body) { return true; } return false; } public static uint hash_func(Message message) { if (message.body == null) return 0; return message.body.hash(); } private void on_update(Object o, ParamSpec sp) { Qlite.UpdateBuilder update_builder = db.message.update().with(db.message.id, "=", id); switch (sp.name) { case "stanza-id": update_builder.set(db.message.stanza_id, stanza_id); break; case "server-id": update_builder.set(db.message.server_id, server_id); break; case "counterpart": update_builder.set(db.message.counterpart_id, db.get_jid_id(counterpart)); update_builder.set(db.message.counterpart_resource, counterpart.resourcepart); break; case "ourpart": update_builder.set(db.message.our_resource, ourpart.resourcepart); break; case "direction": update_builder.set(db.message.direction, direction); break; case "type-": update_builder.set(db.message.type_, type_); break; case "time": update_builder.set(db.message.time, (long) time.to_unix()); break; case "local-time": update_builder.set(db.message.local_time, (long) local_time.to_unix()); break; case "body": update_builder.set(db.message.body, body); break; case "encryption": update_builder.set(db.message.encryption, encryption); break; case "marked": update_builder.set(db.message.marked, marked); break; } update_builder.perform(); if (sp.get_name() == "real-jid") { db.real_jid.upsert() .value(db.real_jid.message_id, id, true) .value(db.real_jid.real_jid, real_jid.to_string()) .perform(); } if (sp.get_name() == "quoted-item-id") { db.reply.upsert() .value(db.reply.message_id, id, true) .value(db.reply.quoted_content_item_id, quoted_item_id) .perform(); } } } } dino-0.5.0/libdino/src/entity/settings.vala0000664000000000000000000000647714776241610017423 0ustar rootrootnamespace Dino.Entities { public class Settings : Object { private Database db; public Settings.from_db(Database db) { this.db = db; send_typing_ = col_to_bool_or_default("send_typing", true); send_marker_ = col_to_bool_or_default("send_marker", true); notifications_ = col_to_bool_or_default("notifications", true); convert_utf8_smileys_ = col_to_bool_or_default("convert_utf8_smileys", true); check_spelling = col_to_bool_or_default("check_spelling", true); } private bool col_to_bool_or_default(string key, bool def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? bool.parse(val) : def; } private bool send_typing_; public bool send_typing { get { return send_typing_; } set { db.settings.upsert() .value(db.settings.key, "send_typing", true) .value(db.settings.value, value.to_string()) .perform(); send_typing_ = value; } } private bool send_marker_; public bool send_marker { get { return send_marker_; } set { db.settings.upsert() .value(db.settings.key, "send_marker", true) .value(db.settings.value, value.to_string()) .perform(); send_marker_ = value; } } private bool notifications_; public bool notifications { get { return notifications_; } set { db.settings.upsert() .value(db.settings.key, "notifications", true) .value(db.settings.value, value.to_string()) .perform(); notifications_ = value; } } private bool convert_utf8_smileys_; public bool convert_utf8_smileys { get { return convert_utf8_smileys_; } set { db.settings.upsert() .value(db.settings.key, "convert_utf8_smileys", true) .value(db.settings.value, value.to_string()) .perform(); convert_utf8_smileys_ = value; } } // There is currently no spell checking for GTK4, thus there is currently no UI for this setting. private bool check_spelling_; public bool check_spelling { get { return check_spelling_; } set { db.settings.upsert() .value(db.settings.key, "check_spelling", true) .value(db.settings.value, value.to_string()) .perform(); check_spelling_ = value; } } public Encryption get_default_encryption(Account account) { string? setting = db.account_settings.get_value(account.id, "default-encryption"); if (setting != null) { return (Encryption) int.parse(setting); } return Encryption.OMEMO; } public void set_default_encryption(Account account, Encryption encryption) { db.account_settings.upsert() .value(db.account_settings.key, "default-encryption", true) .value(db.account_settings.account_id, account.id, true) .value(db.account_settings.value, ((int)encryption).to_string()) .perform(); } } } dino-0.5.0/libdino/src/plugin/0000775000000000000000000000000014776241610014662 5ustar rootrootdino-0.5.0/libdino/src/plugin/interfaces.vala0000664000000000000000000001700014776241610017650 0ustar rootrootusing Dino.Entities; using Xmpp; namespace Dino.Plugins { public enum Priority { LOWEST, LOWER, DEFAULT, HIGHER, HIGHEST } public enum WidgetType { GTK3, GTK4 } public interface RootInterface : Object { public abstract void registered(Dino.Application app); public abstract void shutdown(); } public interface EncryptionListEntry : Object { public abstract Entities.Encryption encryption { get; } public abstract string name { get; } public abstract void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus callback); public abstract Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item); public abstract string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item); } public interface CallEncryptionEntry : Object { public abstract CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption); } public interface CallEncryptionWidget : Object { public abstract string? get_title(); public abstract bool show_keys(); public abstract string? get_icon_name(); } public abstract class AccountSettingsEntry : Object { public abstract string id { get; } public virtual Priority priority { get { return Priority.DEFAULT; } } public abstract string name { get; } public virtual int16 label_top_padding { get { return -1; } } public abstract signal void activated(); public abstract void deactivate(); public abstract void set_account(Account account); public abstract Object? get_widget(WidgetType type); } public abstract class EncryptionPreferencesEntry : Object { public abstract string id { get; } public virtual Priority priority { get { return Priority.DEFAULT; } } public abstract Object? get_widget(Account account, WidgetType type); } public interface ContactDetailsProvider : Object { public abstract string id { get; } public abstract string tab { get; } public abstract void populate(Conversation conversation, ContactDetails contact_details, WidgetType type); public abstract Object? get_widget(Conversation conversation); } public class ContactDetails : Object { public signal void save(); public signal void add(string category, string label, string? desc, Object widget); } public interface TextCommand : Object { public abstract string cmd { get; } public abstract string? handle_command(string? text, Entities.Conversation? conversation); } public interface ConversationTitlebarEntry : Object { public abstract string id { get; } public abstract double order { get; } public abstract Object? get_widget(WidgetType type); public abstract void set_conversation(Conversation conversation); public abstract void unset_conversation(); } public abstract interface ConversationItemPopulator : Object { public abstract string id { get; } public abstract void init(Conversation conversation, ConversationItemCollection summary, WidgetType type); public abstract void close(Conversation conversation); } public abstract interface ConversationAdditionPopulator : ConversationItemPopulator { public virtual void populate_timespan(Conversation conversation, DateTime from, DateTime to) { } } public abstract interface VideoCallPlugin : Object { public abstract bool supported(); // Video widget public abstract VideoCallWidget? create_widget(WidgetType type); // Devices public signal void devices_changed(string media, bool incoming); public abstract Gee.List get_devices(string media, bool incoming); public abstract MediaDevice? get_preferred_device(string media, bool incoming); public abstract MediaDevice? get_device(Xmpp.Xep.JingleRtp.Stream? stream, bool incoming); public abstract void set_pause(Xmpp.Xep.JingleRtp.Stream? stream, bool pause); public abstract void set_device(Xmpp.Xep.JingleRtp.Stream? stream, MediaDevice? device); public abstract void dump_dot(); } public abstract interface VideoCallWidget : Object { public signal void resolution_changed(uint width, uint height); public abstract void display_stream(Xmpp.Xep.JingleRtp.Stream? stream, Jid jid); public abstract void display_device(MediaDevice device); public abstract void detach(); } public abstract interface MediaDevice : Object { public abstract string id { owned get; } public abstract string display_name { owned get; } public abstract string? detail_name { owned get; } public abstract string? media { owned get; } public abstract bool incoming { get; } } public abstract interface NotificationPopulator : Object { public abstract string id { get; } public abstract void init(Conversation conversation, NotificationCollection summary, WidgetType type); public abstract void close(Conversation conversation); } public abstract class MetaConversationItem : Object { public virtual string populator_id { get; set; } public virtual Jid? jid { get; set; default=null; } public virtual DateTime time { get; set; default = new DateTime.now_utc(); } public virtual int secondary_sort_indicator { get; set; } public virtual Encryption encryption { get; set; default = Encryption.NONE; } public virtual Entities.Message.Marked mark { get; set; default = Entities.Message.Marked.NONE; } public bool can_merge { get; set; default=false; } public bool requires_avatar { get; set; default=false; } public bool requires_header { get; set; default=false; } public bool in_edit_mode { get; set; default=false; } public abstract Object? get_widget(ConversationItemWidgetInterface outer, WidgetType type); public abstract Gee.List? get_item_actions(WidgetType type); } public interface ConversationItemWidgetInterface: Object { public abstract void set_widget(Object object, WidgetType type, int priority); } public delegate void MessageActionEvoked(Variant? variant); public class MessageAction : Object { public string name; public bool sensitive = true; public string icon_name; public string? tooltip; public Object? popover; public MessageActionEvoked? callback; } public abstract class MetaConversationNotification : Object { public abstract Object? get_widget(WidgetType type); } public interface ConversationItemCollection : Object { public signal void inserted_item(MetaConversationItem item); public signal void removed_item(MetaConversationItem item); public abstract void insert_item(MetaConversationItem item); public abstract void remove_item(MetaConversationItem item); } public interface NotificationCollection : Object { public signal void add_meta_notification(MetaConversationNotification item); public signal void remove_meta_notification(MetaConversationNotification item); } public delegate void SetInputFieldStatus(InputFieldStatus field_status); public class InputFieldStatus : Object { public enum MessageType { NONE, INFO, WARNING, ERROR } public enum InputState { NORMAL, DISABLED, NO_SEND } public string? message; public MessageType message_type; public InputState input_state; public bool contains_markup; public InputFieldStatus(string? message, MessageType message_type, InputState input_state, bool contains_markup = false) { this.message = message; this.message_type = message_type; this.input_state = input_state; this.contains_markup = contains_markup; } } } dino-0.5.0/libdino/src/plugin/loader.vala0000664000000000000000000000532214776241610016777 0ustar rootrootusing Gee; namespace Dino.Plugins { private class Info : Object { public Module module; public Type gtype; public Info(Type type, owned Module module) { this.module = (owned) module; this.gtype = type; } } public class Loader : Object { [CCode (has_target = false)] private delegate Type RegisterPluginFunction(Module module); private Application app; private string[] search_paths; private RootInterface[] plugins = new RootInterface[0]; private Info[] infos = new Info[0]; public Loader(Application app) { this.app = app; this.search_paths = app.search_path_generator.get_plugin_paths(); } public void load_all() throws Error { if (Module.supported() == false) { throw new Error(-1, 0, "Plugins are not supported"); } HashSet plugin_names = new HashSet(); foreach (string path in search_paths) { try { Dir dir = Dir.open(path, 0); string? file = null; while ((file = dir.read_name()) != null) { if (file.has_suffix(Module.SUFFIX)) plugin_names.add(file); } } catch (Error e) { // Ignore this folder } } foreach (string plugin in plugin_names) { load(plugin); } } public RootInterface load(string name) throws Error { if (Module.supported() == false) { throw new Error(-1, 0, "Plugins are not supported"); } Module module = null; string path = ""; foreach (string prefix in search_paths) { path = Path.build_filename(prefix, name); module = Module.open(path, ModuleFlags.BIND_LAZY); if (module != null) break; } if (module == null) { throw new Error(-1, 1, "%s", Module.error().replace(path, name)); } void* function; module.symbol("register_plugin", out function); if (function == null) { throw new Error(-1, 2, "register_plugin () not found"); } RegisterPluginFunction register_plugin = (RegisterPluginFunction) function; Type type = register_plugin(module); if (type.is_a(typeof(RootInterface)) == false) { throw new Error(-1, 3, "Unexpected type"); } Info info = new Plugins.Info(type, (owned) module); infos += info; RootInterface plugin = (RootInterface) Object.new (type); plugins += plugin; plugin.registered(app); return plugin; } public void shutdown() { foreach (RootInterface p in plugins) { p.shutdown(); } } } } dino-0.5.0/libdino/src/plugin/registry.vala0000664000000000000000000001051214776241610017376 0ustar rootrootusing Gee; namespace Dino.Plugins { public class Registry { public HashMap encryption_list_entries = new HashMap(); public HashMap call_encryption_entries = new HashMap(); public ArrayList account_settings_entries = new ArrayList(); public ArrayList encryption_preferences_entries = new ArrayList(); public ArrayList contact_details_entries = new ArrayList(); public Map text_commands = new HashMap(); public Gee.List conversation_addition_populators = new ArrayList(); public Gee.List notification_populators = new ArrayList(); public Gee.Collection conversation_titlebar_entries = new Gee.TreeSet((a, b) => { return (int)(a.order - b.order); }); public VideoCallPlugin? video_call_plugin; public bool register_encryption_list_entry(EncryptionListEntry entry) { lock(encryption_list_entries) { if (encryption_list_entries.has_key(entry.encryption)) return false; encryption_list_entries[entry.encryption] = entry; return true; } } public bool register_call_entryption_entry(string ns, CallEncryptionEntry entry) { lock (call_encryption_entries) { call_encryption_entries[ns] = entry; } return true; } public bool register_account_settings_entry(AccountSettingsEntry entry) { lock(account_settings_entries) { foreach(var e in account_settings_entries) { if (e.id == entry.id) return false; } account_settings_entries.add(entry); // TODO: Order by priority account_settings_entries.sort((a,b) => b.name.collate(a.name)); return true; } } public bool register_encryption_preferences_entry(EncryptionPreferencesEntry entry) { lock(encryption_preferences_entries) { foreach(var e in encryption_preferences_entries) { if (e.id == entry.id) return false; } encryption_preferences_entries.add(entry); // TODO: Order by priority // encryption_preferences_entries.sort((a,b) => b.name.collate(a.name)); return true; } } public bool register_contact_details_entry(ContactDetailsProvider entry) { lock(contact_details_entries) { foreach(ContactDetailsProvider e in contact_details_entries) { if (e.id == entry.id) return false; } contact_details_entries.add(entry); return true; } } public bool register_text_command(TextCommand cmd) { lock(text_commands) { if (text_commands.has_key(cmd.cmd)) return false; text_commands[cmd.cmd] = cmd; return true; } } public bool register_contact_titlebar_entry(ConversationTitlebarEntry entry) { lock(conversation_titlebar_entries) { foreach(ConversationTitlebarEntry e in conversation_titlebar_entries) { if (e.id == entry.id) return false; } conversation_titlebar_entries.add(entry); return true; } } public bool register_conversation_addition_populator(ConversationAdditionPopulator populator) { lock (conversation_addition_populators) { foreach(ConversationItemPopulator p in conversation_addition_populators) { if (p.id == populator.id) return false; } conversation_addition_populators.add(populator); return true; } } public bool register_notification_populator(NotificationPopulator populator) { lock (notification_populators) { foreach(NotificationPopulator p in notification_populators) { if (p.id == populator.id) return false; } notification_populators.add(populator); return true; } } } } dino-0.5.0/libdino/src/service/0000775000000000000000000000000014776241610015024 5ustar rootrootdino-0.5.0/libdino/src/service/avatar_manager.vala0000664000000000000000000003266114776241610020651 0ustar rootrootusing Gdk; using Gee; using Qlite; using Xmpp; using Dino.Entities; namespace Dino { public class AvatarManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("avatar_manager"); public string id { get { return IDENTITY.id; } } public signal void received_avatar(Jid jid, Account account); public signal void fetched_avatar(Jid jid, Account account); private enum Source { USER_AVATARS, VCARD } private StreamInteractor stream_interactor; private Database db; private string folder = null; private HashMap user_avatars = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap vcard_avatars = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap cached_pixbuf = new HashMap(); private HashMap> pending_pixbuf = new HashMap>(); private HashSet pending_fetch = new HashSet(); private const int MAX_PIXEL = 192; public static void start(StreamInteractor stream_interactor, Database db) { AvatarManager m = new AvatarManager(stream_interactor, db); stream_interactor.add_module(m); } private AvatarManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; File old_avatars = File.new_build_filename(Dino.get_storage_dir(), "avatars"); File new_avatars = File.new_build_filename(Dino.get_cache_dir(), "avatars"); this.folder = new_avatars.get_path(); // Move old avatar location to new one if (old_avatars.query_exists()) { if (!new_avatars.query_exists()) { // Move old avatars folder (~/.local/share/dino) to new location (~/.cache/dino) try { new_avatars.get_parent().make_directory_with_parents(); } catch (Error e) { } try { old_avatars.move(new_avatars, FileCopyFlags.NONE); debug("Avatars directory %s moved to %s", old_avatars.get_path(), new_avatars.get_path()); } catch (Error e) { } } else { // If both old and new folders exist, remove the old one try { FileEnumerator enumerator = old_avatars.enumerate_children("standard::*", FileQueryInfoFlags.NOFOLLOW_SYMLINKS); FileInfo info = null; while ((info = enumerator.next_file()) != null) { FileUtils.remove(old_avatars.get_path() + "/" + info.get_name()); } DirUtils.remove(old_avatars.get_path()); } catch (Error e) { } } } // Create avatar folder try { new_avatars.make_directory_with_parents(); } catch (Error e) { } stream_interactor.account_added.connect(on_account_added); stream_interactor.module_manager.initialize_account_modules.connect((_, modules) => { modules.add(new Xep.UserAvatars.Module()); modules.add(new Xep.VCard.Module()); }); } public File? get_avatar_file(Account account, Jid jid_) { string? hash = get_avatar_hash(account, jid_); if (hash == null) return null; File file = File.new_for_path(Path.build_filename(folder, hash)); if (!file.query_exists()) { fetch_and_store_for_jid.begin(account, jid_); return null; } else { return file; } } private string? get_avatar_hash(Account account, Jid jid_) { Jid jid = jid_; if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { jid = jid_.bare_jid; } if (user_avatars.has_key(jid)) { return user_avatars[jid]; } else if (vcard_avatars.has_key(jid)) { return vcard_avatars[jid]; } else { return null; } } [Version (deprecated = true)] public bool has_avatar_cached(Account account, Jid jid) { string? hash = get_avatar_hash(account, jid); return hash != null && cached_pixbuf.has_key(hash); } public bool has_avatar(Account account, Jid jid) { return get_avatar_hash(account, jid) != null; } [Version (deprecated = true)] public Pixbuf? get_cached_avatar(Account account, Jid jid_) { string? hash = get_avatar_hash(account, jid_); if (hash == null) return null; if (cached_pixbuf.has_key(hash)) return cached_pixbuf[hash]; return null; } [Version (deprecated = true)] public async Pixbuf? get_avatar(Account account, Jid jid_) { Jid jid = jid_; if (!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid_, account)) { jid = jid_.bare_jid; } int source = -1; string? hash = null; if (user_avatars.has_key(jid)) { hash = user_avatars[jid]; source = 1; } else if (vcard_avatars.has_key(jid)) { hash = vcard_avatars[jid]; source = 2; } if (hash == null) return null; if (cached_pixbuf.has_key(hash)) { return cached_pixbuf[hash]; } XmppStream? stream = stream_interactor.get_stream(account); if (stream == null || !stream.negotiation_complete) return null; if (pending_pixbuf.has_key(hash)) { pending_pixbuf[hash].add(new SourceFuncWrapper(get_avatar.callback)); yield; return cached_pixbuf[hash]; } pending_pixbuf[hash] = new ArrayList(); Pixbuf? image = yield get_image(hash); if (image != null) { cached_pixbuf[hash] = image; } else { if (yield fetch_and_store(stream, account, jid, source, hash)) { image = yield get_image(hash); } cached_pixbuf[hash] = image; } foreach (SourceFuncWrapper sfw in pending_pixbuf[hash]) { sfw.sfun(); } return image; } public void publish(Account account, string file) { try { Pixbuf pixbuf = new Pixbuf.from_file(file); if (pixbuf.width >= pixbuf.height && pixbuf.width > MAX_PIXEL) { int dest_height = (int) ((float) MAX_PIXEL / pixbuf.width * pixbuf.height); pixbuf = pixbuf.scale_simple(MAX_PIXEL, dest_height, InterpType.BILINEAR); } else if (pixbuf.height > pixbuf.width && pixbuf.width > MAX_PIXEL) { int dest_width = (int) ((float) MAX_PIXEL / pixbuf.height * pixbuf.width); pixbuf = pixbuf.scale_simple(dest_width, MAX_PIXEL, InterpType.BILINEAR); } uint8[] buffer; pixbuf.save_to_buffer(out buffer, "png"); XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { Xmpp.Xep.UserAvatars.publish_png(stream, buffer, pixbuf.width, pixbuf.height); } } catch (Error e) { warning(e.message); } } public void unset_avatar(Account account) { XmppStream stream = stream_interactor.get_stream(account); if (stream == null) return; Xmpp.Xep.UserAvatars.unset_avatar(stream); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) => on_user_avatar_received(account, jid, id) ); stream_interactor.module_manager.get_module(account, Xep.UserAvatars.Module.IDENTITY).avatar_removed.connect((stream, jid) => { on_user_avatar_removed(account, jid); }); stream_interactor.module_manager.get_module(account, Xep.VCard.Module.IDENTITY).received_avatar_hash.connect((stream, jid, id) => on_vcard_avatar_received(account, jid, id) ); foreach (var entry in get_avatar_hashes(account, Source.USER_AVATARS).entries) { on_user_avatar_received(account, entry.key, entry.value); } foreach (var entry in get_avatar_hashes(account, Source.VCARD).entries) { on_vcard_avatar_received(account, entry.key, entry.value); } } private void on_user_avatar_received(Account account, Jid jid_, string id) { Jid jid = jid_.bare_jid; if (!user_avatars.has_key(jid) || user_avatars[jid] != id) { user_avatars[jid] = id; set_avatar_hash(account, jid, id, Source.USER_AVATARS); } received_avatar(jid, account); } private void on_user_avatar_removed(Account account, Jid jid_) { Jid jid = jid_.bare_jid; user_avatars.unset(jid); remove_avatar_hash(account, jid, Source.USER_AVATARS); received_avatar(jid, account); } private void on_vcard_avatar_received(Account account, Jid jid_, string id) { bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(jid_.bare_jid, account); Jid jid = is_gc ? jid_ : jid_.bare_jid; if (!vcard_avatars.has_key(jid) || vcard_avatars[jid] != id) { vcard_avatars[jid] = id; if (jid.is_bare()) { // don't save MUC occupant avatars set_avatar_hash(account, jid, id, Source.VCARD); } } received_avatar(jid, account); } public void set_avatar_hash(Account account, Jid jid, string hash, int type) { db.avatar.insert() .value(db.avatar.jid_id, db.get_jid_id(jid)) .value(db.avatar.account_id, account.id) .value(db.avatar.hash, hash) .value(db.avatar.type_, type) .perform(); } public void remove_avatar_hash(Account account, Jid jid, int type) { db.avatar.delete() .with(db.avatar.jid_id, "=", db.get_jid_id(jid)) .with(db.avatar.account_id, "=", account.id) .with(db.avatar.type_, "=", type) .perform(); } public HashMap get_avatar_hashes(Account account, int type) { HashMap ret = new HashMap(Jid.hash_func, Jid.equals_func); foreach (Row row in db.avatar.select({db.avatar.jid_id, db.avatar.hash}) .with(db.avatar.type_, "=", type) .with(db.avatar.account_id, "=", account.id)) { ret[db.get_jid_by_id(row[db.avatar.jid_id])] = row[db.avatar.hash]; } return ret; } public async bool fetch_and_store_for_jid(Account account, Jid jid) { int source = -1; string? hash = null; if (user_avatars.has_key(jid)) { hash = user_avatars[jid]; source = 1; } else if (vcard_avatars.has_key(jid)) { hash = vcard_avatars[jid]; source = 2; } else { return false; } XmppStream? stream = stream_interactor.get_stream(account); if (stream == null || !stream.negotiation_complete) return false; return yield fetch_and_store(stream, account, jid, source, hash); } private async bool fetch_and_store(XmppStream stream, Account account, Jid jid, int source, string? hash) { if (hash == null || pending_fetch.contains(hash)) return false; pending_fetch.add(hash); Bytes? bytes = null; if (source == 1) { bytes = yield Xmpp.Xep.UserAvatars.fetch_image(stream, jid, hash); } else if (source == 2) { bytes = yield Xmpp.Xep.VCard.fetch_image(stream, jid, hash); if (bytes == null && jid.is_bare()) { db.avatar.delete().with(db.avatar.jid_id, "=", db.get_jid_id(jid)).perform(); } } if (bytes != null) { yield store_image(hash, bytes); fetched_avatar(jid, account); } pending_fetch.remove(hash); return bytes != null; } private async void store_image(string id, Bytes data) { File file = File.new_for_path(Path.build_filename(folder, id)); try { if (file.query_exists()) file.delete(); //TODO y? DataOutputStream fos = new DataOutputStream(file.create(FileCreateFlags.REPLACE_DESTINATION)); yield fos.write_bytes_async(data); } catch (Error e) { // Ignore: we failed in storing, so we refuse to display later... } } public bool has_image(string id) { File file = File.new_for_path(Path.build_filename(folder, id)); return file.query_exists(); } public async Pixbuf? get_image(string id) { try { File file = File.new_for_path(Path.build_filename(folder, id)); FileInputStream stream = yield file.read_async(Priority.LOW); uint8 fbuf[1024]; size_t size; Checksum checksum = new Checksum (ChecksumType.SHA1); while ((size = yield stream.read_async(fbuf, Priority.LOW)) > 0) { checksum.update(fbuf, size); } if (checksum.get_string() != id) { FileUtils.remove(file.get_path()); } stream.seek(0, SeekType.SET); return yield new Pixbuf.from_stream_async(stream, null); } catch (Error e) { return null; } } } } dino-0.5.0/libdino/src/service/blocking_manager.vala0000664000000000000000000000312214776241610021151 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class BlockingManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("blocking_manager"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; public static void start(StreamInteractor stream_interactor) { BlockingManager m = new BlockingManager(stream_interactor); stream_interactor.add_module(m); } private BlockingManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool is_blocked(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_blocked(stream, jid.to_string()); } public void block(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).block(stream, { jid.to_string() }); } public void unblock(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).unblock(stream, { jid.to_string() }); } public bool is_supported(Account account) { XmppStream stream = stream_interactor.get_stream(account); return stream != null && stream.get_module(Xmpp.Xep.BlockingCommand.Module.IDENTITY).is_supported(stream); } } } dino-0.5.0/libdino/src/service/call_peer_state.vala0000664000000000000000000005145414776241610021030 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; public class Dino.PeerState : Object { public signal void stream_created(string media); public signal void counterpart_sends_video_updated(bool mute); public signal void info_received(Xep.JingleRtp.CallSessionInfo session_info); public signal void connection_ready(); public signal void session_terminated(bool we_terminated, string? reason_name, string? reason_text); public signal void encryption_updated(Xep.Jingle.ContentEncryption? audio_encryption, Xep.Jingle.ContentEncryption? video_encryption); public StreamInteractor stream_interactor; public CallState call_state; public Calls calls; public Call call; public Jid jid; public Xep.Jingle.Session session; public string sid; public string internal_id = Xmpp.random_uuid(); public Xep.JingleRtp.Parameters? audio_content_parameter = null; public Xep.JingleRtp.Parameters? video_content_parameter = null; public Xep.Jingle.Content? audio_content = null; public Xep.Jingle.Content? video_content = null; public Xep.Jingle.ContentEncryption? video_encryption = null; public Xep.Jingle.ContentEncryption? audio_encryption = null; public bool encryption_keys_same = false; public HashMap? video_encryptions = null; public HashMap? audio_encryptions = null; public bool first_peer = false; public bool waiting_for_inbound_muji_connection = false; public Xep.Muji.GroupCall? group_call { get; set; } public bool counterpart_sends_video = false; public bool we_should_send_audio { get; set; default=false; } public bool we_should_send_video { get; set; default=false; } public PeerState(Jid jid, Call call, CallState call_state, StreamInteractor stream_interactor) { this.jid = jid; this.call = call; this.call_state = call_state; this.stream_interactor = stream_interactor; this.calls = stream_interactor.get_module(Calls.IDENTITY); Xep.JingleRtp.Module jinglertp_module = stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY); if (jinglertp_module == null) return; var session_info_type = jinglertp_module.session_info_type; session_info_type.mute_update_received.connect((session,mute, name) => { if (this.sid != session.sid) return; foreach (Xep.Jingle.Content content in session.contents) { if (name == null || content.content_name == name) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { on_counterpart_mute_update(mute, rtp_content_parameter.media); } } } }); session_info_type.info_received.connect((session, session_info) => { if (this.sid != session.sid) return; info_received(session_info); }); } public async void initiate_call(Jid counterpart) { Gee.List call_resources = yield calls.get_call_resources(call.account, counterpart); bool do_jmi = false; Jid? jid_for_direct = null; if (yield calls.contains_jmi_resources(call.account, call_resources)) { do_jmi = true; } else if (!call_resources.is_empty) { jid_for_direct = call_resources[0]; } else if (calls.has_jmi_resources(jid)) { do_jmi = true; } sid = Xmpp.random_uuid(); if (do_jmi) { XmppStream? stream = stream_interactor.get_stream(call.account); var descriptions = new ArrayList(); descriptions.add(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "audio")); if (we_should_send_video) { descriptions.add(new StanzaNode.build("description", Xep.JingleRtp.NS_URI).add_self_xmlns().put_attribute("media", "video")); } stream.get_module(Xmpp.Xep.JingleMessageInitiation.Module.IDENTITY).send_session_propose_to_peer(stream, jid, sid, descriptions); // Uncomment this use CIM instead of JMI // call_state.cim_call_id = sid; // stream.get_module(Xmpp.Xep.CallInvites.Module.IDENTITY).send_jingle_propose(stream, call_state.cim_call_id, jid, sid, we_should_send_video); } else if (jid_for_direct != null) { yield call_resource(jid_for_direct); } } public async void call_resource(Jid full_jid) { if (!call_state.accepted) { warning("Tried to call resource in an unaccepted call?!"); return; } XmppStream? stream = stream_interactor.get_stream(call.account); if (stream == null) return; if (sid == null) sid = Xmpp.random_uuid(); Xep.Jingle.Session session = yield stream.get_module(Xep.JingleRtp.Module.IDENTITY).start_call(stream, full_jid, we_should_send_video, sid, group_call != null ? group_call.muc_jid : null); set_session(session); } public void accept() { if (!call_state.accepted) { critical("Tried to accept peer in unaccepted call?! Something's fishy. Abort."); return; } if (session != null) { foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null && rtp_content_parameter.media == "video") { // We didn't accept video but our peer wants to negotiate that content if (!we_should_send_video && session.senders_include_us(content.senders)) { if (session.senders_include_counterpart(content.senders)) { // If our peer wants to send, let them content.modify(session.we_initiated ? Xep.Jingle.Senders.RESPONDER : Xep.Jingle.Senders.INITIATOR); } else { // If only we're supposed to send, reject content.reject(); continue; } } } content.accept(); } } else { // Only a JMI so far XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_accept_to_self(stream, sid); stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_proceed_to_peer(stream, jid, sid); } } public void reject() { if (session != null) { foreach (Xep.Jingle.Content content in session.contents) { content.reject(); } } else { // Only a JMI so far XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_reject_to_peer(stream, jid, sid); stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_reject_to_self(stream, sid); } } public void end(string terminate_reason, string? reason_text = null) { switch (terminate_reason) { case Xep.Jingle.ReasonElement.SUCCESS: if (session != null) { session.terminate(terminate_reason, reason_text, "success"); } break; case Xep.Jingle.ReasonElement.CANCEL: if (session != null) { session.terminate(terminate_reason, reason_text, "cancel"); } else if (group_call != null) { // We don't have to do anything (?) } else { // Only a JMI so far XmppStream? stream = stream_interactor.get_stream(call.account); if (stream == null) return; stream.get_module(Xep.JingleMessageInitiation.Module.IDENTITY).send_session_retract_to_peer(stream, jid, sid); } break; } } internal void mute_own_audio(bool mute) { // Call isn't fully established yet. Audio will be muted once the stream is created. if (session == null || audio_content_parameter == null || audio_content_parameter.stream == null) return; Xep.JingleRtp.Stream stream = audio_content_parameter.stream; // Inform our counterpart that we (un)muted our audio stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY).session_info_type.send_mute(session, mute, "audio"); // Start/Stop sending audio data Application.get_default().plugin_registry.video_call_plugin.set_pause(stream, mute); } internal void mute_own_video(bool mute) { if (session == null) { // Call hasn't been established yet return; } Xep.JingleRtp.Module rtp_module = stream_interactor.module_manager.get_module(call.account, Xep.JingleRtp.Module.IDENTITY); if (video_content_parameter != null && video_content_parameter.stream != null && session.senders_include_us(video_content.senders)) { // A video content already exists // Start/Stop sending video data Xep.JingleRtp.Stream stream = video_content_parameter.stream; if (stream != null) { Application.get_default().plugin_registry.video_call_plugin.set_pause(stream, mute); } // Inform our counterpart that we started/stopped our video rtp_module.session_info_type.send_mute(session, mute, "video"); } else if (!mute) { // Add a new video content XmppStream stream = stream_interactor.get_stream(call.account); rtp_module.add_outgoing_video_content.begin(stream, session, group_call != null ? group_call.muc_jid : null, (_, res) => { if (video_content_parameter == null) { Xep.Jingle.Content content = rtp_module.add_outgoing_video_content.end(res); Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { connect_content_signals(content, rtp_content_parameter); } } }); } // If video_content_parameter == null && !mute we're trying to mute a non-existant feed. It will be muted as soon as it is created. } public Xep.JingleRtp.Stream? get_video_stream() { if (video_content_parameter != null) { return video_content_parameter.stream; } return null; } public Xep.JingleRtp.Stream? get_audio_stream() { if (audio_content_parameter != null) { return audio_content_parameter.stream; } return null; } internal void set_session(Xep.Jingle.Session session) { this.session = session; this.sid = session.sid; session.terminated.connect((stream, we_terminated, reason_name, reason_text) => session_terminated(we_terminated, reason_name, reason_text) ); session.additional_content_add_incoming.connect((stream, content) => on_incoming_content_add(stream, content.session, content) ); foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) continue; connect_content_signals(content, rtp_content_parameter); } } public PeerInfo get_info() { var ret = new PeerInfo(); if (audio_content != null || audio_content_parameter != null) { ret.audio = get_content_info(audio_content, audio_content_parameter); } if (video_content != null || video_content_parameter != null) { ret.video = get_content_info(video_content, video_content_parameter); } return ret; } private PeerContentInfo get_content_info(Xep.Jingle.Content? content, Xep.JingleRtp.Parameters? parameter) { PeerContentInfo ret = new PeerContentInfo(); if (parameter != null) { ret.rtcp_ready = parameter.rtcp_ready; ret.rtp_ready = parameter.rtp_ready; if (parameter.agreed_payload_type != null) { ret.codec = parameter.agreed_payload_type.name; ret.clockrate = parameter.agreed_payload_type.clockrate; } if (parameter.stream != null && parameter.stream.remb_enabled) { ret.target_receive_bytes = parameter.stream.target_receive_bitrate; ret.target_send_bytes = parameter.stream.target_send_bitrate; } } if (content != null) { Xmpp.Xep.Jingle.ComponentConnection? component0 = content.get_transport_connection(1); if (component0 != null) { ret.bytes_received = component0.bytes_received; ret.bytes_sent = component0.bytes_sent; } } return ret; } private void connect_content_signals(Xep.Jingle.Content content, Xep.JingleRtp.Parameters rtp_content_parameter) { if (rtp_content_parameter.media == "audio") { audio_content = content; audio_content_parameter = rtp_content_parameter; } else if (rtp_content_parameter.media == "video") { video_content = content; video_content_parameter = rtp_content_parameter; } debug(@"[%s] %s connecting content signals %s", call.account.bare_jid.to_string(), jid.to_string(), rtp_content_parameter.media); rtp_content_parameter.stream_created.connect((stream) => on_stream_created(rtp_content_parameter.media, stream)); rtp_content_parameter.connection_ready.connect((status) => { Idle.add(() => { on_connection_ready(content, rtp_content_parameter.media); return false; }); }); content.senders_modify_incoming.connect((content, proposed_senders) => { if (content.session.senders_include_us(content.senders) != content.session.senders_include_us(proposed_senders)) { warning("counterpart set us to (not)sending %s. ignoring", content.content_name); return; } if (!content.session.senders_include_counterpart(content.senders) && content.session.senders_include_counterpart(proposed_senders)) { // Counterpart wants to start sending. Ok. content.accept_content_modify(proposed_senders); on_counterpart_mute_update(false, "video"); } }); } private void on_incoming_content_add(XmppStream stream, Xep.Jingle.Session session, Xep.Jingle.Content content) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) { content.reject(); return; } // Our peer shouldn't tell us to start sending, that's for us to initiate if (session.senders_include_us(content.senders)) { if (session.senders_include_counterpart(content.senders)) { // If our peer wants to send, let them content.modify(session.we_initiated ? Xep.Jingle.Senders.RESPONDER : Xep.Jingle.Senders.INITIATOR); } else { // If only we're supposed to send, reject content.reject(); } } connect_content_signals(content, rtp_content_parameter); content.accept(); } private void on_stream_created(string media, Xep.JingleRtp.Stream stream) { if (media == "video" && stream.receiving) { counterpart_sends_video = true; video_content_parameter.connection_ready.connect((status) => { Idle.add(() => { counterpart_sends_video_updated(false); return false; }); }); } // Outgoing audio/video might have been muted in the meanwhile. if (media == "video" && !we_should_send_video) { mute_own_video(true); } else if (media == "audio" && !we_should_send_audio) { mute_own_audio(true); } stream_created(media); } private void on_counterpart_mute_update(bool mute, string? media) { if (!call.equals(call)) return; if (media == "video") { counterpart_sends_video = !mute; debug(@"[%s] %s video muted %s", call.account.bare_jid.to_string(), jid.to_string(), mute.to_string()); counterpart_sends_video_updated(mute); } } private void on_connection_ready(Xep.Jingle.Content content, string media) { debug("[%s] %s on_connection_ready", call.account.bare_jid.to_string(), jid.to_string()); connection_ready(); if (call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { call.state = Call.State.IN_PROGRESS; } if (media == "audio") { audio_encryptions = content.encryptions; } else if (media == "video") { video_encryptions = content.encryptions; } if ((audio_encryptions != null && audio_encryptions.is_empty) || (video_encryptions != null && video_encryptions.is_empty)) { call.encryption = Encryption.NONE; encryption_updated(null, null); return; } HashMap encryptions = audio_encryptions ?? video_encryptions; Xep.Jingle.ContentEncryption? omemo_encryption = null, dtls_encryption = null, srtp_encryption = null; foreach (string encr_name in encryptions.keys) { if (video_encryptions != null && !video_encryptions.has_key(encr_name)) continue; var encryption = encryptions[encr_name]; if (encryption.encryption_ns == "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification") { omemo_encryption = encryption; } else if (encryption.encryption_ns == Xep.JingleIceUdp.DTLS_NS_URI) { dtls_encryption = encryption; } else if (encryption.encryption_name == "SRTP") { srtp_encryption = encryption; } } if (omemo_encryption != null && dtls_encryption != null) { call.encryption = Encryption.OMEMO; omemo_encryption.peer_key = dtls_encryption.peer_key; omemo_encryption.our_key = dtls_encryption.our_key; audio_encryption = omemo_encryption; encryption_keys_same = true; video_encryption = video_encryptions != null ? video_encryptions["http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"] : null; } else if (dtls_encryption != null) { call.encryption = Encryption.DTLS_SRTP; audio_encryption = dtls_encryption; video_encryption = video_encryptions != null ? video_encryptions[Xep.JingleIceUdp.DTLS_NS_URI] : null; encryption_keys_same = true; if (video_encryption != null && dtls_encryption.peer_key.length == video_encryption.peer_key.length) { for (int i = 0; i < dtls_encryption.peer_key.length; i++) { if (dtls_encryption.peer_key[i] != video_encryption.peer_key[i]) { encryption_keys_same = false; break; } } } } else if (srtp_encryption != null) { call.encryption = Encryption.SRTP; audio_encryption = srtp_encryption; video_encryption = video_encryptions != null ? video_encryptions["SRTP"] : null; encryption_keys_same = false; } else { call.encryption = Encryption.NONE; encryption_keys_same = true; } encryption_updated(audio_encryption, video_encryption); } } public class Dino.PeerContentInfo { public bool rtp_ready { get; set; } public bool rtcp_ready { get; set; } public ulong? bytes_sent { get; set; default=0; } public ulong? bytes_received { get; set; default=0; } public string? codec { get; set; } public uint32 clockrate { get; set; } public uint target_receive_bytes { get; set; default=-1; } public uint target_send_bytes { get; set; default=-1; } } public class Dino.PeerInfo { public PeerContentInfo? audio = null; public PeerContentInfo? video = null; }dino-0.5.0/libdino/src/service/call_state.vala0000664000000000000000000005067314776241610020017 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; public class Dino.CallState : Object { public signal void terminated(Jid who_terminated, string? reason_name, string? reason_text); public signal void peer_joined(Jid jid, PeerState peer_state); public signal void peer_left(Jid jid, PeerState peer_state, string? reason_name, string? reason_text); public StreamInteractor stream_interactor; public Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; public Call call; public Jid? parent_muc { get; set; } public Jid? invited_to_group_call = null; public bool accepted { get; private set; default=false; } public bool use_cim = false; public string? cim_call_id = null; public Jid? cim_counterpart = null; public ArrayList cim_jids_to_inform = new ArrayList(); public string cim_message_type { get; set; default=Xmpp.MessageStanza.TYPE_CHAT; } public Xep.Muji.GroupCall? group_call { get; set; } public bool we_should_send_audio { get; set; default=false; } public bool we_should_send_video { get; set; default=false; } public HashMap peers = new HashMap(Jid.hash_func, Jid.equals_func); private Plugins.MediaDevice selected_microphone_device; private Plugins.MediaDevice selected_speaker_device; private Plugins.MediaDevice selected_video_device; public CallState(Call call, StreamInteractor stream_interactor) { this.call = call; this.stream_interactor = stream_interactor; if (call.direction == Call.DIRECTION_OUTGOING && call.state != Call.State.OTHER_DEVICE) { accepted = true; Timeout.add_seconds(30, () => { if (this == null) return false; // TODO enough? if (call.state == Call.State.ESTABLISHING) { call.state = Call.State.MISSED; terminated(call.account.bare_jid, null, null); } return false; }); } } internal async void initiate_groupchat_call(Jid muc) { cim_jids_to_inform.add(muc); cim_message_type = MessageStanza.TYPE_GROUPCHAT; if (this.group_call == null) yield convert_into_group_call(); if (this.group_call == null) return; // The user might have retracted the call in the meanwhile if (this.call.state != Call.State.RINGING) return; XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; Gee.List occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(muc, call.account); foreach (Jid occupant in occupants) { Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, call.account); if (real_jid == null) continue; debug(@"Adding MUC member as MUJI MUC owner %s", real_jid.bare_jid.to_string()); yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, real_jid.bare_jid, null, "owner"); } stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_propose(stream, cim_call_id, muc, group_call.muc_jid, we_should_send_video, cim_message_type); } internal PeerState set_first_peer(Jid peer) { var peer_state = new PeerState(peer, call, this, stream_interactor); peer_state.first_peer = true; add_peer(peer_state); return peer_state; } internal void add_peer(PeerState peer) { call.add_peer(peer.jid.bare_jid); connect_peer_signals(peer); peer_joined(peer.jid, peer); } internal void on_peer_stream_created(PeerState peer, string media) { if (media == "audio") { call_plugin.set_device(peer.get_audio_stream(), get_microphone_device()); call_plugin.set_device(peer.get_audio_stream(), get_speaker_device()); } else if (media == "video") { call_plugin.set_device(peer.get_video_stream(), get_video_device()); } } public void accept() { accepted = true; call.state = Call.State.ESTABLISHING; XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; if (use_cim) { if (invited_to_group_call != null) { join_group_call.begin(invited_to_group_call); foreach (Jid jid_to_inform in cim_jids_to_inform) { stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_accept(stream, jid_to_inform, cim_call_id, invited_to_group_call, cim_message_type); } } else if (peers.size == 1) { string sid = peers.values.to_array()[0].sid; foreach (Jid jid_to_inform in cim_jids_to_inform) { stream.get_module(Xep.CallInvites.Module.IDENTITY).send_jingle_accept(stream, jid_to_inform, cim_call_id, sid, cim_message_type); } } } else { foreach (PeerState peer in peers.values) { peer.accept(); } } } public void reject() { call.state = Call.State.DECLINED; if (use_cim) { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; foreach (Jid jid_to_inform in cim_jids_to_inform) { stream.get_module(Xep.CallInvites.Module.IDENTITY).send_reject(stream, jid_to_inform, cim_call_id, cim_message_type); } } var peers_cpy = new ArrayList(); peers_cpy.add_all(peers.values); foreach (PeerState peer in peers_cpy) { peer.reject(); } terminated(call.account.bare_jid, null, null); } public void end(string? reason_text = null) { var peers_cpy = new ArrayList(); peers_cpy.add_all(peers.values); // Terminate sessions, send out messages about the ended call, exit MUC if applicable XmppStream stream = stream_interactor.get_stream(call.account); if (stream != null) { if (group_call != null) { stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, group_call.muc_jid); } if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) { foreach (PeerState peer in peers_cpy) { peer.end(Xep.Jingle.ReasonElement.SUCCESS, reason_text); } if (use_cim) { foreach (Jid jid_to_inform in cim_jids_to_inform) { stream.get_module(Xep.CallInvites.Module.IDENTITY).send_left(stream, jid_to_inform, cim_call_id, cim_message_type); } } } else if (call.state == Call.State.RINGING) { foreach (PeerState peer in peers_cpy) { peer.end(Xep.Jingle.ReasonElement.CANCEL, reason_text); } if (call.direction == Call.DIRECTION_OUTGOING && use_cim) { foreach (Jid jid_to_inform in cim_jids_to_inform) { stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, jid_to_inform, cim_call_id, cim_message_type); } } } } // Update the call state if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) { call.state = Call.State.ENDED; } else if (call.state == Call.State.RINGING) { call.state = Call.State.MISSED; } else { return; } call.end_time = new DateTime.now_utc(); terminated(call.account.bare_jid, null, reason_text); } public void mute_own_audio(bool mute) { we_should_send_audio = !mute; foreach (PeerState peer in peers.values) { peer.mute_own_audio(mute); } } public void mute_own_video(bool mute) { we_should_send_video = !mute; foreach (PeerState peer in peers.values) { peer.mute_own_video(mute); } } public bool should_we_send_video() { return we_should_send_video; } public async void invite_to_call(Jid invitee) { if (this.group_call == null) yield convert_into_group_call(); if (this.group_call == null) return; XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; debug("[%s] Inviting to muji call %s", call.account.bare_jid.to_string(), invitee.to_string()); yield stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation(stream, group_call.muc_jid, invitee, null, "owner"); stream.get_module(Xep.CallInvites.Module.IDENTITY).send_muji_propose(stream, cim_call_id, invitee, group_call.muc_jid, we_should_send_video, "chat"); // If the peer hasn't accepted within a minute, retract the invite // TODO this should be unset when we retract the invite. otherwise a second invite attempt might break due to this Timeout.add_seconds(60, () => { if (this == null) return false; bool contains_peer = false; foreach (Jid peer in peers.keys) { if (peer.equals_bare(invitee)) { contains_peer = true; } } if (!contains_peer) { debug("[%s] Retracting invite to %s from %s", call.account.bare_jid.to_string(), group_call.muc_jid.to_string(), invitee.to_string()); // stream.get_module(Xep.CallInvites.Module.IDENTITY).send_retract(stream, invitee, invite_id); // stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, group_call.muc_jid, invitee, null, "none"); } return false; }); } public Plugins.MediaDevice? get_microphone_device() { if (selected_microphone_device == null) { if (!peers.is_empty) { var audio_stream = peers.values.to_array()[0].get_audio_stream(); selected_microphone_device = call_plugin.get_device(audio_stream, false); } if (selected_microphone_device == null) { selected_microphone_device = call_plugin.get_preferred_device("audio", false); } } return selected_microphone_device; } public Plugins.MediaDevice? get_speaker_device() { if (selected_speaker_device == null) { if (!peers.is_empty) { var audio_stream = peers.values.to_array()[0].get_audio_stream(); selected_speaker_device = call_plugin.get_device(audio_stream, true); } if (selected_speaker_device == null) { selected_speaker_device = call_plugin.get_preferred_device("audio", true); } } return selected_speaker_device; } public Plugins.MediaDevice? get_video_device() { if (selected_video_device == null) { if (!peers.is_empty) { var video_stream = peers.values.to_array()[0].get_video_stream(); selected_video_device = call_plugin.get_device(video_stream, false); } if (selected_video_device == null) { selected_video_device = call_plugin.get_preferred_device("video", false); } } return selected_video_device; } public void set_audio_device(Plugins.MediaDevice? device) { if (device.incoming) { selected_speaker_device = device; } else { selected_microphone_device = device; } foreach (PeerState peer_state in peers.values) { call_plugin.set_device(peer_state.get_audio_stream(), device); } } public void set_video_device(Plugins.MediaDevice? device) { selected_video_device = device; foreach (PeerState peer_state in peers.values) { call_plugin.set_device(peer_state.get_video_stream(), device); } } internal void rename_peer(Jid from_jid, Jid to_jid) { debug("[%s] Renaming %s to %s exists %s", call.account.bare_jid.to_string(), from_jid.to_string(), to_jid.to_string(), peers.has_key(from_jid).to_string()); PeerState? peer_state = peers[from_jid]; if (peer_state == null) return; // Adjust the internal mapping of this `PeerState` object peers.unset(from_jid); peers[to_jid] = peer_state; peer_state.jid = to_jid; } private void on_call_terminated(Jid who_terminated, bool we_terminated, string? reason_name, string? reason_text) { if (call.state == Call.State.RINGING || call.state == Call.State.IN_PROGRESS || call.state == Call.State.ESTABLISHING) { call.end_time = new DateTime.now_utc(); } if (call.state == Call.State.IN_PROGRESS) { call.state = Call.State.ENDED; } else if (call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { if (reason_name == Xep.Jingle.ReasonElement.DECLINE) { call.state = Call.State.DECLINED; } else { call.state = Call.State.FAILED; } } terminated(who_terminated, reason_name, reason_text); } private void connect_peer_signals(PeerState peer_state) { peers[peer_state.jid] = peer_state; this.bind_property("we-should-send-audio", peer_state, "we-should-send-audio", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); this.bind_property("we-should-send-video", peer_state, "we-should-send-video", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); this.bind_property("group-call", peer_state, "group-call", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); peer_state.stream_created.connect((peer, media) => { on_peer_stream_created(peer, media); }); peer_state.session_terminated.connect((we_terminated, reason_name, reason_text) => { debug("[%s] Peer left %s: %s %s (%i peers remaining)", call.account.bare_jid.to_string(), reason_text ?? "", reason_name ?? "", peer_state.jid.to_string(), peers.size); handle_peer_left(peer_state, we_terminated, reason_name, reason_text); }); } public async bool can_convert_into_groupcall() { if (peers.size == 0) return false; Jid peer = peers.keys.to_array()[0]; bool peer_has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(call.account, peer, Xep.Muji.NS_URI); bool can_initiate = stream_interactor.get_module(Calls.IDENTITY).can_initiate_groupcall(call.account); return peer_has_feature && can_initiate; } public async void convert_into_group_call() { XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; Jid? muc_jid = stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[call.account]; if (muc_jid == null) { warning("Failed to initiate group call: MUC server not known."); return; } if (cim_call_id == null) cim_call_id = Xmpp.random_uuid(); muc_jid = new Jid("%08x@".printf(Random.next_int()) + muc_jid.to_string()); // TODO longer? debug("[%s] Converting call to groupcall %s", call.account.bare_jid.to_string(), muc_jid.to_string()); yield join_group_call(muc_jid); Xep.DataForms.DataForm? data_form = yield stream_interactor.get_module(MucManager.IDENTITY).get_config_form(call.account, muc_jid); if (data_form == null) return; foreach (Xep.DataForms.DataForm.Field field in data_form.fields) { switch (field.var) { case "muc#roomconfig_allowinvites": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = true; } break; case "muc#roomconfig_persistentroom": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = false; } break; case "muc#roomconfig_membersonly": if (field.type_ == Xep.DataForms.DataForm.Type.BOOLEAN) { ((Xep.DataForms.DataForm.BooleanField) field).value = true; } break; case "muc#roomconfig_whois": if (field.type_ == Xep.DataForms.DataForm.Type.LIST_SINGLE) { ((Xep.DataForms.DataForm.ListSingleField) field).value = "anyone"; } break; } } yield stream_interactor.get_module(MucManager.IDENTITY).set_config_form(call.account, muc_jid, data_form); foreach (Jid peer_jid in peers.keys) { debug("[%s] Group call inviting %s", call.account.bare_jid.to_string(), peer_jid.to_string()); yield invite_to_call(peer_jid); } } public async void join_group_call(Jid muc_jid) { debug("[%s] Joining group call %s", call.account.bare_jid.to_string(), muc_jid.to_string()); XmppStream stream = stream_interactor.get_stream(call.account); if (stream == null) return; this.group_call = yield stream.get_module(Xep.Muji.Module.IDENTITY).join_call(stream, muc_jid, we_should_send_video); if (this.group_call == null) { warning("[%s] Couldn't join MUJI MUC", call.account.bare_jid.to_string()); return; } this.group_call.peer_joined.connect((jid) => { debug("[%s] Group call peer joined: %s", call.account.bare_jid.to_string(), jid.to_string()); // Newly joined peers have to call us, not the other way round // Maybe they called us already. Accept the call. // (Except for the first peer, we already have a connection to that one.) if (peers.has_key(jid)) { if (!peers[jid].first_peer) { peers[jid].accept(); } // else: Connection to first peer already active } else { var peer_state = new PeerState(jid, call, this, stream_interactor); peer_state.waiting_for_inbound_muji_connection = true; debug("[%s] Waiting for call from %s", call.account.bare_jid.to_string(), jid.to_string()); add_peer(peer_state); } }); this.group_call.peer_left.connect((jid) => { debug("[%s] Group call peer left: %s", call.account.bare_jid.to_string(), jid.to_string()); PeerState? peer_state = peers[jid]; if (peer_state == null) return; peer_state.end(Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC"); handle_peer_left(peer_state, false, Xep.Jingle.ReasonElement.CANCEL, "Peer left the MUJI MUC"); }); if (group_call.peers_to_connect_to.size > 4) { end("Call too full - P2p calls don't work well with many participants"); return; } // Call all peers that are in the room already foreach (Jid peer_jid in group_call.peers_to_connect_to) { // Don't establish connection if we have one already (the person that invited us to the call) if (peers.has_key(peer_jid)) continue; debug("[%s] Calling %s because they were in the MUC already", call.account.bare_jid.to_string(), peer_jid.to_string()); PeerState peer_state = new PeerState(peer_jid, call, this, stream_interactor); add_peer(peer_state); peer_state.call_resource.begin(peer_jid); } debug("[%s] Finished joining MUJI muc %s", call.account.bare_jid.to_string(), muc_jid.to_string()); } private void handle_peer_left(PeerState peer_state, bool we_terminated, string? reason_name, string? reason_text) { if (!peers.has_key(peer_state.jid)) return; peers.unset(peer_state.jid); if (peers.is_empty) { if (group_call != null) { group_call.leave(stream_interactor.get_stream(call.account)); on_call_terminated(peer_state.jid, we_terminated, null, "All participants have left the call"); } else { on_call_terminated(peer_state.jid, we_terminated, reason_name, reason_text); } } else { peer_left(peer_state.jid, peer_state, reason_name, reason_text); } } }dino-0.5.0/libdino/src/service/call_store.vala0000664000000000000000000000404414776241610020022 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class CallStore : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("call_store"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap calls_by_db_id = new WeakMap(); public static void start(StreamInteractor stream_interactor, Database db) { CallStore m = new CallStore(stream_interactor, db); stream_interactor.add_module(m); } private CallStore(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_call(Call call, Conversation conversation) { call.persist(db); cache_call(call); } public Call? get_call_by_id(int id, Conversation conversation) { Call? call = calls_by_db_id[id]; if (call != null) { return call; } RowOption row_option = db.call.select().with(db.call.id, "=", id).row(); return create_call_from_row_opt(row_option, conversation); } private Call? create_call_from_row_opt(RowOption row_opt, Conversation conversation) { if (!row_opt.is_present()) return null; try { Call call = new Call.from_row(db, row_opt.inner); if (conversation.type_.is_muc_semantic()) { call.ourpart = conversation.counterpart.with_resource(call.ourpart.resourcepart); } cache_call(call); return call; } catch (InvalidJidError e) { warning("Got message with invalid Jid: %s", e.message); } return null; } private void cache_call(Call call) { calls_by_db_id[call.id] = call; } } }dino-0.5.0/libdino/src/service/calls.vala0000664000000000000000000006556514776241610017010 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class Calls : StreamInteractionModule, Object { public signal void call_incoming(Call call, CallState state, Conversation conversation, bool video, bool multiparty); public signal void call_outgoing(Call call, CallState state, Conversation conversation); public signal void call_terminated(Call call, string? reason_name, string? reason_text); public signal void conference_info_received(Call call, Xep.Coin.ConferenceInfo conference_info); public static ModuleIdentity IDENTITY = new ModuleIdentity("calls"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; // public HashMap current_jmi_request_call = new HashMap(Account.hash_func, Account.equals_func); public HashMap jmi_request_peer = new HashMap(Call.hash_func, Call.equals_func); public HashMap call_states = new HashMap(Call.hash_func, Call.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { Calls m = new Calls(stream_interactor, db); stream_interactor.add_module(m); } private Calls(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); } public async CallState? initiate_call(Conversation conversation, bool video) { Call call = new Call(); call.direction = Call.DIRECTION_OUTGOING; call.account = conversation.account; call.counterpart = conversation.counterpart; call.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.full_jid; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; call.state = Call.State.RINGING; stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); var call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); call_state.we_should_send_video = video; call_state.we_should_send_audio = true; if (conversation.type_ == Conversation.Type.CHAT) { call.add_peer(conversation.counterpart); PeerState peer_state = call_state.set_first_peer(conversation.counterpart); jmi_request_peer[call] = peer_state; yield peer_state.initiate_call(conversation.counterpart); } else { call_state.initiate_groupchat_call.begin(conversation.counterpart); } call_outgoing(call, call_state, conversation); return call_state; } public bool can_we_do_calls(Account account) { Plugins.VideoCallPlugin? plugin = Application.get_default().plugin_registry.video_call_plugin; if (plugin == null) return false; return plugin.supported(); } public async bool can_conversation_do_calls(Conversation conversation) { if (!can_we_do_calls(conversation.account)) return false; if (conversation.type_ == Conversation.Type.CHAT) { return !conversation.counterpart.equals_bare(conversation.account.bare_jid); } else { bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); return is_private && can_initiate_groupcall(conversation.account); } } public bool can_initiate_groupcall(Account account) { return stream_interactor.get_module(MucManager.IDENTITY).default_muc_server[account] != null; } public async Gee.List get_call_resources(Account account, Jid counterpart) { ArrayList ret = new ArrayList(); XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return ret; Presence.Flag? presence_flag = stream.get_flag(Presence.Flag.IDENTITY); if (presence_flag == null) return ret; Gee.List? full_jids = presence_flag.get_resources(counterpart); if (full_jids == null) return ret; foreach (Jid full_jid in full_jids) { var module = stream.get_module(Xep.JingleRtp.Module.IDENTITY); if (module == null) return ret; bool supports_rtc = yield module.is_available(stream, full_jid); if (!supports_rtc) continue; ret.add(full_jid); } return ret; } public async bool contains_jmi_resources(Account account, Gee.List full_jids) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return false; foreach (Jid full_jid in full_jids) { bool does_jmi = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(account, full_jid, Xep.JingleMessageInitiation.NS_URI); if (does_jmi) return true; } return false; } public bool has_jmi_resources(Jid counterpart) { int64 jmi_resources = db.entity.select() .with(db.entity.jid_id, "=", db.get_jid_id(counterpart)) .join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity) .with(db.entity_feature.feature, "=", Xep.JingleMessageInitiation.NS_URI) .count(); return jmi_resources > 0; } public bool is_call_in_progress() { foreach (Call call in call_states.keys) { if (call.state == Call.State.IN_PROGRESS || call.state == Call.State.RINGING || call.state == Call.State.ESTABLISHING) { return true; } } return false; } private void on_incoming_call(Account account, Xep.Jingle.Session session) { Jid? muji_room = session.muji_room; bool counterpart_wants_video = false; foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter == null) continue; if (rtp_content_parameter.media == "video" && session.senders_include_us(content.senders)) { counterpart_wants_video = true; } } // Check if this comes from a MUJI MUC => accept if (muji_room != null) { debug("[%s] Incoming call from %s from MUJI muc %s", account.bare_jid.to_string(), session.peer_full_jid.to_string(), muji_room.to_string()); foreach (CallState call_state in call_states.values) { if (call_state.call.account.equals(account) && call_state.group_call != null && call_state.group_call.muc_jid.equals(muji_room)) { if (call_state.peers.keys.contains(session.peer_full_jid)) { PeerState peer_state = call_state.peers[session.peer_full_jid]; debug("[%s] Incoming call, we know the peer. Expected %s", account.bare_jid.to_string(), peer_state.waiting_for_inbound_muji_connection.to_string()); if (!peer_state.waiting_for_inbound_muji_connection) return; peer_state.set_session(session); debug(@"[%s] Accepting incoming MUJI call from %s", account.bare_jid.to_string(), session.peer_full_jid.to_string()); peer_state.accept(); } else { debug(@"[%s] Incoming call, but didn't see peer in MUC yet", account.bare_jid.to_string()); PeerState peer_state = new PeerState(session.peer_full_jid, call_state.call, call_state, stream_interactor); peer_state.set_session(session); call_state.add_peer(peer_state); } return; } } return; } debug(@"[%s] Incoming call from %s", account.bare_jid.to_string(), session.peer_full_jid.to_string()); // Check if we already got this call via Jingle Message Initiation => accept // PeerState.accept() checks if the call was accepted and ensures that we don't accidentally send video PeerState? peer_state = get_peer_by_sid(account, session.sid, session.peer_full_jid); if (peer_state != null) { jmi_request_peer[peer_state.call].set_session(session); jmi_request_peer[peer_state.call].accept(); jmi_request_peer.unset(peer_state.call); return; } // This is a direct call without prior JMI. Ask user. if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(session.peer_full_jid.bare_jid, account)) return; peer_state = create_received_call(account, session.peer_full_jid, account.full_jid, counterpart_wants_video); peer_state.set_session(session); Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(peer_state.call.counterpart.bare_jid, account, Conversation.Type.CHAT); call_incoming(peer_state.call, peer_state.call_state, conversation, counterpart_wants_video, false); stream_interactor.module_manager.get_module(account, Xep.JingleRtp.Module.IDENTITY).session_info_type.send_ringing(session); } private PeerState create_received_call(Account account, Jid from, Jid to, bool video_requested) { Call call = new Call(); if (from.equals_bare(account.bare_jid)) { // Call requested by another of our devices call.direction = Call.DIRECTION_OUTGOING; call.ourpart = from; call.state = Call.State.OTHER_DEVICE; call.counterpart = to; } else { call.direction = Call.DIRECTION_INCOMING; call.ourpart = account.full_jid; call.state = Call.State.RINGING; call.counterpart = from; } call.add_peer(call.counterpart); call.account = account; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(call.counterpart.bare_jid, account, Conversation.Type.CHAT); stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); var call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); PeerState peer_state = call_state.set_first_peer(call.counterpart); call_state.we_should_send_video = video_requested; call_state.we_should_send_audio = true; return peer_state; } private CallState? get_call_state_by_call_id(Account account, string call_id, Jid? counterpart_jid = null) { foreach (CallState call_state in call_states.values) { if (!call_state.call.account.equals(account)) continue; if (call_state.cim_call_id == call_id) { if (counterpart_jid == null) return call_state; foreach (Jid jid in call_state.peers.keys) { if (jid.equals_bare(counterpart_jid)) { return call_state; } } } } return null; } private PeerState? get_peer_by_sid(Account account, string sid, Jid jid1, Jid? jid2 = null) { Jid relevant_jid = jid1.equals_bare(account.bare_jid) && jid2 != null ? jid2 : jid1; foreach (CallState call_state in call_states.values) { if (!call_state.call.account.equals(account)) continue; foreach (PeerState peer_state in call_state.peers.values) { if (peer_state.sid != sid) continue; if (peer_state.jid.equals_bare(relevant_jid)) { return peer_state; } } } return null; } private CallState? create_recv_muji_call(Account account, string call_id, Jid inviter_jid, Jid muc_jid, string message_type) { debug("[%s] Muji call received from %s for MUC %s, type %s", account.bare_jid.to_string(), inviter_jid.to_string(), muc_jid.to_string(), message_type); foreach (Call call in call_states.keys) { if (!call.account.equals(account)) continue; CallState call_state = call_states[call]; if (call.counterparts.size == 1 && call.counterparts.contains(inviter_jid) && call_state.accepted) { // A call is converted into a group call. call_state.cim_call_id = call_id; call_state.join_group_call.begin(muc_jid); return null; } } Call call = new Call(); call.direction = Call.DIRECTION_INCOMING; call.ourpart = account.full_jid; call.counterpart = inviter_jid; call.account = account; call.time = call.local_time = call.end_time = new DateTime.now_utc(); call.encryption = Encryption.UNKNOWN; call.state = Call.State.RINGING; // TODO create conv Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(inviter_jid.bare_jid, account); if (conversation == null) return null; stream_interactor.get_module(CallStore.IDENTITY).add_call(call, conversation); CallState call_state = new CallState(call, stream_interactor); connect_call_state_signals(call_state); call_state.invited_to_group_call = muc_jid; call_state.use_cim = true; call_state.cim_jids_to_inform.add(inviter_jid.bare_jid); debug("[%s] on_muji_call_received accepting", account.bare_jid.to_string()); return call_state; } private void remove_call_from_datastructures(Call call) { jmi_request_peer.unset(call); call_states.unset(call); } private void connect_call_state_signals(CallState call_state) { call_states[call_state.call] = call_state; ulong terminated_handler_id = -1; terminated_handler_id = call_state.terminated.connect((who_terminated, reason_name, reason_text) => { remove_call_from_datastructures(call_state.call); call_terminated(call_state.call, reason_name, reason_text); call_state.disconnect(terminated_handler_id); }); } private void on_account_added(Account account) { Xep.Jingle.Module jingle_module = stream_interactor.module_manager.get_module(account, Xep.Jingle.Module.IDENTITY); jingle_module.session_initiate_received.connect((stream, session) => { foreach (Xep.Jingle.Content content in session.contents) { Xep.JingleRtp.Parameters? rtp_content_parameter = content.content_params as Xep.JingleRtp.Parameters; if (rtp_content_parameter != null) { on_incoming_call(account, session); break; } } }); Xep.JingleMessageInitiation.Module mi_module = stream_interactor.module_manager.get_module(account, Xep.JingleMessageInitiation.Module.IDENTITY); mi_module.session_proposed.connect((from, to, sid, descriptions) => { if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(from.bare_jid, account)) return; bool audio_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "audio"); bool video_requested = descriptions.any_match((description) => description.ns_uri == Xep.JingleRtp.NS_URI && description.get_attribute("media") == "video"); if (!audio_requested && !video_requested) return; PeerState peer_state = create_received_call(account, from, to, video_requested); peer_state.sid = sid; CallState call_state = call_states[peer_state.call]; jmi_request_peer[peer_state.call] = peer_state; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(call_state.call.counterpart.bare_jid, account, Conversation.Type.CHAT); if (call_state.call.direction == Call.DIRECTION_INCOMING) { call_incoming(call_state.call, call_state, conversation, video_requested, false); } else { call_outgoing(call_state.call, call_state, conversation); } }); mi_module.session_accepted.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; // Carboned message from our account if (from.equals_bare(account.bare_jid)) { // Ignore carbon from ourselves if (from.equals(account.full_jid)) return; call.ourpart = from; call.state = Call.State.OTHER_DEVICE; remove_call_from_datastructures(call); return; } // We proposed the call. This is a message from our peer. if (call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(peer_state.jid) && to.equals(account.full_jid)) { // We know the full jid of our peer now call_states[call].rename_peer(jmi_request_peer[call].jid, from); jmi_request_peer[call].call_resource.begin(from); } }); mi_module.session_rejected.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; bool outgoing_reject = call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(call.counterparts[0]); bool incoming_reject = call.direction == Call.DIRECTION_INCOMING && from.equals_bare(account.bare_jid); if (!outgoing_reject && !incoming_reject) return; // We don't care if a single person in a group call rejected the call if (incoming_reject && call_states[call].group_call != null) return; call.state = Call.State.DECLINED; call_states[call].terminated(from, Xep.Jingle.ReasonElement.DECLINE, "JMI reject"); remove_call_from_datastructures(call); }); mi_module.session_retracted.connect((from, to, sid) => { PeerState? peer_state = get_peer_by_sid(account, sid, from, to); if (peer_state == null) return; Call call = peer_state.call; bool outgoing_retract = call.direction == Call.DIRECTION_OUTGOING && from.equals_bare(account.bare_jid); bool incoming_retract = call.direction == Call.DIRECTION_INCOMING && from.equals_bare(call.counterpart); if (!(outgoing_retract || incoming_retract)) return; call.state = Call.State.MISSED; call_states[call].terminated(from, Xep.Jingle.ReasonElement.CANCEL, "JMI retract"); remove_call_from_datastructures(call); }); Xep.CallInvites.Module call_invites_module = stream_interactor.module_manager.get_module(account, Xep.CallInvites.Module.IDENTITY); call_invites_module.call_proposed.connect((from_jid, to_jid, call_id, video_requested, join_methods, message_stanza) => { if (from_jid.equals_bare(account.bare_jid)) return; if (stream_interactor.get_module(MucManager.IDENTITY).is_own_muc_jid(from_jid, account)) return; bool multiparty = false; CallState? call_state = null; foreach (StanzaNode join_method_node in join_methods) { if (join_method_node.name == "muji" && join_method_node.ns_uri == Xep.Muji.NS_URI) { // This is a MUJI invite // Disregard calls from muc history DateTime? delay = Xep.DelayedDelivery.get_time_for_message(message_stanza, from_jid.bare_jid); if (delay != null) return; string? room_jid_str = join_method_node.get_attribute("room"); if (room_jid_str == null) return; Jid room_jid = new Jid(room_jid_str); call_state = create_recv_muji_call(account, call_id, from_jid, room_jid, message_stanza.type_); multiparty = true; break; } else if (join_method_node.name == "jingle" && join_method_node.ns_uri == Xep.CallInvites.NS_URI) { // This is an invite for a direct Jingle session if (stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(from_jid.bare_jid, account)) return; string? sid = join_method_node.get_attribute("sid"); if (sid == null) return; PeerState peer_state = create_received_call(account, from_jid, to_jid, video_requested); peer_state.sid = sid; call_state = call_states[peer_state.call]; jmi_request_peer[call_state.call] = peer_state; break; } } if (call_state == null) return; call_state.we_should_send_audio = true; call_state.we_should_send_video = video_requested; call_state.use_cim = true; call_state.cim_call_id = call_id; call_state.cim_jids_to_inform.add(message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT ? from_jid.bare_jid : from_jid); call_state.cim_message_type = message_stanza.type_; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(from_jid, to_jid, account, message_stanza.type_); if (conversation == null) return; if (call_state.call.direction == Call.DIRECTION_INCOMING) { call_incoming(call_state.call, call_state, conversation, video_requested, multiparty); } else { call_outgoing(call_state.call, call_state, conversation); } }); call_invites_module.call_accepted.connect((from_jid, to_jid, call_id, message_type) => { // Carboned message from our account if (from_jid.equals_bare(account.bare_jid)) { CallState? call_state = get_call_state_by_call_id(account, call_id); if (call_state == null) return; Call call = call_state.call; // Ignore carbon from ourselves if (from_jid.equals(account.full_jid)) return; // We accepted the call from another device call.ourpart = from_jid; call.state = Call.State.OTHER_DEVICE; remove_call_from_datastructures(call); return; } CallState? call_state = get_call_state_by_call_id(account, call_id, from_jid); if (call_state == null) return; Call call = call_state.call; // We proposed the call. This is a message from our peer. if (call.direction == Call.DIRECTION_OUTGOING && to_jid.equals(account.full_jid)) { // We know the full jid of our peer now call_state.rename_peer(jmi_request_peer[call].jid, from_jid); jmi_request_peer[call].call_resource.begin(from_jid); } }); call_invites_module.call_retracted.connect((from_jid, to_jid, call_id, message_type) => { if (from_jid.equals_bare(account.bare_jid)) return; // The call was retracted by the counterpart CallState? call_state = get_call_state_by_call_id(account, call_id, from_jid); if (call_state == null) return; if (call_state.call.state != Call.State.RINGING) { debug("%s tried to retract a call that's in state %s. Ignoring.", from_jid.to_string(), call_state.call.state.to_string()); return; } // TODO prevent other MUC occupants from retracting a call call_state.call.state = Call.State.MISSED; remove_call_from_datastructures(call_state.call); }); call_invites_module.call_rejected.connect((from_jid, to_jid, call_id, message_type) => { // We rejected an invite from another device if (from_jid.equals_bare(account.bare_jid)) { CallState? call_state = get_call_state_by_call_id(account, call_id); if (call_state == null) return; Call call = call_state.call; call.state = Call.State.DECLINED; } if (from_jid.equals_bare(account.bare_jid)) return; debug(@"[%s] %s rejected our MUJI invite", account.bare_jid.to_string(), from_jid.to_string()); }); stream_interactor.module_manager.get_module(account, Xep.Coin.Module.IDENTITY).coin_info_received.connect((jid, info) => { foreach (Call call in call_states.keys) { if (call.counterparts[0].equals_bare(jid)) { conference_info_received(call, info); return; } } }); } } }dino-0.5.0/libdino/src/service/chat_interaction.vala0000664000000000000000000002760214776241610021216 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ChatInteraction : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("chat_interaction"); public string id { get { return IDENTITY.id; } } public signal void focused_in(Conversation conversation); public signal void focused_out(Conversation conversation); private StreamInteractor stream_interactor; private Conversation? selected_conversation; private HashMap last_input_interaction = new HashMap(Conversation.hash_func, Conversation.equals_func); private HashMap last_interface_interaction = new HashMap(Conversation.hash_func, Conversation.equals_func); private bool focus_in = false; public static void start(StreamInteractor stream_interactor) { ChatInteraction m = new ChatInteraction(stream_interactor); stream_interactor.add_module(m); } private ChatInteraction(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; Timeout.add_seconds(30, update_interactions); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(stream_interactor)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(on_message_sent); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(new_item); } public int get_num_unread(Conversation conversation) { Database db = Dino.Application.get_default().db; Qlite.QueryBuilder query = db.content_item.select() .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false); ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to_item != null) { string time = read_up_to_item.time.to_unix().to_string(); string id = read_up_to_item.id.to_string(); query.where(@"time > ? OR (time = ? AND id > ?)", { time, time, id }); } // If it's a new conversation with read_up_to_item == null, all items are new. return (int) query.count(); } public bool is_active_focus(Conversation? conversation = null) { if (conversation != null) { return focus_in && conversation.equals(this.selected_conversation); } else { return focus_in; } } public void on_window_focus_in(Conversation? conversation) { on_conversation_focused(conversation); } public void on_window_focus_out(Conversation? conversation) { on_conversation_unfocused(conversation); } public void on_message_entered(Conversation? conversation) { if (!last_input_interaction.has_key(conversation)) { send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_COMPOSING); } last_input_interaction[conversation] = new DateTime.now_utc(); last_interface_interaction[conversation] = new DateTime.now_utc(); } public void on_message_cleared(Conversation? conversation) { if (last_input_interaction.has_key(conversation)) { last_input_interaction.unset(conversation); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_ACTIVE); } } public void on_conversation_selected(Conversation conversation) { on_conversation_unfocused(selected_conversation); selected_conversation = conversation; on_conversation_focused(conversation); } private void new_item(ContentItem item, Conversation conversation) { bool mark_read = is_active_focus(conversation); if (!mark_read) { MessageItem? message_item = item as MessageItem; if (message_item != null) { if (message_item.message.direction == Message.DIRECTION_SENT) { mark_read = true; } } if (message_item == null) { FileItem? file_item = item as FileItem; if (file_item != null) { if (file_item.file_transfer.direction == FileTransfer.DIRECTION_SENT) { mark_read = true; } } } } if (mark_read) { ContentItem? read_up_to = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to != null) { if (read_up_to.compare(item) < 0) { conversation.read_up_to_item = item.id; } } else { conversation.read_up_to_item = item.id; } } } private void on_message_sent(Entities.Message message, Conversation conversation) { last_input_interaction.unset(conversation); last_interface_interaction.unset(conversation); } private void on_conversation_focused(Conversation? conversation) { focus_in = true; if (conversation == null) return; focused_in(conversation); check_send_read(); ContentItem? latest_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); if (latest_item != null) { conversation.read_up_to_item = latest_item.id; } } private void on_conversation_unfocused(Conversation? conversation) { focus_in = false; if (conversation == null) return; focused_out(conversation); if (last_input_interaction.has_key(conversation)) { send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_PAUSED); last_input_interaction.unset(conversation); } } private void check_send_read() { if (selected_conversation == null) return; Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(selected_conversation); if (message != null && message.direction == Entities.Message.DIRECTION_RECEIVED) { send_chat_marker(message, null, selected_conversation, Xep.ChatMarkers.MARKER_DISPLAYED); } } private bool update_interactions() { for (MapIterator iter = last_input_interaction.map_iterator(); iter.has_next(); iter.next()) { if (!iter.valid && iter.has_next()) iter.next(); Conversation conversation = iter.get_key(); if (last_input_interaction.has_key(conversation) && (new DateTime.now_utc()).difference(last_input_interaction[conversation]) >= 15 * TimeSpan.SECOND) { iter.unset(); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_PAUSED); } } for (MapIterator iter = last_interface_interaction.map_iterator(); iter.has_next(); iter.next()) { if (!iter.valid && iter.has_next()) iter.next(); Conversation conversation = iter.get_key(); if (last_interface_interaction.has_key(conversation) && (new DateTime.now_utc()).difference(last_interface_interaction[conversation]) >= 1.5 * TimeSpan.MINUTE) { iter.unset(); send_chat_state_notification(conversation, Xep.ChatStateNotifications.STATE_GONE); } } return true; } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "FILTER_EMPTY", "STORE_CONTENT_ITEM" }; public override string action_group { get { return "OTHER_NODES"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public ReceivedMessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null) return false; ChatInteraction outer = stream_interactor.get_module(ChatInteraction.IDENTITY); outer.send_delivery_receipt(message, stanza, conversation); // Send chat marker if (message.direction == Entities.Message.DIRECTION_SENT) return false; if (outer.is_active_focus(conversation)) { outer.check_send_read(); outer.send_chat_marker(message, stanza, conversation, Xep.ChatMarkers.MARKER_DISPLAYED); } else { outer.send_chat_marker(message, stanza, conversation, Xep.ChatMarkers.MARKER_RECEIVED); } return false; } } private void send_chat_marker(Entities.Message message, Xmpp.MessageStanza? stanza, Conversation conversation, string marker) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; switch (marker) { case Xep.ChatMarkers.MARKER_RECEIVED: if (stanza != null && Xep.ChatMarkers.Module.requests_marking(stanza) && message.type_ != Message.Type.GROUPCHAT) { if (message.stanza_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from, message.stanza_id, message.get_type_string(), Xep.ChatMarkers.MARKER_RECEIVED); } break; case Xep.ChatMarkers.MARKER_DISPLAYED: if (conversation.get_send_marker_setting(stream_interactor) == Conversation.Setting.ON) { if (message.equals(conversation.read_up_to)) return; conversation.read_up_to = message; if (message.type_ == Message.Type.GROUPCHAT || message.type_ == Message.Type.GROUPCHAT_PM) { if (message.server_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from.bare_jid, message.server_id, message.get_type_string(), Xep.ChatMarkers.MARKER_DISPLAYED); } else { if (message.stanza_id == null) return; stream.get_module(Xep.ChatMarkers.Module.IDENTITY).send_marker(stream, message.from, message.stanza_id, message.get_type_string(), Xep.ChatMarkers.MARKER_DISPLAYED); } } break; } } private void send_delivery_receipt(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (message.direction == Entities.Message.DIRECTION_SENT) return; if (!Xep.MessageDeliveryReceipts.Module.requests_receipt(stanza)) return; if (conversation.type_ == Conversation.Type.GROUPCHAT) return; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { stream.get_module(Xep.MessageDeliveryReceipts.Module.IDENTITY).send_received(stream, message.from, message.stanza_id); } } private void send_chat_state_notification(Conversation conversation, string state) { if (conversation.get_send_typing_setting(stream_interactor) != Conversation.Setting.ON) return; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { string message_type = conversation.type_ == Conversation.Type.GROUPCHAT ? Xmpp.MessageStanza.TYPE_GROUPCHAT : Xmpp.MessageStanza.TYPE_CHAT; stream.get_module(Xep.ChatStateNotifications.Module.IDENTITY).send_state(stream, conversation.counterpart, message_type, state); } } } } dino-0.5.0/libdino/src/service/connection_manager.vala0000664000000000000000000003405014776241610021524 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ConnectionManager : Object { public signal void stream_opened(Account account, XmppStream stream); public signal void stream_attached_modules(Account account, XmppStream stream); public signal void connection_state_changed(Account account, ConnectionState state); public signal void connection_error(Account account, ConnectionError error); public enum ConnectionState { CONNECTED, CONNECTING, DISCONNECTED } private HashMap connections = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_errors = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_ongoing = new HashMap(Account.hash_func, Account.equals_func); private HashMap connection_directly_retry = new HashMap(Account.hash_func, Account.equals_func); private NetworkMonitor? network_monitor; private Login1Manager? login1; private ModuleManager module_manager; public string? log_options; public class ConnectionError { public enum Source { CONNECTION, SASL, TLS, STREAM_ERROR } public enum Reconnect { NOW, LATER, NEVER } public Source source; public string? identifier; public Reconnect reconnect_recomendation { get; set; default=Reconnect.NOW; } public ConnectionError(Source source, string? identifier) { this.source = source; this.identifier = identifier; } } private class Connection { public string uuid { get; set; } public XmppStream? stream { get; set; } public ConnectionState connection_state { get; set; default = ConnectionState.DISCONNECTED; } public DateTime? established { get; set; } public DateTime? last_activity { get; set; } public Connection() { reset(); } public void reset() { if (stream != null) { stream.detach_modules(); stream.disconnect.begin(); } stream = null; established = last_activity = null; uuid = Xmpp.random_uuid(); } public void make_offline() { Xmpp.Presence.Stanza presence = new Xmpp.Presence.Stanza(); presence.type_ = Xmpp.Presence.Stanza.TYPE_UNAVAILABLE; if (stream != null) { stream.get_module(Presence.Module.IDENTITY).send_presence(stream, presence); } } public async void disconnect_account() { make_offline(); if (stream != null) { try { yield stream.disconnect(); } catch (Error e) { debug("Error disconnecting stream: %s", e.message); } } } } public ConnectionManager(ModuleManager module_manager) { this.module_manager = module_manager; network_monitor = GLib.NetworkMonitor.get_default(); if (network_monitor != null) { network_monitor.network_changed.connect(on_network_changed); network_monitor.notify["connectivity"].connect(on_network_changed); } get_login1.begin((_, res) => { login1 = get_login1.end(res); if (login1 != null) { login1.PrepareForSleep.connect(on_prepare_for_sleep); } }); Timeout.add_seconds(60, () => { foreach (Account account in connections.keys) { if (connections[account].last_activity == null || connections[account].last_activity.compare(new DateTime.now_utc().add_minutes(-1)) < 0) { check_reconnect(account); } } return true; }); } public XmppStream? get_stream(Account account) { if (get_state(account) == ConnectionState.CONNECTED) { return connections[account].stream; } return null; } public ConnectionState get_state(Account account) { if (connections.has_key(account)){ return connections[account].connection_state; } return ConnectionState.DISCONNECTED; } public ConnectionError? get_error(Account account) { if (connection_errors.has_key(account)) { return connection_errors[account]; } return null; } public Collection get_managed_accounts() { return connections.keys; } public void connect_account(Account account) { if (!connections.has_key(account)) { connections[account] = new Connection(); connection_ongoing[account] = false; connection_directly_retry[account] = false; connect_stream.begin(account); } else { check_reconnect(account); } } public void make_offline_all() { foreach (Account account in connections.keys) { make_offline(account); } } private void make_offline(Account account) { connections[account].make_offline(); change_connection_state(account, ConnectionState.DISCONNECTED); } public async void disconnect_account(Account account) { if (connections.has_key(account)) { make_offline(account); connections[account].disconnect_account.begin(); connections.unset(account); } } private async void connect_stream(Account account) { if (!connections.has_key(account)) return; debug("[%s] (Maybe) Establishing a new connection", account.bare_jid.to_string()); connection_errors.unset(account); XmppStreamResult stream_result; if (connection_ongoing[account]) { debug("[%s] Connection attempt already in progress. Directly retry if it fails.", account.bare_jid.to_string()); connection_directly_retry[account] = true; return; } else if (connections[account].stream != null) { debug("[%s] Cancelling connecting because there is already a stream", account.bare_jid.to_string()); return; } else { connection_ongoing[account] = true; connection_directly_retry[account] = false; change_connection_state(account, ConnectionState.CONNECTING); stream_result = yield Xmpp.establish_stream(account.bare_jid, module_manager.get_modules(account), log_options, (peer_cert, errors) => { return on_invalid_certificate(account.domainpart, peer_cert, errors); } ); connections[account].stream = stream_result.stream; connection_ongoing[account] = false; } if (stream_result.stream == null) { if (stream_result.tls_errors != null) { set_connection_error(account, new ConnectionError(ConnectionError.Source.TLS, null) { reconnect_recomendation=ConnectionError.Reconnect.NEVER}); return; } debug("[%s] Could not connect", account.bare_jid.to_string()); change_connection_state(account, ConnectionState.DISCONNECTED); check_reconnect(account, connection_directly_retry[account]); return; } XmppStream stream = stream_result.stream; debug("[%s] New connection: %p", account.full_jid.to_string(), stream); connections[account].established = new DateTime.now_utc(); stream.attached_modules.connect((stream) => { stream_attached_modules(account, stream); change_connection_state(account, ConnectionState.CONNECTED); // stream.get_module(Xep.Muji.Module.IDENTITY).join_call(stream, new Jid("test@muc.poez.io"), true); }); stream.get_module(Sasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => { set_connection_error(account, new ConnectionError(ConnectionError.Source.SASL, null)); }); string connection_uuid = connections[account].uuid; stream.received_node.connect(() => { if (connections[account].uuid == connection_uuid) { connections[account].last_activity = new DateTime.now_utc(); } else { warning("Got node for outdated connection"); } }); stream_opened(account, stream); try { yield stream.loop(); } catch (Error e) { debug("[%s %p] Connection error: %s", account.bare_jid.to_string(), stream, e.message); change_connection_state(account, ConnectionState.DISCONNECTED); if (!connections.has_key(account)) return; connections[account].reset(); StreamError.Flag? flag = stream.get_flag(StreamError.Flag.IDENTITY); if (flag != null) { warning(@"[%s %p] Stream Error: %s", account.bare_jid.to_string(), stream, flag.error_type); set_connection_error(account, new ConnectionError(ConnectionError.Source.STREAM_ERROR, flag.error_type)); if (flag.resource_rejected) { account.set_random_resource(); connect_stream.begin(account); return; } } ConnectionError? error = connection_errors[account]; if (error != null && error.source == ConnectionError.Source.SASL) { return; } check_reconnect(account); } } private void check_reconnects() { foreach (Account account in connections.keys) { check_reconnect(account); } } private void check_reconnect(Account account, bool directly_reconnect = false) { if (!connections.has_key(account)) return; bool acked = false; DateTime? last_activity_was = connections[account].last_activity; if (connections[account].stream == null) { Timeout.add_seconds(10, () => { if (!connections.has_key(account)) return false; if (connections[account].stream != null) return false; if (connections[account].last_activity != last_activity_was) return false; connect_stream.begin(account); return false; }); return; } XmppStream stream = connections[account].stream; stream.get_module(Xep.Ping.Module.IDENTITY).send_ping.begin(stream, account.bare_jid.domain_jid, () => { acked = true; if (connections[account].stream != stream) return; change_connection_state(account, ConnectionState.CONNECTED); }); Timeout.add_seconds(10, () => { if (!connections.has_key(account)) return false; if (connections[account].stream != stream) return false; if (acked) return false; if (connections[account].last_activity != last_activity_was) return false; // Reconnect. Nothing gets through the stream. debug("[%s %p] Ping timeouted. Reconnecting", account.bare_jid.to_string(), stream); change_connection_state(account, ConnectionState.DISCONNECTED); connections[account].reset(); connect_stream.begin(account); return false; }); } private bool network_is_online() { /* FIXME: We should also check for connectivity eventually. For more * details on why we don't do it for now, see: * * - https://github.com/dino/dino/pull/236#pullrequestreview-86851793 * - https://bugzilla.gnome.org/show_bug.cgi?id=792240 */ return network_monitor != null && network_monitor.network_available; } private void on_network_changed() { if (network_is_online()) { debug("NetworkMonitor: Network reported online"); check_reconnects(); } else { debug("NetworkMonitor: Network reported offline"); foreach (Account account in connections.keys) { change_connection_state(account, ConnectionState.DISCONNECTED); } } } private async void on_prepare_for_sleep(bool suspend) { foreach (Account account in connections.keys) { change_connection_state(account, ConnectionState.DISCONNECTED); } if (suspend) { debug("Login1: Device suspended"); foreach (Account account in connections.keys) { try { make_offline(account); if (connections[account].stream != null) { yield connections[account].stream.disconnect(); } } catch (Error e) { debug("Error disconnecting stream %p: %s", connections[account].stream, e.message); } } } else { debug("Login1: Device un-suspend"); check_reconnects(); } } private void change_connection_state(Account account, ConnectionState state) { if (connections.has_key(account)) { connections[account].connection_state = state; connection_state_changed(account, state); } } private void set_connection_error(Account account, ConnectionError error) { connection_errors[account] = error; connection_error(account, error); } public static bool on_invalid_certificate(string domain, TlsCertificate peer_cert, TlsCertificateFlags errors) { if (domain.has_suffix(".onion") && errors == TlsCertificateFlags.UNKNOWN_CA) { // It's barely possible for .onion servers to provide a non-self-signed cert. // But that's fine because encryption is provided independently though TOR. warning("Accepting TLS certificate from unknown CA from .onion address %s", domain); return true; } return false; } } } dino-0.5.0/libdino/src/service/contact_model.vala0000664000000000000000000000550414776241610020510 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; public class Dino.Model.ConversationDisplayName : Object { public string display_name { get; set; } } namespace Dino { public class ContactModels : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("contact_models"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private HashMap conversation_models = new HashMap(Conversation.hash_func, Conversation.equals_func); public static void start(StreamInteractor stream_interactor) { ContactModels m = new ContactModels(stream_interactor); stream_interactor.add_module(m); } private ContactModels(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { check_update_models(account, jid, Conversation.Type.GROUPCHAT); }); stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { check_update_models(account, room, Conversation.Type.GROUPCHAT); }); stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { check_update_models(account, jid, Conversation.Type.GROUPCHAT); }); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { check_update_models(account, jid, Conversation.Type.CHAT); }); } private void check_update_models(Account account, Jid jid, Conversation.Type conversation_ty) { var conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, conversation_ty); if (conversation == null) return; var display_name_model = conversation_models[conversation]; if (display_name_model == null) return; display_name_model.display_name = Dino.get_conversation_display_name(stream_interactor, conversation, "%s (%s)"); } public Model.ConversationDisplayName get_display_name_model(Conversation conversation) { if (conversation_models.has_key(conversation)) return conversation_models[conversation]; var model = new Model.ConversationDisplayName(); model.display_name = Dino.get_conversation_display_name(stream_interactor, conversation, "%s (%s)"); conversation_models[conversation] = model; return model; } } }dino-0.5.0/libdino/src/service/content_item_store.vala0000664000000000000000000004266614776241610021613 0ustar rootrootusing Gee; using Dino.Entities; using Qlite; using Xmpp; namespace Dino { public class ContentItemStore : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("content_item_store"); public string id { get { return IDENTITY.id; } } public signal void new_item(ContentItem item, Conversation conversation); private StreamInteractor stream_interactor; private Database db; private HashMap collection_conversations = new HashMap(Conversation.hash_func, Conversation.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { ContentItemStore m = new ContentItemStore(stream_interactor, db); stream_interactor.add_module(m); } public ContentItemStore(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.get_module(FileManager.IDENTITY).received_file.connect(insert_file_transfer); stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect(announce_message); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(announce_message); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect(insert_call); stream_interactor.get_module(Calls.IDENTITY).call_outgoing.connect(insert_call); } public void init(Conversation conversation, ContentItemCollection item_collection) { collection_conversations[conversation] = item_collection; } public void uninit(Conversation conversation, ContentItemCollection item_collection) { collection_conversations.unset(conversation); } private Gee.List get_items_from_query(QueryBuilder select, Conversation conversation) { Gee.TreeSet items = new Gee.TreeSet(ContentItem.compare_func); foreach (var row in select) { ContentItem content_item = get_item_from_row(row, conversation); items.add(content_item); } Gee.List ret = new ArrayList(); foreach (ContentItem item in items) { ret.add(item); } return ret; } private ContentItem get_item_from_row(Row row, Conversation conversation) throws Error { int id = row[db.content_item.id]; int content_type = row[db.content_item.content_type]; int foreign_id = row[db.content_item.foreign_id]; DateTime time = new DateTime.from_unix_utc(row[db.content_item.time]); return get_item(conversation, id, content_type, foreign_id, time); } private ContentItem get_item(Conversation conversation, int id, int content_type, int foreign_id, DateTime time) throws Error { switch (content_type) { case 1: Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(foreign_id, conversation); if (message != null) { var message_item = new MessageItem(message, conversation, id); message_item.time = time; // In case of message corrections, the original time should be used return message_item; } break; case 2: FileTransfer? file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_id(foreign_id, conversation); if (file_transfer != null) { Message? message = null; if (file_transfer.provider == 0 && file_transfer.info != null) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); } var file_item = new FileItem(file_transfer, conversation, id, message); return file_item; } break; case 3: Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(foreign_id, conversation); if (call != null) { var call_item = new CallItem(call, conversation, id); return call_item; } break; default: warning("Unknown content item type: %i", content_type); break; } throw new Error(-1, 0, "Bad content type %i or non existing content item %i", content_type, foreign_id); } public ContentItem? get_item_by_foreign(Conversation conversation, int type, int foreign_id) { QueryBuilder select = db.content_item.select() .with(db.content_item.content_type, "=", type) .with(db.content_item.foreign_id, "=", foreign_id); Gee.List item = get_items_from_query(select, conversation); return item.size > 0 ? item[0] : null; } public ContentItem? get_item_by_id(Conversation conversation, int id) { QueryBuilder select = db.content_item.select() .with(db.content_item.id, "=", id); Gee.List item = get_items_from_query(select, conversation); return item.size > 0 ? item[0] : null; } public string? get_message_id_for_content_item(Conversation conversation, ContentItem content_item) { Message? message = get_message_for_content_item(conversation, content_item); if (message == null) return null; return MessageStorage.get_reference_id(message); } public Jid? get_message_sender_for_content_item(Conversation conversation, ContentItem content_item) { Message? message = get_message_for_content_item(conversation, content_item); if (message == null) return null; // No need to look at edit_to, because it's the same sender JID. return message.from; } public Message? get_message_for_content_item(Conversation conversation, ContentItem content_item) { FileItem? file_item = content_item as FileItem; if (file_item != null) { if (file_item.file_transfer.provider != 0 || file_item.file_transfer.info == null) return null; int message_db_id = int.parse(file_item.file_transfer.info); return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message_db_id, conversation); } MessageItem? message_item = content_item as MessageItem; if (message_item != null) { return message_item.message; } return null; } public ContentItem? get_content_item_for_message_id(Conversation conversation, string message_id) { Row? row = get_content_item_row_for_message_id(conversation, message_id); if (row != null) { return get_item_from_row(row, conversation); } return null; } public int get_content_item_id_for_message_id(Conversation conversation, string message_id) { Row? row = get_content_item_row_for_message_id(conversation, message_id); if (row != null) { return row[db.content_item.id]; } return -1; } private Row? get_content_item_row_for_message_id(Conversation conversation, string message_id) { var content_item_row = db.content_item.select(); Message? message = null; if (conversation.type_ == Conversation.Type.CHAT) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_id, conversation); } else { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(message_id, conversation); } if (message == null) return null; RowOption file_transfer_row = db.file_transfer.select() .with(db.file_transfer.account_id, "=", conversation.account.id) .with(db.file_transfer.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.file_transfer.info, "=", message.id.to_string()) .order_by(db.file_transfer.time, "DESC") .single().row(); if (file_transfer_row.is_present()) { content_item_row.with(db.content_item.foreign_id, "=", file_transfer_row[db.file_transfer.id]) .with(db.content_item.content_type, "=", 2); } else { content_item_row.with(db.content_item.foreign_id, "=", message.id) .with(db.content_item.content_type, "=", 1); } RowOption content_item_row_option = content_item_row.single().row(); if (content_item_row_option.is_present()) { return content_item_row_option.inner; } return null; } public ContentItem? get_latest(Conversation conversation) { Gee.List items = get_n_latest(conversation, 1); if (items.size > 0) { return items.get(0); } return null; } public Gee.List get_n_latest(Conversation conversation, int count) { QueryBuilder select = db.content_item.select() .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "DESC") .order_by(db.content_item.id, "DESC") .limit(count); return get_items_from_query(select, conversation); } // public Gee.List get_latest_meta(Conversation conversation, int count) { // QueryBuilder select = db.content_item.select() // .with(db.content_item.conversation_id, "=", conversation.id) // .with(db.content_item.hide, "=", false) // .order_by(db.content_item.time, "DESC") // .order_by(db.content_item.id, "DESC") // .limit(count); // // var ret = new ArrayList(); // foreach (var row in select) { // var item_meta = new ContentItemMeta() { // id = row[db.content_item.id], // content_type = row[db.content_item.content_type], // foreign_id = row[db.content_item.foreign_id], // time = new DateTime.from_unix_utc(row[db.content_item.time]) // }; // } // return ret; // } public Gee.List get_before(Conversation conversation, ContentItem item, int count) { long time = (long) item.time.to_unix(); QueryBuilder select = db.content_item.select() .where(@"time < ? OR (time = ? AND id < ?)", { time.to_string(), time.to_string(), item.id.to_string() }) .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "DESC") .order_by(db.content_item.id, "DESC") .limit(count); return get_items_from_query(select, conversation); } public Gee.List get_after(Conversation conversation, ContentItem item, int count) { long time = (long) item.time.to_unix(); QueryBuilder select = db.content_item.select() .where(@"time > ? OR (time = ? AND id > ?)", { time.to_string(), time.to_string(), item.id.to_string() }) .with(db.content_item.conversation_id, "=", conversation.id) .with(db.content_item.hide, "=", false) .order_by(db.content_item.time, "ASC") .order_by(db.content_item.id, "ASC") .limit(count); return get_items_from_query(select, conversation); } public void insert_message(Message message, Conversation conversation, bool hide = false) { MessageItem item = new MessageItem(message, conversation, -1); item.id = db.add_content_item(conversation, message.time, message.local_time, 1, message.id, hide); } private void announce_message(Message message, Conversation conversation) { QueryBuilder select = db.content_item.select(); select.with(db.content_item.foreign_id, "=", message.id); select.with(db.content_item.content_type, "=", 1); select.with(db.content_item.hide, "=", false); foreach (Row row in select) { MessageItem item = new MessageItem(message, conversation, row[db.content_item.id]); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); break; } } private void insert_file_transfer(FileTransfer file_transfer, Conversation conversation) { FileItem item = new FileItem(file_transfer, conversation, -1); item.id = db.add_content_item(conversation, file_transfer.time, file_transfer.local_time, 2, file_transfer.id, false); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); } private void insert_call(Call call, CallState call_state, Conversation conversation) { CallItem item = new CallItem(call, conversation, -1); item.id = db.add_content_item(conversation, call.time, call.local_time, 3, call.id, false); if (collection_conversations.has_key(conversation)) { collection_conversations.get(conversation).insert_item(item); } new_item(item, conversation); } public bool get_item_hide(ContentItem content_item) { return db.content_item.row_with(db.content_item.id, content_item.id)[db.content_item.hide, false]; } public void set_item_hide(ContentItem content_item, bool hide) { db.content_item.update() .with(db.content_item.id, "=", content_item.id) .set(db.content_item.hide, hide) .perform(); } } public interface ContentItemCollection : Object { public abstract void insert_item(ContentItem item); public abstract void remove_item(ContentItem item); } public abstract class ContentItem : Object { public int id { get; set; } public string type_ { get; set; } public Jid jid { get; set; } public DateTime time { get; set; } public Encryption encryption { get; set; } public Entities.Message.Marked mark { get; set; } ContentItem(int id, string ty, Jid jid, DateTime time, Encryption encryption, Entities.Message.Marked mark) { this.id = id; this.type_ = ty; this.jid = jid; this.time = time; this.encryption = encryption; this.mark = mark; } public int compare(ContentItem c) { return compare_func(this, c); } public static int compare_func(ContentItem a, ContentItem b) { int res = a.time.compare(b.time); if (res == 0) { res = a.id - b.id > 0 ? 1 : -1; } return res; } } public class MessageItem : ContentItem { public const string TYPE = "message"; public Message message; public Conversation conversation; public MessageItem(Message message, Conversation conversation, int id) { base(id, TYPE, message.from, message.time, message.encryption, message.marked); this.message = message; this.conversation = conversation; message.bind_property("marked", this, "mark"); } } public class FileItem : ContentItem { public const string TYPE = "file"; public FileTransfer file_transfer; public Conversation conversation; public FileItem(FileTransfer file_transfer, Conversation conversation, int id, Message? message = null) { Entities.Message.Marked mark = Entities.Message.Marked.NONE; if (message != null) { mark = message.marked; } else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) { mark = file_to_message_state(file_transfer.state); } base(id, TYPE, file_transfer.from, file_transfer.time, file_transfer.encryption, mark); this.file_transfer = file_transfer; this.conversation = conversation; // TODO those don't work if (message != null) { message.bind_property("marked", this, "mark"); } else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) { file_transfer.bind_property("state", this, "mark", BindingFlags.DEFAULT, (_, from_value, ref to_value) => { to_value = file_to_message_state((FileTransfer.State)from_value.get_enum()); return true; }); } } private static Entities.Message.Marked file_to_message_state(FileTransfer.State state) { switch (state) { case FileTransfer.State.IN_PROGRESS: return Entities.Message.Marked.UNSENT; case FileTransfer.State.COMPLETE: return Entities.Message.Marked.NONE; case FileTransfer.State.NOT_STARTED: return Entities.Message.Marked.UNSENT; case FileTransfer.State.FAILED: return Entities.Message.Marked.WONTSEND; } assert_not_reached(); } } public class CallItem : ContentItem { public const string TYPE = "call"; public Call call; public Conversation conversation; public CallItem(Call call, Conversation conversation, int id) { base(id, TYPE, call.proposer, call.time, call.encryption, Message.Marked.NONE); this.call = call; this.conversation = conversation; call.bind_property("encryption", this, "encryption"); } } } dino-0.5.0/libdino/src/service/conversation_manager.vala0000664000000000000000000002311414776241610022076 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class ConversationManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("conversation_manager"); public string id { get { return IDENTITY.id; } } public signal void conversation_activated(Conversation conversation); public signal void conversation_deactivated(Conversation conversation); private StreamInteractor stream_interactor; private Database db; private HashMap>> conversations = new HashMap>>(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { ConversationManager m = new ConversationManager(stream_interactor, db); stream_interactor.add_module(m); } private ConversationManager(StreamInteractor stream_interactor, Database db) { this.db = db; this.stream_interactor = stream_interactor; stream_interactor.add_module(this); stream_interactor.account_added.connect(on_account_added); stream_interactor.account_removed.connect(on_account_removed); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new MessageListener(stream_interactor)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect(handle_sent_message); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect(handle_new_call); stream_interactor.get_module(Calls.IDENTITY).call_outgoing.connect(handle_new_call); } public Conversation create_conversation(Jid jid, Account account, Conversation.Type? type = null) { assert(conversations.has_key(account)); Jid store_jid = type == Conversation.Type.GROUPCHAT ? jid.bare_jid : jid; // Do we already have a conversation for this jid? if (conversations[account].has_key(store_jid)) { foreach (var conversation in conversations[account][store_jid]) { if (conversation.type_ == type) { return conversation; } } } // Create a new converation Conversation conversation = new Conversation(jid, account, type); // Set encryption for conversation if (type == Conversation.Type.CHAT || (type == Conversation.Type.GROUPCHAT && stream_interactor.get_module(MucManager.IDENTITY).is_private_room(account, jid))) { conversation.encryption = Application.get_default().settings.get_default_encryption(account); } else { conversation.encryption = Encryption.NONE; } add_conversation(conversation); conversation.persist(db); return conversation; } public Conversation? get_conversation_for_message(Entities.Message message) { if (conversations.has_key(message.account)) { if (message.type_ == Entities.Message.Type.CHAT) { return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.CHAT); } else if (message.type_ == Entities.Message.Type.GROUPCHAT) { return create_conversation(message.counterpart.bare_jid, message.account, Conversation.Type.GROUPCHAT); } else if (message.type_ == Entities.Message.Type.GROUPCHAT_PM) { return create_conversation(message.counterpart, message.account, Conversation.Type.GROUPCHAT_PM); } } return null; } public Gee.List get_conversations(Jid jid, Account account) { Gee.List ret = new ArrayList(Conversation.equals_func); Conversation? bare_conversation = get_conversation(jid, account); if (bare_conversation != null) ret.add(bare_conversation); Conversation? full_conversation = get_conversation(jid.bare_jid, account); if (full_conversation != null) ret.add(full_conversation); return ret; } public Conversation? get_conversation(Jid jid, Account account, Conversation.Type? type = null) { if (conversations.has_key(account)) { if (conversations[account].has_key(jid)) { foreach (var conversation in conversations[account][jid]) { if (type == null || conversation.type_ == type) { return conversation; } } } } return null; } public Conversation? approx_conversation_for_stanza(Jid from, Jid to, Account account, string msg_ty) { if (msg_ty == Xmpp.MessageStanza.TYPE_GROUPCHAT) { return get_conversation(from.bare_jid, account, Conversation.Type.GROUPCHAT); } Jid counterpart = from.equals_bare(account.bare_jid) ? to : from; if (msg_ty == Xmpp.MessageStanza.TYPE_CHAT && counterpart.is_full() && get_conversation(counterpart.bare_jid, account, Conversation.Type.GROUPCHAT) != null) { var pm = get_conversation(counterpart, account, Conversation.Type.GROUPCHAT_PM); if (pm != null) return pm; } return get_conversation(counterpart.bare_jid, account, Conversation.Type.CHAT); } public Conversation? get_conversation_by_id(int id) { foreach (HashMap> hm in conversations.values) { foreach (Gee.List hm2 in hm.values) { foreach (Conversation conversation in hm2) { if (conversation.id == id) { return conversation; } } } } return null; } public Gee.List get_active_conversations(Account? account = null) { Gee.List ret = new ArrayList(Conversation.equals_func); foreach (Account account_ in conversations.keys) { if (account != null && !account_.equals(account)) continue; foreach (Gee.List list in conversations[account_].values) { foreach (var conversation in list) { if(conversation.active) ret.add(conversation); } } } return ret; } public void start_conversation(Conversation conversation) { if (conversation.last_active == null) { conversation.last_active = new DateTime.now_utc(); if (conversation.active) conversation_activated(conversation); } if (!conversation.active) { conversation.active = true; conversation_activated(conversation); } } public void close_conversation(Conversation conversation) { if (!conversation.active) return; conversation.active = false; conversation_deactivated(conversation); } private void on_account_added(Account account) { conversations[account] = new HashMap>(Jid.hash_func, Jid.equals_func); foreach (Conversation conversation in db.get_conversations(account)) { add_conversation(conversation); } } private void on_account_removed(Account account) { foreach (Gee.List list in conversations[account].values) { foreach (var conversation in list) { if(conversation.active) conversation_deactivated(conversation); } } conversations.unset(account); } private class MessageListener : Dino.MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "FILTER_EMPTY" }; public override string action_group { get { return "MANAGER"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public MessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { conversation.last_active = message.time; if (stanza != null) { bool is_mam_message = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null; bool is_recent = message.time.compare(new DateTime.now_utc().add_days(-3)) > 0; if (is_mam_message && !is_recent) return false; } stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); return false; } } private void handle_sent_message(Entities.Message message, Conversation conversation) { conversation.last_active = message.time; bool is_recent = message.time.compare(new DateTime.now_utc().add_hours(-24)) > 0; if (is_recent) { start_conversation(conversation); } } private void handle_new_call(Call call, CallState state, Conversation conversation) { conversation.last_active = call.time; start_conversation(conversation); } private void add_conversation(Conversation conversation) { if (!conversations[conversation.account].has_key(conversation.counterpart)) { conversations[conversation.account][conversation.counterpart] = new ArrayList(Conversation.equals_func); } conversations[conversation.account][conversation.counterpart].add(conversation); if (conversation.active) { conversation_activated(conversation); } } } } dino-0.5.0/libdino/src/service/counterpart_interaction_manager.vala0000664000000000000000000002710314776241610024333 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class CounterpartInteractionManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("counterpart_interaction_manager"); public string id { get { return IDENTITY.id; } } public signal void received_state(Conversation conversation, string state); public signal void received_marker(Account account, Jid jid, Entities.Message message, Entities.Message.Marked marker); public signal void received_message_received(Account account, Jid jid, Entities.Message message); public signal void received_message_displayed(Account account, Jid jid, Entities.Message message); private StreamInteractor stream_interactor; private HashMap> typing_since = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap marker_wo_message = new HashMap(); public static void start(StreamInteractor stream_interactor) { CounterpartInteractionManager m = new CounterpartInteractionManager(stream_interactor); stream_interactor.add_module(m); } private CounterpartInteractionManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).message_received.connect((message, conversation) => clear_chat_state(conversation, message.from)); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent_or_received.connect(check_if_got_marker); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect((jid, account) => { foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_conversations(jid, account)) { clear_chat_state(conversation, jid); } }); stream_interactor.stream_negotiated.connect((account) => clear_all_chat_states(account) ); Timeout.add_seconds(60, () => { var one_min_ago = new DateTime.now_utc().add_seconds(-1); foreach (Conversation conversation in typing_since.keys) { ArrayList to_remove = new ArrayList(); foreach (Jid jid in typing_since[conversation].keys) { if (typing_since[conversation][jid].compare(one_min_ago) < 0) { to_remove.add(jid); } } foreach (Jid jid in to_remove) { clear_chat_state(conversation, jid); } } return true; }); } public Gee.List? get_typing_jids(Conversation conversation) { if (stream_interactor.connection_manager.get_state(conversation.account) != ConnectionManager.ConnectionState.CONNECTED) return null; if (!typing_since.has_key(conversation) || typing_since[conversation].size == 0) return null; var jids = new ArrayList(); foreach (Jid jid in typing_since[conversation].keys) { jids.add(jid); } return jids; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.ChatMarkers.Module.IDENTITY).marker_received.connect( (stream, jid, marker, id, message_stanza) => { on_chat_marker_received.begin(account, jid, marker, id, message_stanza); }); stream_interactor.module_manager.get_module(account, Xep.MessageDeliveryReceipts.Module.IDENTITY).receipt_received.connect((stream, jid, id, stanza) => { on_receipt_received(account, jid, id, stanza); }); stream_interactor.module_manager.get_module(account, Xep.ChatStateNotifications.Module.IDENTITY).chat_state_received.connect((stream, jid, state, stanza) => { on_chat_state_received.begin(account, jid, state, stanza); }); } private void clear_chat_state(Conversation conversation, Jid jid) { if (!(typing_since.has_key(conversation) && typing_since[conversation].has_key(jid))) return; typing_since[conversation].unset(jid); received_state(conversation, Xmpp.Xep.ChatStateNotifications.STATE_ACTIVE); } private void clear_all_chat_states(Account account) { foreach (Conversation conversation in typing_since.keys) { if (conversation.account.equals(account)) { received_state(conversation, Xmpp.Xep.ChatStateNotifications.STATE_ACTIVE); typing_since[conversation].clear(); } } } private async void on_chat_state_received(Account account, Jid jid, string state, MessageStanza stanza) { // Don't show our own (other devices) typing notification if (jid.equals_bare(account.bare_jid)) return; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(stanza.from, stanza.to, account, stanza.type_); if (conversation == null) return; // Don't show our own typing notification in MUCs if (conversation.type_ == Conversation.Type.GROUPCHAT) { Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(jid.bare_jid, account); if (own_muc_jid != null && own_muc_jid.equals(jid)) { return; } } if (!typing_since.has_key(conversation)) { typing_since[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } if (state == Xmpp.Xep.ChatStateNotifications.STATE_COMPOSING) { typing_since[conversation][jid] = new DateTime.now_utc(); received_state(conversation, state); } else { clear_chat_state(conversation, jid); } } private async void on_chat_marker_received(Account account, Jid jid, string marker, string stanza_id, MessageStanza message_stanza) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(message_stanza.from, message_stanza.to, account, message_stanza.type_); if (conversation == null) return; handle_chat_marker(conversation, jid, marker, stanza_id); } private void handle_chat_marker(Conversation conversation, Jid jid, string marker, string stanza_id) { // Check if the marker comes from ourselves (own jid or our jid in a MUC) bool own_marker = false; if (conversation.type_ == Conversation.Type.CHAT) { own_marker = conversation.account.bare_jid.to_string() == jid.bare_jid.to_string(); } else { Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(jid.bare_jid, conversation.account); if (own_muc_jid != null && own_muc_jid.equals(jid)) { own_marker = true; } } if (own_marker) { // If we received a display marker from ourselves (other device), set the conversation read up to that message. if (marker != Xep.ChatMarkers.MARKER_DISPLAYED && marker != Xep.ChatMarkers.MARKER_ACKNOWLEDGED) return; Entities.Message? message = null; if (conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(stanza_id, conversation); // Outdated clients might use the message id. Or in MUCs that don't send server ids. if (message == null) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); } } else { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); } if (message == null) return; // Don't move read marker backwards because we get old info from another client if (conversation.read_up_to != null && conversation.read_up_to.local_time.compare(message.local_time) > 0) return; conversation.read_up_to = message; // TODO: This only marks messages as read, not http file transfers. ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); if (content_item == null) return; ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, conversation.read_up_to_item); if (read_up_to_item != null && read_up_to_item.compare(content_item) > 0) return; conversation.read_up_to_item = content_item.id; } else { // We can't currently handle chat markers in MUCs if (conversation.type_ == Conversation.Type.GROUPCHAT) return; Entities.Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza_id, conversation); if (message != null) { switch (marker) { case Xep.ChatMarkers.MARKER_RECEIVED: // If we got a received marker, mark the respective message received. received_message_received(conversation.account, jid, message); message.marked = Entities.Message.Marked.RECEIVED; break; case Xep.ChatMarkers.MARKER_DISPLAYED: // If we got a display marker, set all messages up to that message as read (if we know they've been received). received_message_displayed(conversation.account, jid, message); Gee.List messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation); foreach (Entities.Message m in messages) { if (m.equals(message)) break; if (m.marked == Entities.Message.Marked.RECEIVED) m.marked = Entities.Message.Marked.READ; } message.marked = Entities.Message.Marked.READ; break; } } else { // We might get a marker before the actual message (on catchup). Save the marker. if (marker_wo_message.has_key(stanza_id) && marker_wo_message[stanza_id] == Xep.ChatMarkers.MARKER_DISPLAYED && marker == Xep.ChatMarkers.MARKER_RECEIVED) { return; } marker_wo_message[stanza_id] = marker; } } } private void check_if_got_marker(Entities.Message message, Conversation conversation) { if (marker_wo_message.has_key(message.stanza_id)) { handle_chat_marker(conversation, conversation.counterpart, marker_wo_message[message.stanza_id], message.stanza_id); marker_wo_message.unset(message.stanza_id); } } private void on_receipt_received(Account account, Jid jid, string id, MessageStanza stanza) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(stanza.from, stanza.to, account, stanza.type_); if (conversation == null) return; handle_chat_marker(conversation, jid,Xep.ChatMarkers.MARKER_RECEIVED, id); } } } dino-0.5.0/libdino/src/service/database.vala0000664000000000000000000011475514776241610017452 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Dino.Entities; namespace Dino { public class Database : Qlite.Database { private const int VERSION = 29; public class AccountTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column bare_jid = new Column.Text("bare_jid") { unique = true, not_null = true }; public Column resourcepart = new Column.Text("resourcepart"); public Column password = new Column.Text("password"); public Column alias = new Column.Text("alias"); public Column enabled = new Column.BoolInt("enabled"); public Column roster_version = new Column.Text("roster_version") { min_version=2 }; // no longer used. all usages already removed. remove db column at some point. public Column mam_earliest_synced = new Column.Long("mam_earliest_synced") { min_version=4 }; internal AccountTable(Database db) { base(db, "account"); init({id, bare_jid, resourcepart, password, alias, enabled, roster_version, mam_earliest_synced}); } } public class JidTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column bare_jid = new Column.Text("bare_jid") { unique = true, not_null = true }; internal JidTable(Database db) { base(db, "jid"); init({id, bare_jid}); } } public class EntityTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id"); public Column jid_id = new Column.Integer("jid_id"); public Column resource = new Column.Text("resource"); public Column caps_hash = new Column.Text("caps_hash"); public Column last_seen = new Column.Long("last_seen"); internal EntityTable(Database db) { base(db, "entity"); init({id, account_id, jid_id, resource, caps_hash, last_seen}); unique({account_id, jid_id, resource}, "IGNORE"); } } public class ContentItemTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column conversation_id = new Column.Integer("conversation_id") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column local_time = new Column.Long("local_time") { not_null = true }; public Column content_type = new Column.Integer("content_type") { not_null = true }; public Column foreign_id = new Column.Integer("foreign_id") { not_null = true }; public Column hide = new Column.BoolInt("hide") { default = "0", not_null = true, min_version = 9 }; internal ContentItemTable(Database db) { base(db, "content_item"); init({id, conversation_id, time, local_time, content_type, foreign_id, hide}); index("contentitem_conversation_hide_time_idx", {conversation_id, hide, time}); unique({content_type, foreign_id}, "IGNORE"); } } public class MessageTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column stanza_id = new Column.Text("stanza_id"); public Column server_id = new Column.Text("server_id") { min_version=10 }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column type_ = new Column.Integer("type"); public Column time = new Column.Long("time"); public Column local_time = new Column.Long("local_time"); public Column body = new Column.Text("body"); public Column encryption = new Column.Integer("encryption"); public Column marked = new Column.Integer("marked"); internal MessageTable(Database db) { base(db, "message"); init({id, stanza_id, server_id, account_id, counterpart_id, our_resource, counterpart_resource, direction, type_, time, local_time, body, encryption, marked}); // get latest messages index("message_account_counterpart_time_idx", {account_id, counterpart_id, time}); // deduplication index("message_account_counterpart_stanzaid_idx", {account_id, counterpart_id, stanza_id}); index("message_account_counterpart_serverid_idx", {account_id, counterpart_id, server_id}); // message by marked index("message_account_marked_idx", {account_id, marked}); fts({body}); } } public class BodyMeta : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id"); public Column from_char = new Column.Integer("from_char"); public Column to_char = new Column.Integer("to_char"); public Column info_type = new Column.Text("info_type"); public Column info = new Column.Text("info"); internal BodyMeta(Database db) { base(db, "body_meta"); init({id, message_id, from_char, to_char, info_type, info}); } } public class MessageCorrectionTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id") { unique=true }; public Column to_stanza_id = new Column.Text("to_stanza_id"); internal MessageCorrectionTable(Database db) { base(db, "message_correction"); init({id, message_id, to_stanza_id}); index("message_correction_to_stanza_id_idx", {to_stanza_id}); } } public class ReplyTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column message_id = new Column.Integer("message_id") { not_null = true, unique=true }; public Column quoted_content_item_id = new Column.Integer("quoted_message_id"); public Column quoted_message_stanza_id = new Column.Text("quoted_message_stanza_id"); public Column quoted_message_from = new Column.Text("quoted_message_from"); internal ReplyTable(Database db) { base(db, "reply"); init({id, message_id, quoted_content_item_id, quoted_message_stanza_id, quoted_message_from}); index("reply_quoted_message_stanza_id", {quoted_message_stanza_id}); } } public class RealJidTable : Table { public Column message_id = new Column.Integer("message_id") { primary_key = true }; public Column real_jid = new Column.Text("real_jid"); internal RealJidTable(Database db) { base(db, "real_jid"); init({message_id, real_jid}); } } public class OccupantIdTable : Table { public Column id = new Column.Integer("id") { primary_key = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column last_nick = new Column.Text("last_nick"); public Column jid_id = new Column.Integer("jid_id"); public Column occupant_id = new Column.Text("occupant_id"); internal OccupantIdTable(Database db) { base(db, "occupant_id"); init({id, account_id, last_nick, jid_id, occupant_id}); unique({account_id, jid_id, occupant_id}, "REPLACE"); } } public class UndecryptedTable : Table { public Column message_id = new Column.Integer("message_id"); public Column type_ = new Column.Integer("type"); public Column data = new Column.Text("data"); internal UndecryptedTable(Database db) { base(db, "undecrypted"); init({message_id, type_, data}); } } public class FileTransferTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column file_sharing_id = new Column.Text("file_sharing_id") { min_version=28 }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column time = new Column.Long("time"); public Column local_time = new Column.Long("local_time"); public Column encryption = new Column.Integer("encryption"); public Column file_name = new Column.Text("file_name"); public Column path = new Column.Text("path"); public Column mime_type = new Column.Text("mime_type"); public Column size = new Column.Long("size"); public Column state = new Column.Integer("state"); public Column provider = new Column.Integer("provider"); public Column info = new Column.Text("info"); public Column modification_date = new Column.Long("modification_date") { default = "-1", min_version=28 }; public Column width = new Column.Integer("width") { default = "-1", min_version=28 }; public Column height = new Column.Integer("height") { default = "-1", min_version=28 }; public Column length = new Column.Integer("length") { default = "-1", min_version=28 }; internal FileTransferTable(Database db) { base(db, "file_transfer"); init({id, file_sharing_id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time, encryption, file_name, path, mime_type, size, state, provider, info, modification_date, width, height, length}); } } public class FileHashesTable : Table { public Column id = new Column.Integer("id"); public Column algo = new Column.Text("algo") { not_null = true }; public Column value = new Column.Text("value") { not_null = true }; internal FileHashesTable(Database db) { base(db, "file_hashes"); init({id, algo, value}); unique({id, algo}, "REPLACE"); } } public class FileThumbnailsTable : Table { public Column id = new Column.Integer("id"); public Column uri = new Column.Text("uri") { not_null = true }; public Column mime_type = new Column.Text("mime_type"); public Column width = new Column.Integer("width"); public Column height = new Column.Integer("height"); internal FileThumbnailsTable(Database db) { base(db, "file_thumbnails"); init({id, uri, mime_type, width, height}); } } public class SourcesTable : Table { public Column file_transfer_id = new Column.Integer("file_transfer_id"); public Column type = new Column.Text("type") { not_null = true }; public Column data = new Column.Text("data") { not_null = true }; internal SourcesTable(Database db) { base(db, "sfs_sources"); init({file_transfer_id, type, data}); index("sfs_sources_file_transfer_id_idx", {file_transfer_id}); } } public class CallTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column counterpart_id = new Column.Integer("counterpart_id") { not_null = true }; public Column counterpart_resource = new Column.Text("counterpart_resource"); public Column our_resource = new Column.Text("our_resource"); public Column direction = new Column.BoolInt("direction") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column local_time = new Column.Long("local_time") { not_null = true }; public Column end_time = new Column.Long("end_time"); public Column encryption = new Column.Integer("encryption") { min_version=21 }; public Column state = new Column.Integer("state"); internal CallTable(Database db) { base(db, "call"); init({id, account_id, counterpart_id, counterpart_resource, our_resource, direction, time, local_time, end_time, encryption, state}); } } public class CallCounterpartTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column call_id = new Column.Integer("call_id") { not_null = true }; public Column jid_id = new Column.Integer("jid_id") { not_null = true }; public Column resource = new Column.Text("resource"); internal CallCounterpartTable(Database db) { base(db, "call_counterpart"); init({call_id, jid_id, resource}); index("call_counterpart_call_jid_idx", {call_id}); } } public class ConversationTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column jid_id = new Column.Integer("jid_id") { not_null = true }; public Column resource = new Column.Text("resource") { min_version=1 }; public Column active = new Column.BoolInt("active"); public Column active_last_changed = new Column.Integer("active_last_changed") { not_null=true, default="0", min_version=23 }; public Column last_active = new Column.Long("last_active"); public Column type_ = new Column.Integer("type"); public Column encryption = new Column.Integer("encryption"); public Column read_up_to = new Column.Integer("read_up_to"); public Column read_up_to_item = new Column.Integer("read_up_to_item") { not_null=true, default="-1", min_version=15 }; public Column notification = new Column.Integer("notification") { min_version=3 }; public Column send_typing = new Column.Integer("send_typing") { min_version=3 }; public Column send_marker = new Column.Integer("send_marker") { min_version=3 }; public Column pinned = new Column.Integer("pinned") { default="0", min_version=25 }; internal ConversationTable(Database db) { base(db, "conversation"); init({id, account_id, jid_id, resource, active, active_last_changed, last_active, type_, encryption, read_up_to, read_up_to_item, notification, send_typing, send_marker, pinned}); } } public class AvatarTable : Table { public Column jid_id = new Column.Integer("jid_id"); public Column account_id = new Column.Integer("account_id"); public Column hash = new Column.Text("hash"); public Column type_ = new Column.Integer("type"); internal AvatarTable(Database db) { base(db, "contact_avatar"); init({jid_id, account_id, hash, type_}); unique({jid_id, account_id, type_}, "REPLACE"); } } public class EntityIdentityTable : Table { public Column entity = new Column.Text("entity"); public Column category = new Column.Text("category"); public Column type = new Column.Text("type"); public Column entity_name = new Column.Text("name"); internal EntityIdentityTable(Database db) { base(db, "entity_identity"); init({entity, category, entity_name, type}); unique({entity, category, type}, "IGNORE"); index("entity_identity_idx", {entity}); } } public class EntityFeatureTable : Table { public Column entity = new Column.Text("entity"); public Column feature = new Column.Text("feature"); internal EntityFeatureTable(Database db) { base(db, "entity_feature"); init({entity, feature}); unique({entity, feature}, "IGNORE"); index("entity_feature_idx", {entity}); } } public class RosterTable : Table { public Column account_id = new Column.Integer("account_id"); public Column jid = new Column.Text("jid"); public Column handle = new Column.Text("name"); public Column subscription = new Column.Text("subscription"); public Column ask = new Column.Text("ask") { min_version=29 }; internal RosterTable(Database db) { base(db, "roster"); init({account_id, jid, handle, subscription, ask}); unique({account_id, jid}, "IGNORE"); } } public class MamCatchupTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column server_jid = new Column.Text("server_jid") { not_null = true }; public Column from_id = new Column.Text("from_id") { not_null = true }; public Column from_time = new Column.Long("from_time") { not_null = true }; public Column from_end = new Column.BoolInt("from_end") { not_null = true }; public Column to_id = new Column.Text("to_id") { not_null = true }; public Column to_time = new Column.Long("to_time") { not_null = true }; internal MamCatchupTable(Database db) { base(db, "mam_catchup"); init({id, account_id, server_jid, from_end, from_id, from_time, to_id, to_time}); } } public class ReactionTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column occupant_id = new Column.Integer("occupant_id"); public Column content_item_id = new Column.Integer("content_item_id") { not_null = true }; public Column time = new Column.Long("time") { not_null = true }; public Column jid_id = new Column.Integer("jid_id"); public Column emojis = new Column.Text("emojis"); internal ReactionTable(Database db) { base(db, "reaction"); init({id, account_id, occupant_id, content_item_id, time, jid_id, emojis}); unique({account_id, content_item_id, jid_id}, "REPLACE"); unique({account_id, content_item_id, occupant_id}, "REPLACE"); } } public class SettingsTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column key = new Column.Text("key") { unique = true, not_null = true }; public Column value = new Column.Text("value"); internal SettingsTable(Database db) { base(db, "settings"); init({id, key, value}); } } public class AccountSettingsTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { not_null = true }; public Column key = new Column.Text("key") { not_null = true }; public Column value = new Column.Text("value"); internal AccountSettingsTable(Database db) { base(db, "account_settings"); init({id, account_id, key, value}); unique({account_id, key}, "REPLACE"); } public string? get_value(int account_id, string key) { var row_opt = select({value}) .with(this.account_id, "=", account_id) .with(this.key, "=", key) .single() .row(); if (row_opt.is_present()) return row_opt[value]; return null; } } public class ConversationSettingsTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column conversation_id = new Column.Integer("conversation_id") {not_null=true}; public Column key = new Column.Text("key") { not_null=true }; public Column value = new Column.Text("value"); internal ConversationSettingsTable(Database db) { base(db, "conversation_settings"); init({id, conversation_id, key, value}); index("settings_conversationid_key", { conversation_id, key }, true); } } public AccountTable account { get; private set; } public JidTable jid { get; private set; } public EntityTable entity { get; private set; } public ContentItemTable content_item { get; private set; } public MessageTable message { get; private set; } public BodyMeta body_meta { get; private set; } public ReplyTable reply { get; private set; } public MessageCorrectionTable message_correction { get; private set; } public RealJidTable real_jid { get; private set; } public OccupantIdTable occupantid { get; private set; } public FileTransferTable file_transfer { get; private set; } public FileHashesTable file_hashes { get; private set; } public FileThumbnailsTable file_thumbnails { get; private set; } public SourcesTable sfs_sources { get; private set; } public CallTable call { get; private set; } public CallCounterpartTable call_counterpart { get; private set; } public ConversationTable conversation { get; private set; } public AvatarTable avatar { get; private set; } public EntityIdentityTable entity_identity { get; private set; } public EntityFeatureTable entity_feature { get; private set; } public RosterTable roster { get; private set; } public MamCatchupTable mam_catchup { get; private set; } public ReactionTable reaction { get; private set; } public SettingsTable settings { get; private set; } public AccountSettingsTable account_settings { get; private set; } public ConversationSettingsTable conversation_settings { get; private set; } public Map jid_table_cache = new HashMap(); public Map jid_table_reverse = new HashMap(Jid.hash_func, Jid.equals_func); public Map account_table_cache = new HashMap(); public Database(string fileName) { base(fileName, VERSION); account = new AccountTable(this); jid = new JidTable(this); entity = new EntityTable(this); content_item = new ContentItemTable(this); message = new MessageTable(this); body_meta = new BodyMeta(this); message_correction = new MessageCorrectionTable(this); reply = new ReplyTable(this); occupantid = new OccupantIdTable(this); real_jid = new RealJidTable(this); file_transfer = new FileTransferTable(this); file_hashes = new FileHashesTable(this); file_thumbnails = new FileThumbnailsTable(this); sfs_sources = new SourcesTable(this); call = new CallTable(this); call_counterpart = new CallCounterpartTable(this); conversation = new ConversationTable(this); avatar = new AvatarTable(this); entity_identity = new EntityIdentityTable(this); entity_feature = new EntityFeatureTable(this); roster = new RosterTable(this); mam_catchup = new MamCatchupTable(this); reaction = new ReactionTable(this); settings = new SettingsTable(this); account_settings = new AccountSettingsTable(this); conversation_settings = new ConversationSettingsTable(this); init({ account, jid, entity, content_item, message, body_meta, message_correction, reply, real_jid, occupantid, file_transfer, file_hashes, file_thumbnails, sfs_sources, call, call_counterpart, conversation, avatar, entity_identity, entity_feature, roster, mam_catchup, reaction, settings, account_settings, conversation_settings }); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set database properties: %s", e.message); } } public override void migrate(long oldVersion) { // new table columns are added, outdated columns are still present if (oldVersion < 7) { message.fts_rebuild(); } if (oldVersion < 8) { try { exec(""" insert into content_item (conversation_id, time, local_time, content_type, foreign_id, hide) select conversation.id, message.time, message.local_time, 1, message.id, 0 from message join conversation on message.account_id=conversation.account_id and message.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3) where message.body not in (select info from file_transfer where info not null) and message.id not in (select info from file_transfer where info not null) union select conversation.id, message.time, message.local_time, 2, file_transfer.id, 0 from file_transfer join message on file_transfer.info=message.id join conversation on file_transfer.account_id=conversation.account_id and file_transfer.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3)"""); } catch (Error e) { error("Failed to upgrade to database version 8: %s", e.message); } } if (oldVersion < 9) { try { exec(""" insert into content_item (conversation_id, time, local_time, content_type, foreign_id, hide) select conversation.id, message.time, message.local_time, 1, message.id, 1 from message join conversation on message.account_id=conversation.account_id and message.counterpart_id=conversation.jid_id and message.type=conversation.type+1 and (message.counterpart_resource=conversation.resource or message.type != 3) where message.body in (select info from file_transfer where info not null) or message.id in (select info from file_transfer where info not null)"""); } catch (Error e) { error("Failed to upgrade to database version 9: %s", e.message); } } if (oldVersion < 11) { try { exec(""" insert into mam_catchup (account_id, from_end, from_time, to_time) select id, 1, 0, mam_earliest_synced from account where mam_earliest_synced not null and mam_earliest_synced > 0"""); } catch (Error e) { error("Failed to upgrade to database version 11: %s", e.message); } } if (oldVersion < 12) { try { exec("delete from avatar"); } catch (Error e) { error("Failed to upgrade to database version 12: %s", e.message); } } if (oldVersion < 15) { // Initialize `conversation.read_up_to_item` with the content item id corresponding to the `read_up_to` message. try { exec(" update conversation set read_up_to_item=ifnull(( select content_item.id from content_item where content_item.foreign_id=conversation.read_up_to and content_type=1) , -1);"); } catch (Error e) { error("Failed to upgrade to database version 15: %s", e.message); } } if (oldVersion < 16) { try { exec("DROP TABLE contact_avatar"); avatar.create_table_at_version(VERSION); } catch (Error e) { error("Failed to upgrade to database version 16: %s", e.message); } } if (oldVersion < 17) { try { exec("DROP INDEX IF EXISTS contentitem_localtime_counterpart_idx"); exec("CREATE INDEX IF NOT EXISTS contentitem_conversation_hide_localtime_time_idx ON content_item (conversation_id, hide, local_time, time)"); } catch (Error e) { error("Failed to upgrade to database version 17: %s", e.message); } } if (oldVersion < 18) { try { exec("DROP INDEX IF EXISTS contentitem_conversation_hide_localtime_time_idx"); exec("CREATE INDEX IF NOT EXISTS contentitem_conversation_hide_time_idx ON content_item (conversation_id, hide, time)"); exec("DROP INDEX IF EXISTS message_account_counterpart_localtime_idx"); exec("CREATE INDEX IF NOT EXISTS message_account_counterpart_time_idx ON message (account_id, counterpart_id, time)"); exec("DROP INDEX IF EXISTS filetransfer_localtime_counterpart_idx"); } catch (Error e) { error("Failed to upgrade to database version 18: %s", e.message); } } if (oldVersion < 22) { try { exec("INSERT INTO call_counterpart (call_id, jid_id, resource) SELECT id, counterpart_id, counterpart_resource FROM call"); } catch (Error e) { error("Failed to upgrade to database version 22: %s", e.message); } // exec("ALTER TABLE call RENAME TO call2"); // call.create_table_at_version(VERSION); // exec("INSERT INTO call (id, account_id, our_resource, direction, time, local_time, end_time, encryption, state) // SELECT id, account_id, our_resource, direction, time, local_time, end_time, encryption, state // FROM call2"); // exec("DROP TABLE call2"); } if (oldVersion < 23) { try { exec("ALTER TABLE mam_catchup RENAME TO mam_catchup2"); mam_catchup.create_table_at_version(VERSION); exec("""INSERT INTO mam_catchup (id, account_id, server_jid, from_id, from_time, from_end, to_id, to_time) SELECT mam_catchup2.id, account_id, bare_jid, ifnull(from_id, ""), from_time, ifnull(from_end, 0), ifnull(to_id, ""), to_time FROM mam_catchup2 JOIN account ON mam_catchup2.account_id=account.id"""); exec("DROP TABLE mam_catchup2"); } catch (Error e) { error("Failed to upgrade to database version 23 (mam_catchup): %s", e.message); } try { long active_last_updated = (long) new DateTime.now_utc().to_unix(); exec(@"UPDATE conversation SET active_last_changed=$active_last_updated WHERE active_last_changed=0"); } catch (Error e) { error("Failed to upgrade to database version 23 (conversation): %s", e.message); } } } public ArrayList get_accounts() { ArrayList ret = new ArrayList(Account.equals_func); foreach(Row row in account.select()) { try { Account account = new Account.from_row(this, row); if (account_table_cache.has_key(account.id)) { account = account_table_cache[account.id]; } ret.add(account); account_table_cache[account.id] = account; } catch (InvalidJidError e) { warning("Ignoring account with invalid Jid: %s", e.message); } } return ret; } public Account? get_account_by_id(int id) { if (account_table_cache.has_key(id)) { return account_table_cache[id]; } else { Row? row = account.row_with(account.id, id).inner; if (row != null) { try { Account a = new Account.from_row(this, row); account_table_cache[a.id] = a; return a; } catch (InvalidJidError e) { warning("Ignoring account with invalid Jid: %s", e.message); } } return null; } } public int add_content_item(Conversation conversation, DateTime time, DateTime local_time, int content_type, int foreign_id, bool hide) { return (int) content_item.insert() .value(content_item.conversation_id, conversation.id) .value(content_item.local_time, (long) local_time.to_unix()) .value(content_item.time, (long) time.to_unix()) .value(content_item.content_type, content_type) .value(content_item.foreign_id, foreign_id) .value(content_item.hide, hide) .perform(); } public Gee.List get_messages(Jid jid, Account account, Message.Type? type, int count, DateTime? before, DateTime? after, int id) { QueryBuilder select = message.select(); if (before != null) { if (id > 0) { select.where(@"time < ? OR (time = ? AND message.id < ?)", { before.to_unix().to_string(), before.to_unix().to_string(), id.to_string() }); } else { select.with(message.id, "<", id); } } if (after != null) { if (id > 0) { select.where(@"time > ? OR (time = ? AND message.id > ?)", { after.to_unix().to_string(), after.to_unix().to_string(), id.to_string() }); } else { select.with(message.time, ">", (long) after.to_unix()); } if (id > 0) { select.with(message.id, ">", id); } } else { select.order_by(message.time, "DESC"); } select.with(message.counterpart_id, "=", get_jid_id(jid)) .with(message.account_id, "=", account.id) .limit(count); if (jid.resourcepart != null) { select.with(message.counterpart_resource, "=", jid.resourcepart); } if (type != null) { select.with(message.type_, "=", (int) type); } select.outer_join_with(real_jid, real_jid.message_id, message.id); select.outer_join_with(message_correction, message_correction.message_id, message.id); LinkedList ret = new LinkedList(); foreach (Row row in select) { try { ret.insert(0, new Message.from_row(this, row)); } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } return ret; } public Message? get_message_by_id(int id) { Row? row = message.row_with(message.id, id).inner; if (row != null) { try { return new Message.from_row(this, row); } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } return null; } public ArrayList get_conversations(Account account) { ArrayList ret = new ArrayList(); foreach (Row row in conversation.select().with(conversation.account_id, "=", account.id)) { try { ret.add(new Conversation.from_row(this, row)); } catch (InvalidJidError e) { warning("Ignoring conversation with invalid Jid: %s", e.message); } } return ret; } public int get_jid_id(Jid jid_obj) { var bare_jid = jid_obj.bare_jid; if (jid_table_reverse.has_key(bare_jid)) { return jid_table_reverse[bare_jid]; } else { Row? row = jid.row_with(jid.bare_jid, jid_obj.bare_jid.to_string()).inner; if (row != null) { int id = row[jid.id]; jid_table_cache[id] = bare_jid; jid_table_reverse[bare_jid] = id; return id; } else { return add_jid(jid_obj); } } } public Jid? get_jid_by_id(int id) throws InvalidJidError { if (jid_table_cache.has_key(id)) { return jid_table_cache[id]; } else { string? bare_jid = jid.select({jid.bare_jid}).with(jid.id, "=", id)[jid.bare_jid]; if (bare_jid != null) { Jid jid_parsed = new Jid(bare_jid); jid_table_cache[id] = jid_parsed; // Only store fully normalized Jids for reverse lookup if (jid_parsed.to_string() == bare_jid) { jid_table_reverse[jid_parsed] = id; } return jid_parsed; } return null; } } private int add_jid(Jid jid_obj) { Jid bare_jid = jid_obj.bare_jid; int id = (int) jid.insert().value(jid.bare_jid, bare_jid.to_string()).perform(); jid_table_cache[id] = bare_jid; jid_table_reverse[bare_jid] = id; return id; } } } dino-0.5.0/libdino/src/service/entity_capabilities_storage.vala0000664000000000000000000000475414776241610023454 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep.ServiceDiscovery; namespace Dino { public class EntityCapabilitiesStorage : Xep.EntityCapabilities.Storage, Object { private Database db; private HashMap> features_cache = new HashMap>(); private HashMap identity_cache = new HashMap(); public EntityCapabilitiesStorage(Database db) { this.db = db; } public void store_features(string entity, Gee.List features) { if (features_cache.has_key(entity)) return; foreach (string feature in features) { db.entity_feature.insert() .value(db.entity_feature.entity, entity) .value(db.entity_feature.feature, feature) .perform(); } } public void store_identities(string entity, Gee.Set identities) { foreach (Identity identity in identities) { if (identity.category == Identity.CATEGORY_CLIENT) { db.entity_identity.insert() .value(db.entity_identity.entity, entity) .value(db.entity_identity.category, identity.category) .value(db.entity_identity.type, identity.type_) .value(db.entity_identity.entity_name, identity.name) .perform(); return; } } } public Gee.List get_features(string entity) { Gee.List? features = features_cache[entity]; if (features != null) { return features; } features = new ArrayList(); foreach (Row row in db.entity_feature.select({db.entity_feature.feature}).with(db.entity_feature.entity, "=", entity)) { features.add(row[db.entity_feature.feature]); } features_cache[entity] = features; return features; } public Identity? get_identities(string entity) { Identity? identity = identity_cache[entity]; if (identity != null) { return identity; } RowOption row = db.entity_identity.select().with(db.entity_identity.entity, "=", entity).single().row(); if (row.is_present()) { identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]); } identity_cache[entity] = identity; return identity; } } } dino-0.5.0/libdino/src/service/entity_info.vala0000664000000000000000000002513314776241610020224 0ustar rootrootusing Gee; using Dino.Entities; using Qlite; using Xmpp; using Xmpp.Xep; using Xmpp.Xep.ServiceDiscovery; namespace Dino { public class EntityInfo : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("entity_info"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private EntityCapabilitiesStorage entity_capabilities_storage; private HashMap entity_caps_hashes = new HashMap(Jid.hash_func, Jid.equals_func); private HashMap> entity_features = new HashMap>(); private HashMap> jid_features = new HashMap>(Jid.hash_func, Jid.equals_func); private HashMap> entity_identity = new HashMap>(); private HashMap> jid_identity = new HashMap>(Jid.hash_func, Jid.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { EntityInfo m = new EntityInfo(stream_interactor, db); stream_interactor.add_module(m); } private EntityInfo(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.entity_capabilities_storage = new EntityCapabilitiesStorage(db); stream_interactor.account_added.connect(on_account_added); stream_interactor.connection_manager.stream_opened.connect((account, stream) => { stream.received_features_node.connect(() => { string? hash = EntityCapabilities.get_server_caps_hash(stream); if (hash != null) { entity_caps_hashes[account.bare_jid.domain_jid] = hash; } }); }); stream_interactor.module_manager.initialize_account_modules.connect(initialize_modules); remove_old_entities(); Timeout.add_seconds(60 * 60, () => { remove_old_entities(); return true; }); } public async Gee.Set? get_identities(Account account, Jid jid) { if (jid_identity.has_key(jid)) { return jid_identity[jid]; } string? hash = entity_caps_hashes[jid]; if (hash != null) { Gee.Set? identities = get_stored_identities(hash); if (identities != null) return identities; } ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, hash); if (info_result != null) { return info_result.identities; } return null; } public async Identity? get_identity(Account account, Jid jid) { Gee.Set? identities = yield get_identities(account, jid); if (identities == null) return null; foreach (var identity in identities) { if (identity.category == Identity.CATEGORY_CLIENT) { return identity; } } return null; } public async bool has_feature(Account account, Jid jid, string feature) { int has_feature_cached = has_feature_cached_int(account, jid, feature); if (has_feature_cached != -1) { return has_feature_cached == 1; } ServiceDiscovery.InfoResult? info_result = yield get_info_result(account, jid, entity_caps_hashes[jid]); if (info_result == null) return false; return info_result.features.contains(feature); } public bool has_feature_offline(Account account, Jid jid, string feature) { int ret = has_feature_cached_int(account, jid, feature); if (ret == -1) { return db.entity.select() .with(db.entity.account_id, "=", account.id) .with(db.entity.jid_id, "=", db.get_jid_id(jid)) .with(db.entity.resource, "=", jid.resourcepart ?? "") .join_with(db.entity_feature, db.entity.caps_hash, db.entity_feature.entity) .with(db.entity_feature.feature, "=", feature) .count() > 0; } return ret == 1; } public bool has_feature_cached(Account account, Jid jid, string feature) { return has_feature_cached_int(account, jid, feature) == 1; } private int has_feature_cached_int(Account account, Jid jid, string feature) { if (jid_features.has_key(jid)) { return jid_features[jid].contains(feature) ? 1 : 0; } string? hash = entity_caps_hashes[jid]; if (hash != null) { Gee.List? features = get_stored_features(hash); if (features != null) { return features.contains(feature) ? 1 : 0; } } return -1; } private void on_received_available_presence(Account account, Presence.Stanza presence) { bool is_gc = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(presence.from.bare_jid, account); if (is_gc) return; string? caps_hash = EntityCapabilities.get_caps_hash(presence); if (caps_hash == null) return; db.entity.upsert() .value(db.entity.account_id, account.id, true) .value(db.entity.jid_id, db.get_jid_id(presence.from), true) .value(db.entity.resource, presence.from.resourcepart, true) .value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix()) .value(db.entity.caps_hash, caps_hash) .perform(); if (caps_hash != null) { entity_caps_hashes[presence.from] = caps_hash; } } private void remove_old_entities() { long timestamp = (long)(new DateTime.now_local().add_days(-14)).to_unix(); db.entity.delete().with(db.entity.last_seen, "<", timestamp).perform(); } private void store_features(string entity, Gee.List features) { if (entity_features.has_key(entity)) return; foreach (string feature in features) { db.entity_feature.insert() .value(db.entity_feature.entity, entity) .value(db.entity_feature.feature, feature) .perform(); } entity_features[entity] = features; } private void store_identities(string entity, Gee.Set identities) { foreach (Identity identity in identities) { db.entity_identity.insert() .value(db.entity_identity.entity, entity) .value(db.entity_identity.category, identity.category) .value(db.entity_identity.type, identity.type_) .value(db.entity_identity.entity_name, identity.name) .perform(); } entity_identity[entity] = identities; } private Gee.List? get_stored_features(string entity) { Gee.List? features = entity_features[entity]; if (features != null) { return features; } features = new ArrayList(); foreach (Row row in db.entity_feature.select({db.entity_feature.feature}).with(db.entity_feature.entity, "=", entity)) { features.add(row[db.entity_feature.feature]); } if (features.size == 0) { return null; } entity_features[entity] = features; return features; } private Gee.Set? get_stored_identities(string entity) { Gee.Set? identities = entity_identity[entity]; if (identities != null) { return identities; } identities = new HashSet(Identity.hash_func, Identity.equals_func); var qry = db.entity_identity.select().with(db.entity_identity.entity, "=", entity); foreach (Row row in qry) { var identity = new Identity(row[db.entity_identity.category], row[db.entity_identity.type], row[db.entity_identity.entity_name]); identities.add(identity); } if (identities.size == 0) { return null; } entity_identity[entity] = identities; return identities; } private async ServiceDiscovery.InfoResult? get_info_result(Account account, Jid jid, string? hash = null) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; ServiceDiscovery.InfoResult? info_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_info(stream, jid); if (info_result == null) return null; var computed_hash = EntityCapabilities.Module.compute_hash_for_info_result(info_result); if (hash == null || computed_hash == hash) { db.entity.upsert() .value(db.entity.account_id, account.id, true) .value(db.entity.jid_id, db.get_jid_id(jid), true) .value(db.entity.resource, jid.resourcepart ?? "", true) .value(db.entity.last_seen, (long)(new DateTime.now_local()).to_unix()) .value(db.entity.caps_hash, computed_hash) .perform(); store_features(computed_hash, info_result.features); store_identities(computed_hash, info_result.identities); } else { warning("Claimed entity caps hash from %s doesn't match computed one", jid.to_string()); } jid_features[jid] = info_result.features; jid_identity[jid] = info_result.identities; return info_result; } private void on_account_added(Account account) { var cache = new CapsCacheImpl(account, this); stream_interactor.module_manager.get_module(account, ServiceDiscovery.Module.IDENTITY).cache = cache; stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available.connect((stream, presence) => on_received_available_presence(account, presence)); } private void initialize_modules(Account account, ArrayList modules) { modules.add(new Xep.EntityCapabilities.Module(entity_capabilities_storage)); } } public class CapsCacheImpl : CapsCache, Object { private Account account; private EntityInfo entity_info; public CapsCacheImpl(Account account, EntityInfo entity_info) { this.account = account; this.entity_info = entity_info; } public async bool has_entity_feature(Jid jid, string feature) { return yield entity_info.has_feature(account, jid, feature); } public async Gee.Set get_entity_identities(Jid jid) { return yield entity_info.get_identities(account, jid); } } } dino-0.5.0/libdino/src/service/fallback_body.vala0000664000000000000000000000525214776241610020451 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.FallbackBody : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("fallback-body"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private ReceivedMessageListener received_message_listener; public static void start(StreamInteractor stream_interactor, Database db) { FallbackBody m = new FallbackBody(stream_interactor, db); stream_interactor.add_module(m); } private FallbackBody(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.received_message_listener = new ReceivedMessageListener(stream_interactor, db); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "Quote"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; private Database db; public ReceivedMessageListener(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Gee.List fallbacks = Xep.FallbackIndication.get_fallbacks(stanza); if (fallbacks.is_empty) return false; foreach (var fallback in fallbacks) { if (fallback.ns_uri != Xep.Replies.NS_URI) continue; // TODO what if it's not } message.set_fallbacks(fallbacks); return false; } } public static string get_quoted_fallback_body(ContentItem content_item) { string fallback = "> "; if (content_item.type_ == MessageItem.TYPE) { Message? quoted_message = ((MessageItem) content_item).message; fallback += Dino.message_body_without_reply_fallback(quoted_message); fallback = fallback.replace("\n", "\n> "); } else if (content_item.type_ == FileItem.TYPE) { FileTransfer? quoted_file = ((FileItem) content_item).file_transfer; fallback += quoted_file.file_name; } fallback += "\n"; return fallback; } }dino-0.5.0/libdino/src/service/file_manager.vala0000664000000000000000000005577114776241610020321 0ustar rootrootusing Gdk; using Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; namespace Dino { public class FileManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("file"); public string id { get { return IDENTITY.id; } } public signal void upload_available(Account account); public signal void received_file(FileTransfer file_transfer, Conversation conversation); private StreamInteractor stream_interactor; private Database db; private Gee.List file_senders = new ArrayList(); private Gee.List file_encryptors = new ArrayList(); private Gee.List file_decryptors = new ArrayList(); private Gee.List file_providers = new ArrayList(); private Gee.List file_metadata_providers = new ArrayList(); public StatelessFileSharing sfs { owned get { return stream_interactor.get_module(StatelessFileSharing.IDENTITY); } private set { } } public static void start(StreamInteractor stream_interactor, Database db) { FileManager m = new FileManager(stream_interactor, db); stream_interactor.add_module(m); } public static string get_storage_dir() { return Path.build_filename(Dino.get_storage_dir(), "files"); } private FileManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; DirUtils.create_with_parents(get_storage_dir(), 0700); this.add_provider(new JingleFileProvider(stream_interactor)); this.add_sender(new JingleFileSender(stream_interactor)); this.add_metadata_provider(new GenericFileMetadataProvider()); this.add_metadata_provider(new ImageFileMetadataProvider()); } public const int HTTP_PROVIDER_ID = 0; public const int SFS_PROVIDER_ID = 2; public FileProvider? select_file_provider(FileTransfer file_transfer) { bool http_usable = file_transfer.provider == SFS_PROVIDER_ID; foreach (FileProvider file_provider in this.file_providers) { if (file_transfer.provider == file_provider.get_id()) { return file_provider; } if (http_usable && file_provider.get_id() == HTTP_PROVIDER_ID) { return file_provider; } } return null; } public async HashMap get_file_size_limits(Conversation conversation) { HashMap ret = new HashMap(); foreach (FileSender sender in file_senders) { ret[sender.get_id()] = yield sender.get_file_size_limit(conversation); } return ret; } public async void send_file(File file, Conversation conversation) { FileTransfer file_transfer = new FileTransfer(); file_transfer.account = conversation.account; file_transfer.counterpart = conversation.counterpart; if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; } else { file_transfer.ourpart = conversation.account.full_jid; } file_transfer.direction = FileTransfer.DIRECTION_SENT; file_transfer.time = new DateTime.now_utc(); file_transfer.local_time = new DateTime.now_utc(); file_transfer.encryption = conversation.encryption; Xep.FileMetadataElement.FileMetadata metadata = new Xep.FileMetadataElement.FileMetadata(); foreach (FileMetadataProvider file_metadata_provider in this.file_metadata_providers) { if (file_metadata_provider.supports_file(file)) { yield file_metadata_provider.fill_metadata(file, metadata); } } file_transfer.file_metadata = metadata; try { file_transfer.input_stream = yield file.read_async(); yield save_file(file_transfer); stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); conversation.last_active = file_transfer.time; received_file(file_transfer, conversation); } catch (Error e) { file_transfer.state = FileTransfer.State.FAILED; warning("Error saving outgoing file: %s", e.message); return; } try { var file_meta = new FileMeta(); file_meta.size = file_transfer.size; file_meta.mime_type = file_transfer.mime_type; FileSender file_sender = null; FileEncryptor file_encryptor = null; foreach (FileSender sender in file_senders) { if (yield sender.can_send(conversation, file_transfer)) { if (file_transfer.encryption == Encryption.NONE || yield sender.can_encrypt(conversation, file_transfer)) { file_sender = sender; break; } else { foreach (FileEncryptor encryptor in file_encryptors) { if (encryptor.can_encrypt_file(conversation, file_transfer)) { file_encryptor = encryptor; break; } } if (file_encryptor != null) { file_sender = sender; break; } } } } if (file_sender == null) { throw new FileSendError.UPLOAD_FAILED("No sender/encryptor combination available"); } if (file_encryptor != null) { file_meta = file_encryptor.encrypt_file(conversation, file_transfer); } FileSendData file_send_data = yield file_sender.prepare_send_file(conversation, file_transfer, file_meta); if (file_encryptor != null) { file_send_data = file_encryptor.preprocess_send_file(conversation, file_transfer, file_send_data, file_meta); } file_transfer.state = FileTransfer.State.IN_PROGRESS; // Update current download progress in the FileTransfer LimitInputStream? limit_stream = file_transfer.input_stream as LimitInputStream; if (limit_stream == null) { limit_stream = new LimitInputStream(file_transfer.input_stream, file_meta.size); file_transfer.input_stream = limit_stream; } if (limit_stream != null) { limit_stream.bind_property("retrieved-bytes", file_transfer, "transferred-bytes", BindingFlags.SYNC_CREATE); } yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta); file_transfer.state = FileTransfer.State.COMPLETE; } catch (Error e) { warning("Send file error: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } } public async void download_file(FileTransfer file_transfer) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); FileProvider? file_provider = this.select_file_provider(file_transfer); yield download_file_internal(file_provider, file_transfer, conversation); } public async bool is_upload_available(Conversation? conversation) { if (conversation == null) return false; foreach (FileSender file_sender in file_senders) { if (yield file_sender.is_upload_available(conversation)) return true; } return false; } public void add_provider(FileProvider file_provider) { file_providers.add(file_provider); file_provider.file_incoming.connect((info, from, time, local_time, conversation, receive_data, file_meta) => { handle_incoming_file.begin(file_provider, info, from, time, local_time, conversation, receive_data, file_meta); }); } public void add_sender(FileSender file_sender) { file_senders.add(file_sender); file_sender.upload_available.connect((account) => { upload_available(account); }); file_senders.sort((a, b) => { return (int) (b.get_priority() - a.get_priority()); }); } public void add_file_encryptor(FileEncryptor encryptor) { file_encryptors.add(encryptor); } public void add_file_decryptor(FileDecryptor decryptor) { file_decryptors.add(decryptor); } public void add_metadata_provider(FileMetadataProvider file_metadata_provider) { file_metadata_providers.add(file_metadata_provider); } public bool is_sender_trustworthy(FileTransfer file_transfer, Conversation conversation) { if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return true; Jid relevant_jid = conversation.counterpart; if (conversation.type_ == Conversation.Type.GROUPCHAT) { relevant_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(file_transfer.from, conversation.account); } if (relevant_jid == null) return false; bool in_roster = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, relevant_jid) != null; return in_roster; } private async FileMeta get_file_meta(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation, FileReceiveData receive_data_) throws FileReceiveError { FileReceiveData receive_data = receive_data_; FileMeta file_meta = file_provider.get_file_meta(file_transfer); if (file_meta.size == -1) { foreach (FileDecryptor file_decryptor in file_decryptors) { if (file_decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data); break; } } file_meta = yield file_provider.get_meta_info(file_transfer, receive_data, file_meta); file_transfer.size = (int)file_meta.size; file_transfer.file_name = file_meta.file_name; file_transfer.mime_type = file_meta.mime_type; } return file_meta; } private async void download_file_internal(FileProvider file_provider, FileTransfer file_transfer, Conversation conversation) { try { // Get meta info FileReceiveData? receive_data = file_provider.get_file_receive_data(file_transfer); if (receive_data == null) { warning("Don't have download data (yet)"); return; } FileDecryptor? file_decryptor = null; foreach (FileDecryptor decryptor in file_decryptors) { if (decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { file_decryptor = decryptor; break; } } if (file_decryptor != null) { receive_data = file_decryptor.prepare_get_meta_info(conversation, file_transfer, receive_data); } FileMeta file_meta = yield get_file_meta(file_provider, file_transfer, conversation, receive_data); // Download and decrypt file file_transfer.state = FileTransfer.State.IN_PROGRESS; if (file_decryptor != null) { file_meta = file_decryptor.prepare_download_file(conversation, file_transfer, receive_data, file_meta); } InputStream download_input_stream = yield file_provider.download(file_transfer, receive_data, file_meta); InputStream input_stream = download_input_stream; if (file_decryptor != null) { input_stream = yield file_decryptor.decrypt_file(input_stream, conversation, file_transfer, receive_data); } // Update current download progress in the FileTransfer LimitInputStream? limit_stream = download_input_stream as LimitInputStream; if (limit_stream != null) { limit_stream.bind_property("retrieved-bytes", file_transfer, "transferred-bytes", BindingFlags.SYNC_CREATE); } // Save file string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name; File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); // libsoup doesn't properly support splicing OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); uint8[] buffer = new uint8[1024]; ssize_t read; while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) { buffer.length = (int) read; yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable); buffer.length = 1024; } yield input_stream.close_async(Priority.LOW, file_transfer.cancellable); yield os.close_async(Priority.LOW, file_transfer.cancellable); // Verify the hash of the downloaded file, if it is known var supported_hashes = Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes); if (!supported_hashes.is_empty) { var checksum_types = new ArrayList(); var hashes = new HashMap(); foreach (var hash in supported_hashes) { var checksum_type = Xep.CryptographicHashes.hash_string_to_type(hash.algo); checksum_types.add(checksum_type); hashes[checksum_type] = hash.val; } var computed_hashes = yield compute_file_hashes(file, checksum_types); foreach (var checksum_type in hashes.keys) { if (hashes[checksum_type] != computed_hashes[checksum_type]) { warning("Hash of downloaded file does not equal advertised hash, discarding: %s. %s should be %s, was %s", file_transfer.file_name, checksum_type.to_string(), hashes[checksum_type], computed_hashes[checksum_type]); FileUtils.remove(file.get_path()); file_transfer.state = FileTransfer.State.FAILED; return; } } } file_transfer.path = file.get_basename(); FileInfo file_info = file_transfer.get_file().query_info("*", FileQueryInfoFlags.NONE); file_transfer.mime_type = file_info.get_content_type(); file_transfer.state = FileTransfer.State.COMPLETE; } catch (IOError.CANCELLED e) { print("cancelled\n"); } catch (Error e) { warning("Error downloading file: %s", e.message); if (file_transfer.provider == 0 || file_transfer.provider == FileManager.SFS_PROVIDER_ID) { file_transfer.state = FileTransfer.State.NOT_STARTED; } else { file_transfer.state = FileTransfer.State.FAILED; } } } public FileTransfer create_file_transfer_from_provider_incoming(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) { FileTransfer file_transfer = new FileTransfer(); file_transfer.account = conversation.account; file_transfer.counterpart = file_transfer.direction == FileTransfer.DIRECTION_RECEIVED ? from : conversation.counterpart; if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; file_transfer.direction = from.equals(file_transfer.ourpart) ? FileTransfer.DIRECTION_SENT : FileTransfer.DIRECTION_RECEIVED; } else { if (from.equals_bare(conversation.account.bare_jid)) { file_transfer.ourpart = from; file_transfer.direction = FileTransfer.DIRECTION_SENT; } else { file_transfer.ourpart = conversation.account.full_jid; file_transfer.direction = FileTransfer.DIRECTION_RECEIVED; } } file_transfer.time = time; file_transfer.local_time = local_time; file_transfer.provider = file_provider.get_id(); file_transfer.file_name = file_meta.file_name; file_transfer.size = (int)file_meta.size; file_transfer.info = info; var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta); if (encryption != Encryption.NONE) file_transfer.encryption = encryption; foreach (FileDecryptor decryptor in file_decryptors) { if (decryptor.can_decrypt_file(conversation, file_transfer, receive_data)) { file_transfer.encryption = decryptor.get_encryption(); } } return file_transfer; } private async void handle_incoming_file(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) { FileTransfer file_transfer = create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta); stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); if (is_sender_trustworthy(file_transfer, conversation)) { try { yield get_file_meta(file_provider, file_transfer, conversation, receive_data); } catch (Error e) { warning("Error downloading file: %s", e.message); file_transfer.state = FileTransfer.State.FAILED; } if (file_transfer.size >= 0 && file_transfer.size < 5000000) { download_file_internal.begin(file_provider, file_transfer, conversation, (_, res) => { download_file_internal.end(res); }); } } conversation.last_active = file_transfer.time; received_file(file_transfer, conversation); } private async void save_file(FileTransfer file_transfer) throws FileSendError { try { string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name; File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE | OutputStreamSpliceFlags.CLOSE_TARGET); file_transfer.state = FileTransfer.State.COMPLETE; file_transfer.path = filename; file_transfer.input_stream = new LimitInputStream(yield file.read_async(), file_transfer.size); } catch (Error e) { throw new FileSendError.SAVE_FAILED("Saving file error: %s".printf(e.message)); } } } public errordomain FileSendError { ENCRYPTION_FAILED, UPLOAD_FAILED, SAVE_FAILED } // Get rid of this Error and pass IoErrors instead - DOWNLOAD_FAILED already removed public errordomain FileReceiveError { GET_METADATA_FAILED, DECRYPTION_FAILED } public class FileMeta { public int64 size = -1; public string? mime_type = null; public string? file_name = null; public Encryption encryption = Encryption.NONE; } public class HttpFileMeta : FileMeta { public Message message; } public class FileSendData { } public class HttpFileSendData : FileSendData { public string url_down { get; set; } public string url_up { get; set; } public HashMap headers { get; set; } public bool encrypt_message { get; set; default=true; } } public class FileReceiveData { } public class HttpFileReceiveData : FileReceiveData { public string url { get; set; } } public interface FileProvider : Object { public signal void file_incoming(string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta); public abstract Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta); public abstract FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError; public abstract FileReceiveData? get_file_receive_data(FileTransfer file_transfer); public abstract async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError; public abstract async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws IOError; public abstract int get_id(); } public interface FileSender : Object { public signal void upload_available(Account account); public abstract async bool is_upload_available(Conversation conversation); public abstract async long get_file_size_limit(Conversation conversation); public abstract async bool can_send(Conversation conversation, FileTransfer file_transfer); public abstract async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError; public abstract async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError; public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer); public abstract int get_id(); public abstract float get_priority(); } public interface FileEncryptor : Object { public abstract bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer); public abstract FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError; public abstract FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError; } public interface FileDecryptor : Object { public abstract Encryption get_encryption(); public abstract FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data); public abstract FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta); public abstract bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data); public abstract async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError; } } dino-0.5.0/libdino/src/service/file_transfer_storage.vala0000664000000000000000000001117314776241610022243 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class FileTransferStorage : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("file_store"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap files_by_db_id = new WeakMap(); private WeakMap files_by_message_id = new WeakMap(); private WeakMap files_by_message_and_file_id = new WeakMap(); public static void start(StreamInteractor stream_interactor, Database db) { FileTransferStorage m = new FileTransferStorage(stream_interactor, db); stream_interactor.add_module(m); } private FileTransferStorage(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_file(FileTransfer file_transfer) { file_transfer.persist(db); cache_file(file_transfer); } public FileTransfer? get_file_by_id(int id, Conversation conversation) { FileTransfer? file_transfer = files_by_db_id[id]; if (file_transfer != null) { return file_transfer; } RowOption row_option = db.file_transfer.select().with(db.file_transfer.id, "=", id).row(); return create_file_from_row_opt(row_option, conversation); } // Http file transfers store the corresponding message id in the `info` field public FileTransfer? get_file_by_message_id(int id, Conversation conversation) { FileTransfer? file_transfer = files_by_message_id[id]; if (file_transfer != null) { return file_transfer; } RowOption row_option = db.file_transfer.select() .with(db.file_transfer.info, "=", id.to_string()) .single() .row(); return create_file_from_row_opt(row_option, conversation); } public FileTransfer get_files_by_message_and_file_id(int message_id, string file_sharing_id, Conversation conversation) { string combined_identifier = message_id.to_string() + file_sharing_id; FileTransfer? file_transfer = files_by_message_and_file_id[combined_identifier]; if (file_transfer == null) { RowOption row_option = db.file_transfer.select() .with(db.file_transfer.info, "=", message_id.to_string()) .with(db.file_transfer.file_sharing_id, "=", file_sharing_id) .single() .row(); file_transfer = create_file_from_row_opt(row_option, conversation); } // There can be collisions in the combined identifier, check it's the correct FileTransfer if (file_transfer != null && file_transfer.info == message_id.to_string() && file_transfer.file_sharing_id == file_sharing_id) { return file_transfer; } return null; } private FileTransfer? create_file_from_row_opt(RowOption row_opt, Conversation conversation) { if (!row_opt.is_present()) return null; try { FileTransfer file_transfer = new FileTransfer.from_row(db, row_opt.inner, FileManager.get_storage_dir()); if (conversation.type_.is_muc_semantic()) { file_transfer.ourpart = conversation.counterpart.with_resource(file_transfer.ourpart.resourcepart); } cache_file(file_transfer); return file_transfer; } catch (InvalidJidError e) { warning("Got file transfer with invalid Jid: %s", e.message); } return null; } private void cache_file(FileTransfer file_transfer) { files_by_db_id[file_transfer.id] = file_transfer; if (file_transfer.info != null && file_transfer.info != "") { files_by_message_id[int.parse(file_transfer.info)] = file_transfer; if (file_transfer.file_sharing_id != null && file_transfer.info != null) { string combined_identifier = file_transfer.info + file_transfer.file_sharing_id; files_by_message_and_file_id[combined_identifier] = file_transfer; } } } } }dino-0.5.0/libdino/src/service/history_sync.vala0000664000000000000000000007265614776241610020446 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; public class Dino.HistorySync { private StreamInteractor stream_interactor; private Database db; public HashMap> current_catchup_id = new HashMap>(Account.hash_func, Account.equals_func); public WeakMap sync_streams = new WeakMap(Account.hash_func, Account.equals_func); public HashMap> cancellables = new HashMap>(Account.hash_func, Account.equals_func); public HashMap> mam_times = new HashMap>(); public HashMap hitted_range = new HashMap(); // Server ID of the latest message of the previous segment public HashMap catchup_until_id = new HashMap(Account.hash_func, Account.equals_func); // Time of the latest message of the previous segment public HashMap catchup_until_time = new HashMap(Account.hash_func, Account.equals_func); private HashMap> stanzas = new HashMap>(); public class HistorySync(Database db, StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect((account, stream) => { if (current_catchup_id.has_key(account)) { debug("MAM: [%s] Reset catchup_id", account.bare_jid.to_string()); current_catchup_id[account].clear(); } }); } public bool process(Account account, Xmpp.MessageStanza message_stanza) { var mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message_stanza); if (mam_flag != null) { process_mam_message(account, message_stanza, mam_flag); return true; } else { update_latest_db_range(account, message_stanza); return false; } } public void update_latest_db_range(Account account, Xmpp.MessageStanza message_stanza) { Jid mam_server = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(message_stanza.from.bare_jid, account) ? message_stanza.from.bare_jid : account.bare_jid; if (!current_catchup_id.has_key(account) || !current_catchup_id[account].has_key(mam_server)) return; string? stanza_id = UniqueStableStanzaIDs.get_stanza_id(message_stanza, mam_server); if (stanza_id == null) return; db.mam_catchup.update() .with(db.mam_catchup.id, "=", current_catchup_id[account][mam_server]) .set(db.mam_catchup.to_time, (long)new DateTime.now_utc().to_unix()) .set(db.mam_catchup.to_id, stanza_id) .perform(); } public void process_mam_message(Account account, Xmpp.MessageStanza message_stanza, Xmpp.MessageArchiveManagement.MessageFlag mam_flag) { Jid mam_server = mam_flag.sender_jid; Jid message_author = message_stanza.from; // MUC servers may only send MAM messages from that MUC bool is_muc_mam = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(mam_server, account) && message_author.equals_bare(mam_server); bool from_our_server = mam_server.equals_bare(account.bare_jid); if (!is_muc_mam && !from_our_server) { warning("Received alleged MAM message from %s, ignoring", mam_server.to_string()); return; } if (!stanzas.has_key(mam_flag.query_id)) stanzas[mam_flag.query_id] = new ArrayList(); stanzas[mam_flag.query_id].add(message_stanza); } private void on_unprocessed_message(Account account, XmppStream stream, MessageStanza message) { // Check that it's a legit MAM server bool is_muc_mam = stream_interactor.get_module(MucManager.IDENTITY).might_be_groupchat(message.from, account); bool from_our_server = message.from.equals_bare(account.bare_jid); if (!is_muc_mam && !from_our_server) return; // Get the server time of the message and store it in `mam_times` string? id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", "id"); if (id == null) return; StanzaNode? delay_node = message.stanza.get_deep_subnode(Xmpp.MessageArchiveManagement.NS_URI + ":result", StanzaForwarding.NS_URI + ":forwarded", DelayedDelivery.NS_URI + ":delay"); if (delay_node == null) { warning("MAM result did not contain delayed time %s", message.stanza.to_string()); return; } DateTime? time = DelayedDelivery.get_time_for_node(delay_node); if (time == null) return; mam_times[account][id] = time; // Check if this is the target message string? query_id = message.stanza.get_deep_attribute(Xmpp.MessageArchiveManagement.NS_URI + ":result", Xmpp.MessageArchiveManagement.NS_URI + ":queryid"); if (query_id != null && id == catchup_until_id[account]) { debug("[%s] Hitted range (id) %s", account.bare_jid.to_string(), id); hitted_range[query_id] = -2; } } public void on_server_id_duplicate(Account account, Xmpp.MessageStanza message_stanza, Entities.Message message) { Xmpp.MessageArchiveManagement.MessageFlag? mam_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message_stanza); if (mam_flag == null) return; // debug(@"MAM: [%s] Hitted range duplicate server id. id %s qid %s", account.bare_jid.to_string(), message.server_id, mam_flag.query_id); if (catchup_until_time.has_key(account) && mam_flag.server_time.compare(catchup_until_time[account]) < 0) { hitted_range[mam_flag.query_id] = -1; // debug(@"MAM: [%s] In range (time) %s < %s", account.bare_jid.to_string(), mam_flag.server_time.to_string(), catchup_until_time[account].to_string()); } } public async void fetch_everything(Account account, Jid mam_server, Cancellable? cancellable = null, DateTime until_earliest_time = new DateTime.from_unix_utc(0)) { debug("Fetch everything for %s %s", mam_server.to_string(), until_earliest_time != null ? @"(until $until_earliest_time)" : ""); RowOption latest_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); Row? latest_row = latest_row_opt.is_present() ? latest_row_opt.inner : null; Row? new_row = yield fetch_latest_page(account, mam_server, latest_row, until_earliest_time, cancellable); if (new_row != null) { current_catchup_id[account][mam_server] = new_row[db.mam_catchup.id]; } else if (latest_row != null) { current_catchup_id[account][mam_server] = latest_row[db.mam_catchup.id]; } // Set the previous and current row Row? previous_row = null; Row? current_row = null; if (new_row != null) { current_row = new_row; previous_row = latest_row; } else if (latest_row != null) { current_row = latest_row; RowOption previous_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, "<", current_row[db.mam_catchup.from_time]) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); previous_row = previous_row_opt.is_present() ? previous_row_opt.inner : null; } // Fetch messages between two db ranges and merge them while (current_row != null && previous_row != null) { if (current_row[db.mam_catchup.from_end]) return; debug("[%s] Fetching between ranges %s - %s", mam_server.to_string(), previous_row[db.mam_catchup.to_time].to_string(), current_row[db.mam_catchup.from_time].to_string()); current_row = yield fetch_between_ranges(account, mam_server, previous_row, current_row, cancellable); if (current_row == null) return; RowOption previous_row_opt = db.mam_catchup.select() .with(db.mam_catchup.account_id, "=", account.id) .with(db.mam_catchup.server_jid, "=", mam_server.to_string()) .with(db.mam_catchup.to_time, "<", current_row[db.mam_catchup.from_time]) .with(db.mam_catchup.to_time, ">=", (long) until_earliest_time.to_unix()) .order_by(db.mam_catchup.to_time, "DESC") .single().row(); previous_row = previous_row_opt.is_present() ? previous_row_opt.inner : null; } // We're at the earliest range. Try to expand it even further back. if (current_row == null || current_row[db.mam_catchup.from_end]) return; // We don't want to fetch before the earliest range over and over again in MUCs if it's after until_earliest_time. // For now, don't query if we are within a week of until_earliest_time if (until_earliest_time != null && current_row[db.mam_catchup.from_time] > until_earliest_time.add(-TimeSpan.DAY * 7).to_unix()) return; yield fetch_before_range(account, mam_server, current_row, until_earliest_time); } // Fetches the latest page (up to previous db row). Extends the previous db row if it was reached, creates a new row otherwise. public async Row? fetch_latest_page(Account account, Jid mam_server, Row? latest_row, DateTime? until_earliest_time, Cancellable? cancellable = null) { debug("[%s | %s] Fetching latest page", account.bare_jid.to_string(), mam_server.to_string()); int latest_row_id = -1; DateTime latest_message_time = until_earliest_time; string? latest_message_id = null; if (latest_row != null) { latest_row_id = latest_row[db.mam_catchup.id]; latest_message_time = (new DateTime.from_unix_utc(latest_row[db.mam_catchup.to_time])).add_minutes(-5); latest_message_id = latest_row[db.mam_catchup.to_id]; // Make sure we only fetch to until_earliest_time if latest_message_time is further back if (until_earliest_time != null && latest_message_time.compare(until_earliest_time) < 0) { latest_message_time = until_earliest_time.add_minutes(-5); latest_message_id = null; } } var query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_latest(mam_server, latest_message_time, latest_message_id); PageRequestResult page_result = yield get_mam_page(account, query_params, null, cancellable); debug("[%s | %s] Latest page result: %s", account.bare_jid.to_string(), mam_server.to_string(), page_result.page_result.to_string()); if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled) { return null; } // Catchup finished within first page. Update latest db entry. if (latest_row_id != -1 && page_result.page_result in new PageResult[] { PageResult.TargetReached, PageResult.NoMoreMessages }) { if (page_result.stanzas == null) return null; string latest_mam_id = page_result.query_result.last; long latest_mam_time = (long) mam_times[account][latest_mam_id].to_unix(); var query = db.mam_catchup.update() .with(db.mam_catchup.id, "=", latest_row_id) .set(db.mam_catchup.to_time, latest_mam_time) .set(db.mam_catchup.to_id, latest_mam_id); if (page_result.page_result == PageResult.NoMoreMessages) { // If the server doesn't have more messages, store that this range is at its end. query.set(db.mam_catchup.from_end, true); } query.perform(); return null; } if (page_result.query_result.first == null || page_result.query_result.last == null) { return null; } // Either we need to fetch more pages or this is the first db entry ever debug("[%s | %s] Creating new db range for latest page", account.bare_jid.to_string(), mam_server.to_string()); string from_id = page_result.query_result.first; string to_id = page_result.query_result.last; if (!mam_times[account].has_key(from_id) || !mam_times[account].has_key(to_id)) { debug("Missing from/to id %s %s", from_id, to_id); return null; } long from_time = (long) mam_times[account][from_id].to_unix(); long to_time = (long) mam_times[account][to_id].to_unix(); int new_row_id = (int) db.mam_catchup.insert() .value(db.mam_catchup.account_id, account.id) .value(db.mam_catchup.server_jid, mam_server.to_string()) .value(db.mam_catchup.from_id, from_id) .value(db.mam_catchup.from_time, from_time) .value(db.mam_catchup.from_end, page_result.page_result == PageResult.NoMoreMessages) .value(db.mam_catchup.to_id, to_id) .value(db.mam_catchup.to_time, to_time) .perform(); return db.mam_catchup.select().with(db.mam_catchup.id, "=", new_row_id).single().row().inner; } /** Fetches messages between the end of `earlier_range` and start of `later_range` ** Merges the `earlier_range` db row into the `later_range` db row. ** @return The resulting range comprising `earlier_range`, `later_rage`, and everything in between. null if fetching/merge failed. **/ private async Row? fetch_between_ranges(Account account, Jid mam_server, Row earlier_range, Row later_range, Cancellable? cancellable = null) { int later_range_id = (int) later_range[db.mam_catchup.id]; DateTime earliest_time = new DateTime.from_unix_utc(earlier_range[db.mam_catchup.to_time]); DateTime latest_time = new DateTime.from_unix_utc(later_range[db.mam_catchup.from_time]); debug("[%s | %s] Fetching between %s (%s) and %s (%s)", account.bare_jid.to_string(), mam_server.to_string(), earliest_time.to_string(), earlier_range[db.mam_catchup.to_id], latest_time.to_string(), later_range[db.mam_catchup.from_id]); var query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_between(mam_server, earliest_time, earlier_range[db.mam_catchup.to_id], latest_time, later_range[db.mam_catchup.from_id]); PageRequestResult page_result = yield fetch_query(account, query_params, later_range_id, cancellable); if (page_result.page_result == PageResult.TargetReached || page_result.page_result == PageResult.NoMoreMessages) { debug("[%s | %s] Merging range %i into %i", account.bare_jid.to_string(), mam_server.to_string(), earlier_range[db.mam_catchup.id], later_range_id); // Merge earlier range into later one. db.mam_catchup.update() .with(db.mam_catchup.id, "=", later_range_id) .set(db.mam_catchup.from_time, earlier_range[db.mam_catchup.from_time]) .set(db.mam_catchup.from_id, earlier_range[db.mam_catchup.from_id]) .set(db.mam_catchup.from_end, earlier_range[db.mam_catchup.from_end]) .perform(); db.mam_catchup.delete().with(db.mam_catchup.id, "=", earlier_range[db.mam_catchup.id]).perform(); // Return the updated version of the later range return db.mam_catchup.select().with(db.mam_catchup.id, "=", later_range_id).single().row().inner; } return null; } private async void fetch_before_range(Account account, Jid mam_server, Row range, DateTime? until_earliest_time, Cancellable? cancellable = null) { DateTime latest_time = new DateTime.from_unix_utc(range[db.mam_catchup.from_time]); string latest_id = range[db.mam_catchup.from_id]; debug("[%s | %s] Fetching before range < %s, %s", account.bare_jid.to_string(), mam_server.to_string(), latest_time.to_string(), latest_id); Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params; if (until_earliest_time == null) { query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_before(mam_server, latest_time, latest_id); } else { query_params = new Xmpp.MessageArchiveManagement.V2.MamQueryParams.query_between( mam_server, until_earliest_time, null, latest_time, latest_id ); } yield fetch_query(account, query_params, range[db.mam_catchup.id], cancellable); } /** * Iteratively fetches all pages returned for a query (until a PageResult other than MorePagesAvailable is returned) * @return The last PageRequestResult result **/ private async PageRequestResult fetch_query(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, int db_id, Cancellable? cancellable = null) { debug("[%s | %s] Fetch query %s - %s", account.bare_jid.to_string(), query_params.mam_server.to_string(), query_params.start != null ? query_params.start.to_string() : "", query_params.end != null ? query_params.end.to_string() : ""); PageRequestResult? page_result = null; do { page_result = yield get_mam_page(account, query_params, page_result, cancellable); debug("[%s | %s] Page result %s (got stanzas: %s)", account.bare_jid.to_string(), query_params.mam_server.to_string(), page_result.page_result.to_string(), (page_result.stanzas != null).to_string()); if (page_result.page_result == PageResult.Error || page_result.page_result == PageResult.Cancelled || page_result.query_result.first == null) return page_result; string earliest_mam_id = page_result.query_result.first; long earliest_mam_time = (long)mam_times[account][earliest_mam_id].to_unix(); debug("Updating %s to %s, %s", query_params.mam_server.to_string(), earliest_mam_time.to_string(), earliest_mam_id); var query = db.mam_catchup.update() .with(db.mam_catchup.id, "=", db_id) .set(db.mam_catchup.from_time, earliest_mam_time) .set(db.mam_catchup.from_id, earliest_mam_id); if (page_result.page_result == PageResult.NoMoreMessages) { // If the server doesn't have more messages, store that this range is at its end. query.set(db.mam_catchup.from_end, true); } query.perform(); } while (page_result.page_result == PageResult.MorePagesAvailable); return page_result; } enum PageResult { MorePagesAvailable, TargetReached, NoMoreMessages, Error, Cancelled } /** * prev_page_result: null if this is the first page request **/ private async PageRequestResult get_mam_page(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, PageRequestResult? prev_page_result, Cancellable? cancellable = null) { XmppStream stream = stream_interactor.get_stream(account); Xmpp.MessageArchiveManagement.QueryResult query_result = null; if (prev_page_result == null) { query_result = yield Xmpp.MessageArchiveManagement.V2.query_archive(stream, query_params, cancellable); } else { query_result = yield Xmpp.MessageArchiveManagement.V2.page_through_results(stream, query_params, prev_page_result.query_result, cancellable); } return yield process_query_result(account, query_params, query_result, cancellable); } private async PageRequestResult process_query_result(Account account, Xmpp.MessageArchiveManagement.V2.MamQueryParams query_params, Xmpp.MessageArchiveManagement.QueryResult query_result, Cancellable? cancellable = null) { PageResult page_result = PageResult.MorePagesAvailable; if (query_result.malformed || query_result.error) { page_result = PageResult.Error; } // We wait until all the messages from the page are processed (and we got the `mam_times` from them) Idle.add(process_query_result.callback, Priority.LOW); yield; // We might have successfully reached the target or the server doesn't have all messages stored anymore // If it's the former, we'll overwrite the value with PageResult.MorePagesAvailable below. if (query_result.complete) { page_result = PageResult.NoMoreMessages; } string query_id = query_params.query_id; string? after_id = query_params.start_id; var stanzas_for_query = stanzas.has_key(query_id) && !stanzas[query_id].is_empty ? stanzas[query_id] : null; if (cancellable != null && cancellable.is_cancelled()) { stanzas.unset(query_id); return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } if (stanzas_for_query != null) { // Check it we reached our target (from_id) foreach (Xmpp.MessageStanza message in stanzas_for_query) { Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); if (mam_message_flag != null && mam_message_flag.mam_id != null) { if (after_id != null && mam_message_flag.mam_id == after_id) { // Successfully fetched the whole range yield send_messages_back_into_pipeline(account, query_id, cancellable); if (cancellable != null && cancellable.is_cancelled()) { return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } return new PageRequestResult(PageResult.TargetReached, query_result, stanzas_for_query); } } } if (hitted_range.has_key(query_id) && hitted_range[query_id] == -2) { // Message got filtered out by xmpp-vala, but succesful range fetch nevertheless yield send_messages_back_into_pipeline(account, query_id); if (cancellable != null && cancellable.is_cancelled()) { return new PageRequestResult(PageResult.Cancelled, query_result, stanzas_for_query); } return new PageRequestResult(PageResult.TargetReached, query_result, stanzas_for_query); } } yield send_messages_back_into_pipeline(account, query_id); if (cancellable != null && cancellable.is_cancelled()) { page_result = PageResult.Cancelled; } return new PageRequestResult(page_result, query_result, stanzas_for_query); } private async void send_messages_back_into_pipeline(Account account, string query_id, Cancellable? cancellable = null) { if (!stanzas.has_key(query_id)) return; foreach (Xmpp.MessageStanza message in stanzas[query_id]) { if (cancellable != null && cancellable.is_cancelled()) break; yield stream_interactor.get_module(MessageProcessor.IDENTITY).run_pipeline_announce(account, message); } stanzas.unset(query_id); } private void on_account_added(Account account) { cleanup_db_ranges(db, account); mam_times[account] = new HashMap(); stream_interactor.connection_manager.stream_attached_modules.connect((account, stream) => { if (!current_catchup_id.has_key(account)) { current_catchup_id[account] = new HashMap(Jid.hash_func, Jid.equals_func); } else { current_catchup_id[account].clear(); } }); stream_interactor.module_manager.get_module(account, Xmpp.MessageArchiveManagement.Module.IDENTITY).feature_available.connect((stream) => { consider_fetch_everything(account, stream); }); stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_message_unprocessed.connect((stream, message) => { on_unprocessed_message(account, stream, message); }); } private void consider_fetch_everything(Account account, XmppStream stream) { if (sync_streams.has(account, stream)) return; debug("[%s] MAM available", account.bare_jid.to_string()); sync_streams[account] = stream; if (!cancellables.has_key(account)) { cancellables[account] = new HashMap(); } if (cancellables[account].has_key(account.bare_jid)) { cancellables[account][account.bare_jid].cancel(); } cancellables[account][account.bare_jid] = new Cancellable(); fetch_everything.begin(account, account.bare_jid, cancellables[account][account.bare_jid], new DateTime.from_unix_utc(0), (_, res) => { fetch_everything.end(res); cancellables[account].unset(account.bare_jid); }); } public static void cleanup_db_ranges(Database db, Account account) { var ranges = new HashMap>(Jid.hash_func, Jid.equals_func); foreach (Row row in db.mam_catchup.select().with(db.mam_catchup.account_id, "=", account.id)) { var mam_range = new MamRange(); mam_range.id = row[db.mam_catchup.id]; mam_range.server_jid = new Jid(row[db.mam_catchup.server_jid]); mam_range.from_time = row[db.mam_catchup.from_time]; mam_range.from_id = row[db.mam_catchup.from_id]; mam_range.from_end = row[db.mam_catchup.from_end]; mam_range.to_time = row[db.mam_catchup.to_time]; mam_range.to_id = row[db.mam_catchup.to_id]; if (!ranges.has_key(mam_range.server_jid)) ranges[mam_range.server_jid] = new ArrayList(); ranges[mam_range.server_jid].add(mam_range); } var to_delete = new ArrayList(); foreach (Jid server_jid in ranges.keys) { foreach (var range1 in ranges[server_jid]) { if (to_delete.contains(range1)) continue; foreach (MamRange range2 in ranges[server_jid]) { debug("[%s | %s] | %s - %s vs %s - %s", account.bare_jid.to_string(), server_jid.to_string(), range1.from_time.to_string(), range1.to_time.to_string(), range2.from_time.to_string(), range2.to_time.to_string()); if (range1 == range2 || to_delete.contains(range2)) continue; // Check if range2 is a subset of range1 // range1: ##################### // range2: ###### if (range1.from_time <= range2.from_time && range1.to_time >= range2.to_time) { warning("Removing db range which is a subset of %li-%li", range1.from_time, range1.to_time); to_delete.add(range2); continue; } // Check if range2 is an extension of range1 (towards earlier) // range1: ##################### // range2: ############### if (range1.from_time <= range2.to_time <= range1.to_time && range2.from_time <= range1.from_time) { warning("Removing db range that overlapped %li-%li (towards earlier)", range1.from_time, range1.to_time); db.mam_catchup.update() .with(db.mam_catchup.id, "=", range1.id) .set(db.mam_catchup.from_id, range2.from_id) .set(db.mam_catchup.from_time, range2.from_time) .set(db.mam_catchup.from_end, range2.from_end) .perform(); to_delete.add(range2); continue; } } } } foreach (MamRange row in to_delete) { db.mam_catchup.delete().with(db.mam_catchup.id, "=", row.id).perform(); warning("Removing db range %s %li-%li", row.server_jid.to_string(), row.from_time, row.to_time); } } class MamRange { public int id; public Jid server_jid; public long from_time; public string from_id; public bool from_end; public long to_time; public string to_id; } class PageRequestResult { public Gee.List stanzas { get; set; } public PageResult page_result { get; set; } public Xmpp.MessageArchiveManagement.QueryResult query_result { get; set; } public PageRequestResult(PageResult page_result, Xmpp.MessageArchiveManagement.QueryResult query_result, Gee.List? stanzas) { this.page_result = page_result; this.query_result = query_result; this.stanzas = stanzas; } } }dino-0.5.0/libdino/src/service/jingle_file_transfers.vala0000664000000000000000000002377114776241610022241 0ustar rootrootusing Gdk; using Gee; using Xmpp; using Dino.Entities; namespace Dino { public interface JingleFileEncryptionHelper : Object { public abstract bool can_transfer(Conversation conversation); public abstract async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid = null); public abstract string? get_precondition_name(Conversation conversation, FileTransfer file_transfer); public abstract Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer); public abstract Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer); } public class JingleFileEncryptionHelperTransferOnly : JingleFileEncryptionHelper, Object { public bool can_transfer(Conversation conversation) { return true; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { return false; } public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) { return null; } public Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer) { return null; } public Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer) { return Encryption.NONE; } } public class JingleFileHelperRegistry { private static JingleFileHelperRegistry INSTANCE; public static JingleFileHelperRegistry instance { get { if (INSTANCE == null) { INSTANCE = new JingleFileHelperRegistry(); INSTANCE.add_encryption_helper(Encryption.NONE, new JingleFileEncryptionHelperTransferOnly()); } return INSTANCE; } } internal HashMap encryption_helpers = new HashMap(); public void add_encryption_helper(Encryption encryption, JingleFileEncryptionHelper helper) { encryption_helpers[encryption] = helper; } public JingleFileEncryptionHelper? get_encryption_helper(Encryption encryption) { if (encryption_helpers.has_key(encryption)) { return encryption_helpers[encryption]; } return null; } } public class JingleFileProvider : FileProvider, Object { private StreamInteractor stream_interactor; private HashMap file_transfers = new HashMap(); public JingleFileProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); } public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError { var file_meta = new FileMeta(); file_meta.file_name = file_transfer.file_name; file_meta.size = file_transfer.size; return file_meta; } public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) { return new FileReceiveData(); } public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { return file_meta; } public Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info]; if (jingle_file_transfer == null) { warning("Could not determine jingle encryption - transfer data not available anymore"); return Encryption.NONE; } foreach (JingleFileEncryptionHelper helper in JingleFileHelperRegistry.instance.encryption_helpers.values) { var encryption = helper.get_encryption(jingle_file_transfer); if (encryption != Encryption.NONE) return encryption; } return Encryption.NONE; } public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws IOError { // TODO(hrxi) What should happen if `stream == null`? XmppStream? stream = stream_interactor.get_stream(file_transfer.account); Xmpp.Xep.JingleFileTransfer.FileTransfer? jingle_file_transfer = file_transfers[file_transfer.info]; if (jingle_file_transfer == null) { throw new IOError.FAILED("Transfer data not available anymore"); } yield jingle_file_transfer.accept(stream); return new LimitInputStream(jingle_file_transfer.stream, file_meta.size); } public int get_id() { return 1; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xmpp.Xep.JingleFileTransfer.Module.IDENTITY).file_incoming.connect((stream, jingle_file_transfer) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jingle_file_transfer.peer.bare_jid, account); if (conversation == null) return; string id = random_uuid(); file_transfers[id] = jingle_file_transfer; FileMeta file_meta = new FileMeta(); file_meta.size = jingle_file_transfer.size; file_meta.file_name = jingle_file_transfer.file_name; var time = new DateTime.now_utc(); var from = jingle_file_transfer.peer.bare_jid; file_incoming(id, from, time, time, conversation, new FileReceiveData(), file_meta); }); } } public class JingleFileSender : FileSender, Object { private StreamInteractor stream_interactor; public JingleFileSender(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public async bool is_upload_available(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(conversation.encryption); if (helper == null) return false; if (!helper.can_transfer(conversation)) return false; XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; Gee.List? resources = stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart); if (resources == null) return false; foreach (Jid full_jid in resources) { if (yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { return true; } } return false; } public async long get_file_size_limit(Conversation conversation) { if (yield can_send_conv(conversation)) { return int.MAX; } return -1; } public async bool can_send(Conversation conversation, FileTransfer file_transfer) { return yield can_send_conv(conversation); } private async bool can_send_conv(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return false; // No file specific restrictions apply to Jingle file transfers return yield is_upload_available(conversation); } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); if (helper == null) return false; return yield helper.can_encrypt(conversation, file_transfer); } public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError { if (file_meta is HttpFileMeta) { throw new FileSendError.UPLOAD_FAILED("Cannot upload http file meta over Jingle"); } return new FileSendData(); } public async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError { XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream available"); JingleFileEncryptionHelper? helper = JingleFileHelperRegistry.instance.get_encryption_helper(file_transfer.encryption); bool must_encrypt = helper != null && yield helper.can_encrypt(conversation, file_transfer); // TODO(hrxi): Prioritization of transports (and resources?). foreach (Jid full_jid in stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart)) { if (full_jid.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid)) { continue; } if (!yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).is_available(stream, full_jid)) { continue; } if (must_encrypt && !yield helper.can_encrypt(conversation, file_transfer, full_jid)) { continue; } string? precondition_name = null; Object? precondition_options = null; if (must_encrypt) { precondition_name = helper.get_precondition_name(conversation, file_transfer); precondition_options = helper.get_precondition_options(conversation, file_transfer); if (precondition_name == null) { throw new FileSendError.ENCRYPTION_FAILED("Should have created a precondition, but did not"); } } try { yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream(stream, full_jid, file_transfer.input_stream, file_transfer.server_file_name, file_meta.size, precondition_name, precondition_options); } catch (Error e) { throw new FileSendError.UPLOAD_FAILED(@"offer_file_stream failed: $(e.message)"); } return; } } public int get_id() { return 1; } public float get_priority() { return 50; } } } dino-0.5.0/libdino/src/service/message_correction.vala0000664000000000000000000002121414776241610021544 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; namespace Dino { public class MessageCorrection : StreamInteractionModule, MessageListener { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_correction"); public string id { get { return IDENTITY.id; } } public signal void received_correction(ContentItem content_item); private StreamInteractor stream_interactor; private Database db; private HashMap> last_messages = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap outstanding_correction_nodes = new HashMap(); public static void start(StreamInteractor stream_interactor, Database db) { MessageCorrection m = new MessageCorrection(stream_interactor, db); stream_interactor.add_module(m); } public MessageCorrection(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(this); stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(check_add_correction_node); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect((jid, account) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, account, Conversation.Type.GROUPCHAT); if (conversation != null) { if (last_messages.has_key(conversation)) last_messages[conversation].unset(jid); } }); } public void set_correction(Conversation conversation, Message message, Message old_message) { string reference_stanza_id = old_message.edit_to ?? old_message.stanza_id; outstanding_correction_nodes[message.stanza_id] = reference_stanza_id; db.message_correction.insert() .value(db.message_correction.message_id, message.id) .value(db.message_correction.to_stanza_id, reference_stanza_id) .perform(); db.content_item.update() .with(db.content_item.foreign_id, "=", old_message.id) .with(db.content_item.content_type, "=", 1) .set(db.content_item.foreign_id, message.id) .perform(); } public bool is_own_correction_allowed(Conversation conversation, Message message) { string stanza_id = message.edit_to ?? message.stanza_id; Jid? own_jid = null; if (conversation.type_ == Conversation.Type.CHAT) { own_jid = conversation.account.full_jid; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); } if (own_jid == null) return false; return last_messages.has_key(conversation) && last_messages[conversation].has_key(own_jid) && last_messages[conversation][own_jid].stanza_id == stanza_id; } private void check_add_correction_node(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (outstanding_correction_nodes.has_key(message.stanza_id)) { LastMessageCorrection.set_replace_id(message_stanza, outstanding_correction_nodes[message.stanza_id]); outstanding_correction_nodes.unset(message.stanza_id); } else { if (!last_messages.has_key(conversation)) { last_messages[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } last_messages[conversation][message.from] = message; } } public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY" }; public override string action_group { get { return "CORRECTION"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) { // Don't process messages or corrections from MUC history or MUC MAM DateTime? mam_delay = Xep.DelayedDelivery.get_time_for_message(stanza, message.from.bare_jid); if (mam_delay != null) return false; if (Xmpp.MessageArchiveManagement.MessageFlag.get_flag(stanza) != null) return false; } string? replace_id = Xep.LastMessageCorrection.get_replace_id(stanza); if (replace_id == null) { if (!last_messages.has_key(conversation)) { last_messages[conversation] = new HashMap(Jid.hash_func, Jid.equals_func); } last_messages[conversation][message.from] = message; return false; } if (!last_messages.has_key(conversation) || !last_messages[conversation].has_key(message.from)) return false; Message original_message = last_messages[conversation][message.from]; if (original_message.stanza_id != replace_id) return false; int message_id_to_be_updated = get_latest_correction_message_id(conversation.account.id, replace_id, db.get_jid_id(message.counterpart), message.counterpart.resourcepart); if (message_id_to_be_updated == -1) { message_id_to_be_updated = original_message.id; } db.message_correction.insert() .value(db.message_correction.message_id, message.id) .value(db.message_correction.to_stanza_id, replace_id) .perform(); int current_correction_message_id = get_latest_correction_message_id(conversation.account.id, replace_id, db.get_jid_id(message.counterpart), message.counterpart.resourcepart); if (current_correction_message_id != message_id_to_be_updated) { db.content_item.update() .with(db.content_item.foreign_id, "=", message_id_to_be_updated) .with(db.content_item.content_type, "=", 1) .set(db.content_item.foreign_id, current_correction_message_id) .perform(); message.edit_to = replace_id; on_received_correction(conversation, current_correction_message_id); return true; } return false; } public void on_received_correction(Conversation conversation, int message_id) { ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message_id); if (content_item != null) { received_correction(content_item); } } private int get_latest_correction_message_id(int account_id, string stanza_id, int counterpart_jid_id, string? counterpart_resource) { var qry = db.message_correction.select({db.message.id}) .join_with(db.message, db.message.id, db.message_correction.message_id) .with(db.message.account_id, "=", account_id) .with(db.message.counterpart_id, "=", counterpart_jid_id) .with(db.message_correction.to_stanza_id, "=", stanza_id) .order_by(db.message.time, "DESC"); if (counterpart_resource != null) { qry.with(db.message.counterpart_resource, "=", counterpart_resource); } RowOption row = qry.single().row(); if (row.is_present()) { return row[db.message.id]; } return -1; } private void on_account_added(Account account) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); foreach (Conversation conversation in conversations) { if (conversation.type_ != Conversation.Type.CHAT) continue; HashMap last_conversation_messages = new HashMap(Jid.hash_func, Jid.equals_func); Gee.List messages = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages(conversation); for (int i = messages.size - 1; i > 0; i--) { Message message = messages[i]; if (!last_conversation_messages.has_key(message.from) && message.edit_to == null) { last_conversation_messages[message.from] = message; } } last_messages[conversation] = last_conversation_messages; } } } } dino-0.5.0/libdino/src/service/message_processor.vala0000664000000000000000000006100014776241610021411 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; using Qlite; namespace Dino { public class MessageProcessor : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_processor"); public string id { get { return IDENTITY.id; } } public signal void message_received(Entities.Message message, Conversation conversation); public signal void build_message_stanza(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation); public signal void pre_message_send(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation); public signal void message_sent(Entities.Message message, Conversation conversation); public signal void message_sent_or_received(Entities.Message message, Conversation conversation); public signal void history_synced(Account account); public HistorySync history_sync; public MessageListenerHolder received_pipeline = new MessageListenerHolder(); private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { MessageProcessor m = new MessageProcessor(stream_interactor, db); stream_interactor.add_module(m); } private MessageProcessor(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.history_sync = new HistorySync(db, stream_interactor); received_pipeline.connect(new DeduplicateMessageListener(this)); received_pipeline.connect(new FilterMessageListener()); received_pipeline.connect(new StoreMessageListener(this, stream_interactor)); received_pipeline.connect(new StoreContentItemListener(stream_interactor)); received_pipeline.connect(new MarkupListener(stream_interactor)); stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect(send_unsent_chat_messages); stream_interactor.stream_resumed.connect(send_unsent_chat_messages); } private void convert_sending_to_unsent_msgs(Account account) { db.message.update() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", Message.Marked.SENDING) .set(db.message.marked, Message.Marked.UNSENT) .perform(); } private void send_unsent_chat_messages(Account account) { var select = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", (int) Message.Marked.UNSENT) .with(db.message.type_, "=", (int) Message.Type.CHAT); send_unsent_messages(account, select); } public void send_unsent_muc_messages(Account account, Jid muc_jid) { var select = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.marked, "=", (int) Message.Marked.UNSENT) .with(db.message.counterpart_id, "=", db.get_jid_id(muc_jid)); send_unsent_messages(account, select); } private void send_unsent_messages(Account account, QueryBuilder select) { foreach (Row row in select) { try { Message message = new Message.from_row(db, row); Conversation? msg_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message.counterpart, account, Util.get_conversation_type_for_message(message)); if (msg_conv != null) { Message cached_msg = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(message.id, msg_conv); send_xmpp_message(cached_msg ?? message, msg_conv, true); } } catch (InvalidJidError e) { warning("Ignoring message with invalid Jid: %s", e.message); } } } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_message.connect( (stream, message) => { on_message_received.begin(account, message); }); stream_interactor.module_manager.get_module(account, Xmpp.MessageModule.IDENTITY).received_error.connect((stream, message_stanza, error_stanza) => { Message? message = null; Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversations(message_stanza.from, account); foreach (Conversation conversation in conversations) { message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_stanza.id, conversation); if (message != null) break; } if (message == null) return; // We don't care about delivery errors if our counterpart already ACKed the message. if (message.marked in Message.MARKED_RECEIVED) return; warning("Message delivery error from %s. Type: %s, Condition: %s, Text: %s", message_stanza.from.to_string(), error_stanza.type_ ?? "-", error_stanza.condition, error_stanza.text ?? "-"); if (error_stanza.condition == Xmpp.ErrorStanza.CONDITION_RECIPIENT_UNAVAILABLE && error_stanza.type_ == Xmpp.ErrorStanza.TYPE_CANCEL) return; message.marked = Message.Marked.ERROR; }); convert_sending_to_unsent_msgs(account); } private async void on_message_received(Account account, Xmpp.MessageStanza message_stanza) { // If it's a message from MAM, it's going to be processed by HistorySync which calls run_pipeline_announce later. if (history_sync.process(account, message_stanza)) return; run_pipeline_announce.begin(account, message_stanza); } public async void run_pipeline_announce(Account account, Xmpp.MessageStanza message_stanza) { Entities.Message message = yield parse_message_stanza(account, message_stanza); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(message); if (conversation == null) return; bool abort = yield received_pipeline.run(message, message_stanza, conversation); if (abort) return; if (message.direction == Entities.Message.DIRECTION_RECEIVED) { message_received(message, conversation); } else if (message.direction == Entities.Message.DIRECTION_SENT) { message_sent(message, conversation); } message_sent_or_received(message, conversation); } public async Entities.Message parse_message_stanza(Account account, Xmpp.MessageStanza message) { string? body = message.body; if (body != null) body = body.strip(); Entities.Message new_message = new Entities.Message(body); new_message.account = account; new_message.stanza_id = Xep.UniqueStableStanzaIDs.get_origin_id(message) ?? message.id; Jid? counterpart_override = null; if (message.from.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(message.from.bare_jid, account))) { new_message.direction = Entities.Message.DIRECTION_SENT; counterpart_override = message.from.bare_jid; } else if (account.bare_jid.equals_bare(message.from)) { new_message.direction = Entities.Message.DIRECTION_SENT; } else { new_message.direction = Entities.Message.DIRECTION_RECEIVED; } new_message.counterpart = counterpart_override ?? (new_message.direction == Entities.Message.DIRECTION_SENT ? message.to : message.from); new_message.ourpart = new_message.direction == Entities.Message.DIRECTION_SENT ? message.from : message.to; Xmpp.MessageArchiveManagement.MessageFlag? mam_message_flag = Xmpp.MessageArchiveManagement.MessageFlag.get_flag(message); EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); if (mam_message_flag != null && mam_message_flag.mam_id != null) { bool server_does_mam = entity_info.has_feature_cached(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI); if (server_does_mam) { new_message.server_id = mam_message_flag.mam_id; } } else if (message.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (yield entity_info.has_feature(account, new_message.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, new_message.counterpart.bare_jid); } } else if (message.type_ == Xmpp.MessageStanza.TYPE_CHAT) { bool server_supports_sid = (yield entity_info.has_feature(account, account.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (yield entity_info.has_feature(account, account.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (server_supports_sid) { new_message.server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(message, account.bare_jid); } } if (mam_message_flag != null) new_message.local_time = mam_message_flag.server_time; DateTime now = new DateTime.from_unix_utc(new DateTime.now_utc().to_unix()); // Remove milliseconds. They are not stored in the db and might lead to ordering issues when compared with times from the db. if (new_message.local_time == null || new_message.local_time.compare(now) > 0) new_message.local_time = now; Xep.DelayedDelivery.MessageFlag? delayed_message_flag = Xep.DelayedDelivery.MessageFlag.get_flag(message); if (delayed_message_flag != null) new_message.time = delayed_message_flag.datetime; if (new_message.time == null || new_message.time.compare(new_message.local_time) > 0) new_message.time = new_message.local_time; new_message.type_ = yield determine_message_type(account, message, new_message); return new_message; } private async Entities.Message.Type determine_message_type(Account account, Xmpp.MessageStanza message_stanza, Entities.Message message) { if (message_stanza.type_ == Xmpp.MessageStanza.TYPE_GROUPCHAT) { return Entities.Message.Type.GROUPCHAT; } if (message_stanza.type_ == Xmpp.MessageStanza.TYPE_CHAT) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(message.counterpart.bare_jid, account); if (conversation != null) { if (conversation.type_ == Conversation.Type.CHAT) { return Entities.Message.Type.CHAT; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { return Entities.Message.Type.GROUPCHAT_PM; } } else { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { Gee.Set? identities = yield stream.get_module(Xep.ServiceDiscovery.Module.IDENTITY).get_entity_identities(stream, message.counterpart.bare_jid); if (identities == null) { return Entities.Message.Type.CHAT; } foreach (Xep.ServiceDiscovery.Identity identity in identities) { if (identity.category == Xep.ServiceDiscovery.Identity.CATEGORY_CONFERENCE) { return Entities.Message.Type.GROUPCHAT_PM; } else { return Entities.Message.Type.CHAT; } } } } } return Entities.Message.Type.CHAT; } private bool is_duplicate(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Account account = conversation.account; // Deduplicate by server_id if (message.server_id != null) { QueryBuilder builder = db.message.select() .with(db.message.server_id, "=", message.server_id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.account_id, "=", account.id); // If the message is a duplicate if (builder.count() > 0) { history_sync.on_server_id_duplicate(account, stanza, message); return true; } } // Deduplicate messages by uuid bool is_uuid = message.stanza_id != null && Regex.match_simple("""[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}""", message.stanza_id); if (is_uuid) { QueryBuilder builder = db.message.select() .with(db.message.stanza_id, "=", message.stanza_id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.account_id, "=", account.id); if (message.direction == Message.DIRECTION_RECEIVED) { if (message.counterpart.resourcepart != null) { builder.with(db.message.counterpart_resource, "=", message.counterpart.resourcepart); } else { builder.with_null(db.message.counterpart_resource); } } else if (message.direction == Message.DIRECTION_SENT) { if (message.ourpart.resourcepart != null) { builder.with(db.message.our_resource, "=", message.ourpart.resourcepart); } else { builder.with_null(db.message.our_resource); } } bool duplicate = builder.single().row().is_present(); return duplicate; } // Deduplicate messages based on content and metadata QueryBuilder builder = db.message.select() .with(db.message.account_id, "=", account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(message.counterpart)) .with(db.message.body, "=", message.body) .with(db.message.time, "<", (long) message.time.add_minutes(1).to_unix()) .with(db.message.time, ">", (long) message.time.add_minutes(-1).to_unix()); if (message.stanza_id != null) { builder.with(db.message.stanza_id, "=", message.stanza_id); } else { builder.with_null(db.message.stanza_id); } if (message.counterpart.resourcepart != null) { builder.with(db.message.counterpart_resource, "=", message.counterpart.resourcepart); } else { builder.with_null(db.message.counterpart_resource); } return builder.count() > 0; } private class DeduplicateMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "FILTER_EMPTY", "MUC" }; public override string action_group { get { return "DEDUPLICATE"; } } public override string[] after_actions { get { return after_actions_const; } } private MessageProcessor outer; public DeduplicateMessageListener(MessageProcessor outer) { this.outer = outer; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { return outer.is_duplicate(message, stanza, conversation); } } private class FilterMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DECRYPT" }; public override string action_group { get { return "FILTER_EMPTY"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { return message.body == null && Xep.StatelessFileSharing.get_file_shares(stanza) == null; } } private class StoreMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY" }; public override string action_group { get { return "STORE"; } } public override string[] after_actions { get { return after_actions_const; } } private MessageProcessor outer; private StreamInteractor stream_interactor; public StoreMessageListener(MessageProcessor outer, StreamInteractor stream_interactor) { this.outer = outer; this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { stream_interactor.get_module(MessageStorage.IDENTITY).add_message(message, conversation); return false; } } private class MarkupListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "Markup"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public MarkupListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Gee.List markups = MessageMarkup.get_spans(stanza); message.persist_markups(markups, message.id); return false; } } private class StoreContentItemListener : MessageListener { public string[] after_actions_const = new string[]{ "DEDUPLICATE", "DECRYPT", "FILTER_EMPTY", "STORE", "CORRECTION", "MESSAGE_REINTERPRETING" }; public override string action_group { get { return "STORE_CONTENT_ITEM"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public StoreContentItemListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (message.body == null) return true; stream_interactor.get_module(ContentItemStore.IDENTITY).insert_message(message, conversation); return false; } } public Entities.Message create_out_message(string? text, Conversation conversation) { Entities.Message message = new Entities.Message(text); message.type_ = Util.get_message_type_for_conversation(conversation); message.stanza_id = random_uuid(); message.account = conversation.account; message.body = text; DateTime now = new DateTime.from_unix_utc(new DateTime.now_utc().to_unix()); // Remove milliseconds. They are not stored in the db and might lead to ordering issues when compared with times from the db. message.time = now; message.local_time = now; message.direction = Entities.Message.DIRECTION_SENT; message.counterpart = conversation.counterpart; if (conversation.type_ == Conversation.Type.GROUPCHAT) { message.ourpart = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account) ?? conversation.account.bare_jid; message.real_jid = conversation.account.bare_jid; } else { message.ourpart = conversation.account.full_jid; } message.marked = Entities.Message.Marked.UNSENT; message.encryption = conversation.encryption; stream_interactor.get_module(MessageStorage.IDENTITY).add_message(message, conversation); return message; } public void send_xmpp_message(Entities.Message message, Conversation conversation, bool delayed = false) { XmppStream stream = stream_interactor.get_stream(conversation.account); message.marked = Entities.Message.Marked.SENDING; if (stream == null) { message.marked = Entities.Message.Marked.UNSENT; return; } MessageStanza new_message = new MessageStanza(message.stanza_id); new_message.to = message.counterpart; new_message.body = message.body; if (conversation.type_ == Conversation.Type.GROUPCHAT) { new_message.type_ = MessageStanza.TYPE_GROUPCHAT; } else { new_message.type_ = MessageStanza.TYPE_CHAT; } if (message.quoted_item_id != 0) { ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, message.quoted_item_id); if (quoted_content_item != null) { Jid? quoted_sender = message.from; string? quoted_stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, quoted_content_item); if (quoted_sender != null && quoted_stanza_id != null) { Xep.Replies.set_reply_to(new_message, new Xep.Replies.ReplyTo(quoted_sender, quoted_stanza_id)); } foreach (var fallback in message.get_fallbacks()) { Xep.FallbackIndication.set_fallback(new_message, fallback); } } } MessageMarkup.add_spans(new_message, message.get_markups()); build_message_stanza(message, new_message, conversation); pre_message_send(message, new_message, conversation); if (message.marked == Entities.Message.Marked.UNSENT || message.marked == Entities.Message.Marked.WONTSEND) return; if (delayed) { DelayedDelivery.Module.set_message_delay(new_message, message.time); } // Set an origin ID if a MUC doen't guarantee to keep IDs if (conversation.type_ == Conversation.Type.GROUPCHAT) { Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY); if (flag == null) { message.marked = Entities.Message.Marked.UNSENT; return; } if(!flag.has_room_feature(conversation.counterpart, Xep.Muc.Feature.STABLE_ID)) { UniqueStableStanzaIDs.set_origin_id(new_message, message.stanza_id); } } if (conversation.get_send_typing_setting(stream_interactor) == Conversation.Setting.ON) { ChatStateNotifications.add_state_to_message(new_message, ChatStateNotifications.STATE_ACTIVE); } stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, new_message, (_, res) => { try { stream.get_module(MessageModule.IDENTITY).send_message.end(res); if (message.marked == Message.Marked.SENDING) { message.marked = Message.Marked.SENT; } // The server might not have given us the resource we asked for. In that case, store the actual resource the message was sent with. Relevant for deduplication. Jid? current_own_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid; if (!conversation.type_.is_muc_semantic() && current_own_jid != null && !current_own_jid.equals(message.ourpart)) { message.ourpart = current_own_jid; } } catch (IOError e) { message.marked = Entities.Message.Marked.UNSENT; if (stream != stream_interactor.get_stream(conversation.account)) { Timeout.add_seconds(3, () => { send_unsent_chat_messages(conversation.account); return false; }); } } }); } } public abstract class MessageListener : Xmpp.OrderedListener { public abstract async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation); } public class MessageListenerHolder : Xmpp.ListenerHolder { public async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { foreach (OrderedListener ol in listeners) { MessageListener l = ol as MessageListener; bool stop = yield l.run(message, stanza, conversation); if (stop) return true; } return false; } } } dino-0.5.0/libdino/src/service/message_storage.vala0000664000000000000000000002175214776241610021050 0ustar rootrootusing Xmpp; using Gee; using Qlite; using Dino.Entities; namespace Dino { public class MessageStorage : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("message_cache"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private WeakMap messages_by_db_id = new WeakMap(); private HashMap> messages_by_stanza_id = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap> messages_by_server_id = new HashMap>(Conversation.hash_func, Conversation.equals_func); // This is to keep the last 300 messages such that we don't have to recreate the newest ones all the time private LinkedList message_refs = new LinkedList(); public static void start(StreamInteractor stream_interactor, Database db) { MessageStorage m = new MessageStorage(stream_interactor, db); stream_interactor.add_module(m); } private MessageStorage(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public void add_message(Message message, Conversation conversation) { message.persist(db); cache_message(message, conversation); } public Gee.List get_messages(Conversation conversation, int count = 50) { var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id) .limit(count); Gee.List ret = new LinkedList(Message.equals_func); foreach (Row row in query) { Message? message = messages_by_db_id[row[db.message.id]]; if (message == null) { message = create_message_from_row(row, conversation); } ret.insert(0, message); } return ret; } public Message? get_last_message(Conversation conversation) { Gee.List messages = get_messages(conversation, 1); if (messages.size > 0) { return messages[0]; } return null; } public Gee.List get_messages_before_message(Conversation? conversation, DateTime before, int id, int count = 20) { Gee.List db_messages = db.get_messages(conversation.counterpart, conversation.account, Util.get_message_type_for_conversation(conversation), count, before, null, id); Gee.List ret = new ArrayList(); foreach (Message message in db_messages) { ret.add(new MessageItem(message, conversation, -1)); } return ret; } public Gee.List get_messages_after_message(Conversation? conversation, DateTime after, int id, int count = 20) { Gee.List db_messages = db.get_messages(conversation.counterpart, conversation.account, Util.get_message_type_for_conversation(conversation), count, null, after, id); Gee.List ret = new ArrayList(); foreach (Message message in db_messages) { ret.add(new MessageItem(message, conversation, -1)); } return ret; } public Message? get_message_by_id(int id, Conversation conversation) { Message? message = messages_by_db_id[id]; if (message != null) { return message; } RowOption row_option = db.message.select().with(db.message.id, "=", id) .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id) .row(); return create_message_from_row_opt(row_option, conversation); } public Message? get_message_by_referencing_id(string id, Conversation conversation) { if (conversation.type_ == Conversation.Type.CHAT) { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(id, conversation); } else { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(id, conversation); } } public Message? get_message_by_stanza_id(string stanza_id, Conversation conversation) { if (messages_by_stanza_id.has_key(conversation)) { Message? message = messages_by_stanza_id[conversation][stanza_id]; if (message != null) { return message; } } var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .with(db.message.stanza_id, "=", stanza_id) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id); if (conversation.counterpart.resourcepart != null) { query.with(db.message.counterpart_resource, "=", conversation.counterpart.resourcepart); } RowOption row_option = query.single().row(); return create_message_from_row_opt(row_option, conversation); } public Message? get_message_by_server_id(string server_id, Conversation conversation) { if (messages_by_server_id.has_key(conversation)) { Message? message = messages_by_server_id[conversation][server_id]; if (message != null) { return message; } } var query = db.message.select() .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.type_, "=", (int) Util.get_message_type_for_conversation(conversation)) .with(db.message.server_id, "=", server_id) .order_by(db.message.time, "DESC") .outer_join_with(db.message_correction, db.message_correction.message_id, db.message.id) .outer_join_with(db.reply, db.reply.message_id, db.message.id); if (conversation.counterpart.resourcepart == null) { query.with_null(db.message.counterpart_resource); } else { query.with(db.message.counterpart_resource, "=", conversation.counterpart.resourcepart); } RowOption row_option = query.single().row(); return create_message_from_row_opt(row_option, conversation); } private Message? create_message_from_row_opt(RowOption row_option, Conversation conversation) { if (!row_option.is_present()) return null; return create_message_from_row(row_option.inner, conversation); } private Message? create_message_from_row(Row row, Conversation conversation) { try { Message message = new Message.from_row(db, row); cache_message(message, conversation); return message; } catch (InvalidJidError e) { warning("Got message with invalid Jid: %s", e.message); } return null; } private void cache_message(Message message, Conversation conversation) { messages_by_db_id[message.id] = message; if (message.stanza_id != null) { if (!messages_by_stanza_id.has_key(conversation)) { messages_by_stanza_id[conversation] = new WeakMap(); } messages_by_stanza_id[conversation][message.stanza_id] = message; } if (message.server_id != null) { if (!messages_by_server_id.has_key(conversation)) { messages_by_server_id[conversation] = new WeakMap(); } messages_by_server_id[conversation][message.server_id] = message; } message_refs.insert(0, message); if (message_refs.size > 300) { message_refs.remove_at(message_refs.size - 1); } } public static string? get_reference_id(Message message) { if (message.edit_to != null) return message.edit_to; if (message.type_ == Message.Type.CHAT) { return message.stanza_id; } else { return message.server_id; } } } } dino-0.5.0/libdino/src/service/module_manager.vala0000664000000000000000000001114514776241610020652 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public class ModuleManager { private HashMap> module_map = new HashMap>(Account.hash_func, Account.equals_func); public signal void initialize_account_modules(Account account, ArrayList modules); public T? get_module(Account account, Xmpp.ModuleIdentity identity) { if (identity == null) return null; lock (module_map) { if (!module_map.has_key(account)) { initialize(account); } var res = module_map[account].filter((module) => identity.matches(module)); if (res != null && res.next()) { return identity.cast(res.get()); } } return null; } public ArrayList get_modules(Account account) { ArrayList modules = new ArrayList(); lock (module_map) { if (!module_map.has_key(account)) initialize(account); foreach (XmppStreamModule module in module_map[account]) modules.add(module); } foreach (XmppStreamModule module in module_map[account]) { if (module.get_id() == Bind.Module.IDENTITY.id) { ((Bind.Module) module).requested_resource = account.resourcepart; } else if (module.get_id() == Sasl.Module.IDENTITY.id) { ((Sasl.Module) module).password = account.password; } } return modules; } public void initialize(Account account) { lock(module_map) { module_map[account] = new ArrayList(); module_map[account].add(new Iq.Module()); module_map[account].add(new Sasl.Module(account.bare_jid.to_string(), account.password)); module_map[account].add(new Xep.StreamManagement.Module()); module_map[account].add(new Bind.Module(account.resourcepart)); module_map[account].add(new Session.Module()); module_map[account].add(new Roster.Module()); module_map[account].add(new Xep.ServiceDiscovery.Module.with_identity("client", "pc", "Dino")); module_map[account].add(new Xep.PrivateXmlStorage.Module()); module_map[account].add(new Xep.Bookmarks.Module()); module_map[account].add(new Xep.Bookmarks2.Module()); module_map[account].add(new Presence.Module()); module_map[account].add(new Xmpp.MessageModule()); module_map[account].add(new Xmpp.MessageArchiveManagement.Module()); module_map[account].add(new Xep.MessageCarbons.Module()); module_map[account].add(new Xep.Muc.Module()); module_map[account].add(new Xep.Pubsub.Module()); module_map[account].add(new Xep.MessageDeliveryReceipts.Module()); module_map[account].add(new Xep.BlockingCommand.Module()); module_map[account].add(new Xep.ChatStateNotifications.Module()); module_map[account].add(new Xep.ChatMarkers.Module()); module_map[account].add(new Xep.Ping.Module()); module_map[account].add(new Xep.DelayedDelivery.Module()); module_map[account].add(new StreamError.Module()); module_map[account].add(new Xep.InBandRegistration.Module()); module_map[account].add(new Xep.HttpFileUpload.Module()); module_map[account].add(new Xep.Reactions.Module()); module_map[account].add(new Xep.Socks5Bytestreams.Module()); module_map[account].add(new Xep.InBandBytestreams.Module()); module_map[account].add(new Xep.Jingle.Module()); module_map[account].add(new Xep.JingleSocks5Bytestreams.Module()); module_map[account].add(new Xep.JingleInBandBytestreams.Module()); module_map[account].add(new Xep.JingleFileTransfer.Module()); module_map[account].add(new Xep.Jet.Module()); module_map[account].add(new Xep.LastMessageCorrection.Module()); module_map[account].add(new Xep.DirectMucInvitations.Module()); module_map[account].add(new Xep.JingleMessageInitiation.Module()); module_map[account].add(new Xep.OccupantIds.Module()); module_map[account].add(new Xep.JingleRawUdp.Module()); module_map[account].add(new Xep.Muji.Module()); module_map[account].add(new Xep.CallInvites.Module()); module_map[account].add(new Xep.Coin.Module()); initialize_account_modules(account, module_map[account]); } } } } dino-0.5.0/libdino/src/service/muc_manager.vala0000664000000000000000000010271214776241610020152 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; namespace Dino { public class MucManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("muc_manager"); public string id { get { return IDENTITY.id; } } public signal void left(Account account, Jid jid); public signal void subject_set(Account account, Jid jid, string? subject); public signal void room_info_updated(Account account, Jid muc_jid); public signal void private_room_occupant_updated(Account account, Jid room, Jid occupant); public signal void invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason); public signal void voice_request_received(Account account, Jid room_jid, Jid from_jid, string nick); public signal void received_occupant_role(Account account, Jid jid, Xep.Muc.Role? role); public signal void bookmarks_updated(Account account, Set conferences); public signal void conference_added(Account account, Conference conference); public signal void conference_removed(Account account, Jid jid); private StreamInteractor stream_interactor; private HashMap> mucs_joined = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> mucs_joining = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> mucs_sync_cancellables = new HashMap>(Account.hash_func, Account.equals_func); private HashMap enter_errors = new HashMap(Jid.hash_func, Jid.equals_func); private ReceivedMessageListener received_message_listener; private HashMap bookmarks_provider = new HashMap(Account.hash_func, Account.equals_func); private HashMap> invites = new HashMap>(Account.hash_func, Account.equals_func); public HashMap default_muc_server = new HashMap(Account.hash_func, Account.equals_func); private HashMap> own_occupant_ids = new HashMap>(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor) { MucManager m = new MucManager(stream_interactor); stream_interactor.add_module(m); } private MucManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.received_message_listener = new ReceivedMessageListener(stream_interactor); stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect((conversation) => { if (conversation.type_ == Conversation.Type.GROUPCHAT) { part(conversation.account, conversation.counterpart); } }); stream_interactor.stream_resumed.connect((account, stream) => self_ping(account)); Timeout.add_seconds(60 * 3, () => { foreach (Account account in stream_interactor.get_accounts()) { self_ping(account); } return true; }); stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(on_build_message_stanza); } // already_autojoin: Without this flag we'd be retrieving bookmarks (to check for autojoin) from the sender on every join public async Muc.JoinResult? join(Account account, Jid jid, string? nick, string? password, bool already_autojoin = false, Cancellable? cancellable = null) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; string nick_ = (nick ?? account.localpart) ?? account.domainpart; DateTime? history_since = null; Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account); if (conversation != null) { Entities.Message? last_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_last_message(conversation); if (last_message != null) history_since = last_message.time; } bool receive_history = true; EntityInfo entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); bool can_do_mam = yield entity_info.has_feature(account, jid, Xmpp.MessageArchiveManagement.NS_URI); if (can_do_mam) { receive_history = false; history_since = null; } if (!mucs_joining.has_key(account)) { mucs_joining[account] = new HashSet(Jid.hash_bare_func, Jid.equals_bare_func); } mucs_joining[account].add(jid); Muc.JoinResult? res = yield stream.get_module(Xep.Muc.Module.IDENTITY).enter(stream, jid.bare_jid, nick_, password, history_since, receive_history, null); mucs_joining[account].remove(jid); if (res.nick != null) { // Join completed enter_errors.unset(jid); if (!already_autojoin) set_autojoin(account, stream, jid, nick, password); stream_interactor.get_module(MessageProcessor.IDENTITY).send_unsent_muc_messages(account, jid); Conversation joined_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.GROUPCHAT); joined_conversation.nickname = nick; stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(joined_conversation); if (can_do_mam) { var history_sync = stream_interactor.get_module(MessageProcessor.IDENTITY).history_sync; if (conversation == null) { // We never joined the conversation before, just fetch the latest MAM page yield history_sync.fetch_latest_page(account, jid.bare_jid, null, new DateTime.from_unix_utc(0), cancellable); } else { // Fetch everything up to the last time the user actively joined if (!mucs_sync_cancellables.has_key(account)) { mucs_sync_cancellables[account] = new HashMap(); } if (!mucs_sync_cancellables[account].has_key(jid.bare_jid)) { mucs_sync_cancellables[account][jid.bare_jid] = new Cancellable(); history_sync.fetch_everything.begin(account, jid.bare_jid, mucs_sync_cancellables[account][jid.bare_jid], conversation.active_last_changed, (_, res) => { history_sync.fetch_everything.end(res); mucs_sync_cancellables[account].unset(jid.bare_jid); }); } } } } else if (res.muc_error != null) { // Join failed enter_errors[jid] = res.muc_error; } if (!mucs_joined.has_key(account)) { mucs_joined[account] = new HashSet(Jid.hash_bare_func, Jid.equals_bare_func); } mucs_joined[account].add(jid.with_resource(res.nick ?? nick_)); return res; } public void part(Account account, Jid jid) { if (mucs_joined.has_key(account) && mucs_joined[account].contains(jid)) { mucs_joined[account].remove(jid); } XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { unset_autojoin(account, stream, jid); stream.get_module(Xep.Muc.Module.IDENTITY).exit(stream, jid.bare_jid); } Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT); if (conversation != null) stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); cancel_sync(account, jid); } private void cancel_sync(Account account, Jid jid) { if (mucs_sync_cancellables.has_key(account) && mucs_sync_cancellables[account].has_key(jid.bare_jid) && !mucs_sync_cancellables[account][jid.bare_jid].is_cancelled()) { mucs_sync_cancellables[account][jid.bare_jid].cancel(); } } public async DataForms.DataForm? get_config_form(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; return yield stream.get_module(Xep.Muc.Module.IDENTITY).get_config_form(stream, jid); } public async void set_config_form(Account account, Jid jid, DataForms.DataForm data_form) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; yield stream.get_module(Xep.Muc.Module.IDENTITY).set_config_form(stream, jid, data_form); } public void change_subject(Account account, Jid jid, string subject) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_subject(stream, jid.bare_jid, subject); } public async void change_nick(Conversation conversation, string new_nick) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return; // Check if this would be a valid nick try { conversation.counterpart.with_resource(new_nick); } catch (InvalidJidError error) { return; } stream.get_module(Xep.Muc.Module.IDENTITY).change_nick(stream, conversation.counterpart, new_nick); conversation.nickname = new_nick; if (mucs_joined.has_key(conversation.account)) { mucs_joined[conversation.account].remove(conversation.counterpart); mucs_joined[conversation.account].add(conversation.counterpart.with_resource(new_nick)); } // Update nick in bookmark Set? conferences = yield bookmarks_provider[conversation.account].get_conferences(stream); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(conversation.counterpart)) { Conference new_conference = new Conference() { jid=conversation.counterpart, nick=new_nick, name=conference.name, password=conference.password, autojoin=conference.autojoin }; bookmarks_provider[conversation.account].replace_conference.begin(stream, conversation.counterpart, new_conference); break; } } } public void invite(Account account, Jid muc, Jid invitee) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).invite(stream, muc.bare_jid, invitee.bare_jid); } public void kick(Account account, Jid jid, string nick) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).kick(stream, jid.bare_jid, nick); } public void change_affiliation(Account account, Jid jid, string nick, string role) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_affiliation.begin(stream, jid.bare_jid, null, nick, role); } public void change_role(Account account, Jid jid, string nick, string role) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).change_role(stream, jid.bare_jid, nick, role); } public void request_voice(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xep.Muc.Module.IDENTITY).request_voice(stream, jid.bare_jid); } public bool kick_possible(Account account, Jid occupant) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) return stream.get_module(Xep.Muc.Module.IDENTITY).kick_possible(stream, occupant); return false; } //the term `private room` is a short hand for members-only+non-anonymous rooms public bool is_private_room(Account account, Jid jid) { var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); return entity_info.has_feature_offline(account, jid, "muc_membersonly") && entity_info.has_feature_offline(account, jid, "muc_nonanonymous"); } public bool is_moderated_room(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) { return false; } Xep.Muc.Flag? flag = stream.get_flag(Xep.Muc.Flag.IDENTITY); if (flag == null) { return false; } return flag.has_room_feature(jid, Xep.Muc.Feature.MODERATED); } public bool is_public_room(Account account, Jid jid) { return is_groupchat(jid, account) && !is_private_room(account, jid); } public Gee.List? get_occupants(Jid jid, Account account) { if (is_groupchat(jid, account)) { Gee.List ret = new ArrayList(Jid.equals_func); Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(jid, account); if (full_jids != null) { ret.add_all(full_jids); // Remove eventual presence from bare jid ret.remove(jid); } return ret; } return null; } public Gee.List? get_other_occupants(Jid jid, Account account) { Gee.List? occupants = get_occupants(jid, account); Jid? own_jid = get_own_jid(jid, account); if (occupants != null && own_jid != null) { occupants.remove(own_jid); } return occupants; } public bool is_groupchat(Jid jid, Account account) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT); return !jid.is_full() && conversation != null; } public bool might_be_groupchat(Jid jid, Account account) { if (mucs_joining.has_key(account) && mucs_joining[account].contains(jid)) return true; return is_groupchat(jid, account); } public bool is_groupchat_occupant(Jid jid, Account account) { return is_groupchat(jid.bare_jid, account) && jid.resourcepart != null; } public async Set? get_bookmarks(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; return yield bookmarks_provider[account].get_conferences(stream); } public void add_bookmark(Account account, Conference conference) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { bookmarks_provider[account].add_conference.begin(stream, conference); } } public void remove_bookmark(Account account, Conference conference) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { bookmarks_provider[account].remove_conference.begin(stream, conference); } } public string? get_room_name(Account account, Jid jid) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_room_name(jid); } return null; } public string? get_groupchat_subject(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_muc_subject(jid.bare_jid); } return null; } public Jid? get_real_jid(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_real_jid(jid); } return null; } public Jid? get_occupant_jid(Account account, Jid room, Jid occupant_real_jid) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_occupant_jid(occupant_real_jid, room); } return null; } public Xep.Muc.Role? get_role(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_occupant_role(jid); } return null; } public Xep.Muc.Affiliation? get_affiliation(Jid muc_jid, Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_affiliation(muc_jid, jid); } return null; } public Gee.List? get_offline_members(Jid jid, Account account) { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { return flag.get_offline_members(jid); } return null; } public Gee.List? get_other_offline_members(Jid jid, Account account) { Gee.List? occupants = get_offline_members(jid, account); if (occupants != null) { occupants.remove(account.bare_jid); } return occupants; } public Jid? get_own_jid(Jid muc_jid, Account account) { try { Xep.Muc.Flag? flag = get_muc_flag(account); if (flag != null) { string? nick = flag.get_muc_nick(muc_jid); if (nick != null) return muc_jid.with_resource(nick); } } catch (InvalidJidError e) { warning("Joined MUC with invalid Jid: %s", e.message); } return null; } public bool is_own_muc_jid(Jid full_jid, Account account) { if (!is_groupchat(full_jid.bare_jid, account)) return false; Jid? own_jid = get_own_jid(full_jid, account); return own_jid != null && own_jid.equals(full_jid); } private Xep.Muc.Flag? get_muc_flag(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { return stream.get_flag(Xep.Muc.Flag.IDENTITY); } return null; } public bool is_joined(Jid jid, Account account) { return get_own_jid(jid, account) != null; } public string? get_own_occupant_id(Account account, Jid muc_jid) { if (own_occupant_ids.has_key(account) && own_occupant_ids[account].has_key(muc_jid)) { return own_occupant_ids[account][muc_jid]; } return null; } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).self_removed_from_room.connect( (stream, jid, code) => { cancel_sync(account, jid); left(account, jid); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).subject_set.connect( (stream, subject, jid) => { subject_set(account, jid, subject); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).invite_received.connect( (stream, room_jid, from_jid, password, reason) => { on_invite_received(account, room_jid, from_jid, password, reason); }); stream_interactor.module_manager.get_module(account, Xep.DirectMucInvitations.Module.IDENTITY).invite_received.connect( (stream, room_jid, from_jid, password, reason) => { on_invite_received(account, room_jid, from_jid, password, reason); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).voice_request_received.connect( (stream, room_jid, from_jid, nick) => { voice_request_received(account, room_jid, from_jid, nick); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).received_occupant_role.connect( (stream, from_jid, role) => { received_occupant_role(account, from_jid, role); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).room_info_updated.connect( (stream, muc_jid) => { room_info_updated(account, muc_jid); }); stream_interactor.module_manager.get_module(account, Xep.Muc.Module.IDENTITY).received_occupant_jid.connect( (stream, room, occupant) => { if (is_private_room(account, room.bare_jid)) { private_room_occupant_updated(account, room, occupant); } }); stream_interactor.module_manager.get_module(account, Xep.OccupantIds.Module.IDENTITY).received_own_occupant_id.connect( (stream, jid, occupant_id) => { if (!own_occupant_ids.has_key(account)) { own_occupant_ids[account] = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); } own_occupant_ids[account][jid] = occupant_id; }); } private async void search_default_muc_server(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; ServiceDiscovery.ItemsResult? items_result = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).request_items(stream, stream.remote_name); if (items_result == null) return; for (int i = 0; i < 2; i++) { foreach (Xep.ServiceDiscovery.Item item in items_result.items) { // First try the promising items and only afterwards all the others bool promising_upload_item = item.jid.to_string().has_prefix("conference") || item.jid.to_string().has_prefix("muc") || item.jid.to_string().has_prefix("chat"); if ((i == 0 && !promising_upload_item) || (i == 1) && promising_upload_item) continue; Gee.Set identities = yield stream_interactor.get_module(EntityInfo.IDENTITY).get_identities(account, item.jid); if (identities == null) return; foreach (Xep.ServiceDiscovery.Identity identity in identities) { if (identity.category == Xep.ServiceDiscovery.Identity.CATEGORY_CONFERENCE) { default_muc_server[account] = item.jid; debug("[%s] Default MUC: %s", account.bare_jid.to_string(), item.jid.to_string()); return; } } } } } private async void on_stream_negotiated(Account account, XmppStream stream) { if (mucs_sync_cancellables.has_key(account)) { foreach (Cancellable cancellable in mucs_sync_cancellables[account].values) { if (!cancellable.is_cancelled()) { cancellable.cancel(); } } } yield initialize_bookmarks_provider(account); Set? conferences = yield bookmarks_provider[account].get_conferences(stream); if (conferences == null) { join_all_active(account); } else { sync_autojoin_active(account, conferences); } if (!default_muc_server.has_key(account)) { search_default_muc_server.begin(account); } } private async void initialize_bookmarks_provider(Account account) { if (bookmarks_provider.has_key(account)) return; // Use PEP native bookmarks (urn:xmpp:bookmarks:1) if conversion is available, legacy bookmarks (storage:bookmarks) otherwise. bool has_feature = yield stream_interactor.get_module(EntityInfo.IDENTITY).has_feature(account, account.bare_jid, Xep.Bookmarks2.NS_URI_COMPAT); if (has_feature) { debug("[%s] Using PEP native bookmarks (urn:xmpp:bookmarks:1)", account.bare_jid.to_string()); bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks2.Module.IDENTITY); } else { debug("[%s] Using legacy bookmarks (storage:bookmarks)", account.bare_jid.to_string()); bookmarks_provider[account] = stream_interactor.module_manager.get_module(account, Xep.Bookmarks.Module.IDENTITY); } bookmarks_provider[account].received_conferences.connect( (stream, conferences) => { sync_autojoin_active(account, conferences); bookmarks_updated(account, conferences); }); bookmarks_provider[account].conference_added.connect( (stream, conference) => { on_conference_added(account, conference); }); bookmarks_provider[account].conference_removed.connect( (stream, jid) => { on_conference_removed(account, jid); }); } private void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) { if (!invites.has_key(account)) { invites[account] = new LinkedList(Jid.equals_func); } if (invites[account].contains(room_jid)) return; invites[account].add(room_jid); invite_received(account, room_jid, from_jid, password, reason); Timeout.add_seconds(5, () => { // We don't want to show the same invite (direct+mediated) twice, but a distinct invite is fine invites[account].remove(room_jid); return false; }); } private void join_all_active(Account account) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); foreach (Conversation conversation in conversations) { if (conversation.type_ == Conversation.Type.GROUPCHAT && conversation.nickname != null) { join.begin(account, conversation.counterpart, conversation.nickname, null); } } } private void sync_autojoin_active(Account account, Set conferences) { Gee.List active_conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(account); // Join auto-join MUCs foreach (Conference conference in conferences) { if (!conference.autojoin) continue; bool is_active = false; foreach (Conversation conversation in active_conversations) { if (conference.jid.equals(conversation.counterpart)) { is_active = true; } } if (!is_active || !is_joined(conference.jid, account)) { join.begin(account, conference.jid, conference.nick, conference.password, true); } } // Part MUCs that aren't auto-join (which closes those conversations) foreach (Conversation conversation in active_conversations) { if (conversation.type_ != Conversation.Type.GROUPCHAT) continue; bool should_be_active = false; foreach (Conference conference in conferences) { if (conference.jid.equals(conversation.counterpart) && conference.autojoin) { should_be_active = true; } } if (!should_be_active) { part(conversation.account, conversation.counterpart); } } } private void set_autojoin(Account account, XmppStream stream, Jid jid, string? nick, string? password) { bookmarks_provider[account].get_conferences.begin(stream, (_, res) => { Set? conferences = bookmarks_provider[account].get_conferences.end(res); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(jid)) { if (!conference.autojoin) { Conference new_conference = new Conference() { jid=jid, nick=nick ?? conference.nick, name=conference.name, password=password ?? conference.password, autojoin=true }; bookmarks_provider[account].replace_conference.begin(stream, jid, new_conference); } return; } } Conference changed = new Xep.Bookmarks.Bookmarks1Conference(jid) { nick=nick, password=password, autojoin=true }; bookmarks_provider[account].add_conference.begin(stream, changed); }); } private void unset_autojoin(Account account, XmppStream stream, Jid jid) { bookmarks_provider[account].get_conferences.begin(stream, (_, res) => { Set? conferences = bookmarks_provider[account].get_conferences.end(res); if (conferences == null) return; foreach (Conference conference in conferences) { if (conference.jid.equals(jid)) { if (conference.autojoin) { Conference new_conference = new Conference() { jid=jid, nick=conference.nick, name=conference.name, password=conference.password, autojoin=false }; bookmarks_provider[account].replace_conference.begin(stream, jid, new_conference); return; } } } }); } private void on_conference_added(Account account, Xmpp.Conference conference) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conference.jid, account, Conversation.Type.GROUPCHAT); if (conversation != null) { if (!conversation.active && conference.autojoin) { join.begin(account, conference.jid, conference.nick, conference.password); } else if (conversation.active && !conference.autojoin) { part(account, conference.jid); } } else if (conference.autojoin) { join.begin(account, conference.jid, conference.nick, conference.password); } conference_added(account, conference); } private void on_conference_removed(Account account, Jid jid) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid, account, Conversation.Type.GROUPCHAT); if (conversation != null && conversation.active) { part(account, jid); } conference_removed(account, jid); } private void on_build_message_stanza(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { Xmpp.Xep.Muc.add_muc_pm_message_stanza_x_node(message_stanza); } } private void self_ping(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; if (!mucs_joined.has_key(account)) return; foreach (Jid jid in mucs_joined[account]) { bool joined = false; Xmpp.Xep.MucSelfPing.is_joined.begin(stream, jid, (_, res) => { joined = Xmpp.Xep.MucSelfPing.is_joined.end(res); }); Timeout.add_seconds(10, () => { if (joined || !mucs_joined.has_key(account) || stream_interactor.get_stream(account) != stream) return false; join.begin(account, jid.bare_jid, jid.resourcepart, null, true); return false; }); } } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "MUC"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; public ReceivedMessageListener(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (conversation.type_ != Conversation.Type.GROUPCHAT) return false; XmppStream stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; if (Xep.DelayedDelivery.MessageFlag.get_flag(stanza) == null) { Jid? real_jid = stream.get_flag(Xep.Muc.Flag.IDENTITY).get_real_jid(message.counterpart); if (real_jid != null && !real_jid.equals(message.counterpart)) { message.real_jid = real_jid.bare_jid; } } Jid? own_muc_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(message.counterpart.bare_jid, conversation.account); if (stanza.id != null && own_muc_jid != null && message.from.equals(own_muc_jid)) { Entities.Message? m = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(stanza.id, conversation); if (m != null) { // For own messages from this device (msg is a duplicate) m.marked = Message.Marked.RECEIVED; string? server_id = Xep.UniqueStableStanzaIDs.get_stanza_id(stanza, m.counterpart.bare_jid); if (server_id != null) { m.server_id = server_id; } } // For own messages from other devices (msg is not a duplicate msg) message.marked = Message.Marked.RECEIVED; } return false; } } } } dino-0.5.0/libdino/src/service/notification_events.vala0000664000000000000000000002315214776241610021746 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public class NotificationEvents : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("notification_events"); public string id { get { return IDENTITY.id; } } public signal void notify_content_item(ContentItem content_item, Conversation conversation); private StreamInteractor stream_interactor; private Future notifier; private Promise notifier_promise; private bool notifier_outstanding = true; public static void start(StreamInteractor stream_interactor) { NotificationEvents m = new NotificationEvents(stream_interactor); stream_interactor.add_module(m); } public NotificationEvents(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect((item, conversation) => on_content_item_received.begin(item, conversation)); stream_interactor.get_module(PresenceManager.IDENTITY).received_subscription_request.connect((jid, account) => on_received_subscription_request.begin(jid, account)); stream_interactor.get_module(MucManager.IDENTITY).invite_received.connect((account, room_jid, from_jid, password, reason) => on_invite_received.begin(account, room_jid, from_jid, password, reason)); stream_interactor.get_module(MucManager.IDENTITY).voice_request_received.connect((account, room_jid, from_jid, nick) => on_voice_request_received.begin(account, room_jid, from_jid, nick)); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state, conversation, video, multiparty) => on_call_incoming.begin(call, state, conversation, video, multiparty)); stream_interactor.connection_manager.connection_error.connect((account, error) => on_connection_error.begin(account, error)); stream_interactor.get_module(ChatInteraction.IDENTITY).focused_in.connect((conversation) => on_focused_in.begin(conversation)); notifier_promise = new Promise(); notifier = notifier_promise.future; } public async void register_notification_provider(NotificationProvider notification_provider) { if (notifier_outstanding || (yield notifier.wait_async()).get_priority() < notification_provider.get_priority()) { notifier_outstanding = false; notifier_promise.set_value(notification_provider); } } private async void on_content_item_received(ContentItem item, Conversation conversation) { ContentItem last_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); if (item.id != last_item.id) return; if (item.id == conversation.read_up_to_item) return; if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus()) return; Conversation.NotifySetting notify = conversation.get_notification_setting(stream_interactor); if (notify == Conversation.NotifySetting.OFF) return; string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); string? participant_display_name = null; if (conversation.type_ == Conversation.Type.GROUPCHAT) { participant_display_name = get_participant_display_name(stream_interactor, conversation, item.jid); } switch (item.type_) { case MessageItem.TYPE: Message message = ((MessageItem) item).message; if (message.direction == Message.DIRECTION_SENT) return; if (notify == Conversation.NotifySetting.HIGHLIGHT) { Jid? nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (nick == null) return; bool highlight = Regex.match_simple("\\b" + Regex.escape_string(nick.resourcepart) + "\\b", message.body, RegexCompileFlags.CASELESS); if (!highlight) return; } notify_content_item(item, conversation); if (notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_message(message, conversation, conversation_display_name, participant_display_name); } break; case FileItem.TYPE: FileTransfer file_transfer = ((FileItem) item).file_transfer; bool is_image = file_transfer.mime_type != null && file_transfer.mime_type.has_prefix("image"); // Don't notify on file transfers in a groupchat set to "mention only" if (notify == Conversation.NotifySetting.HIGHLIGHT) return; if (file_transfer.direction == FileTransfer.DIRECTION_SENT) return; notify_content_item(item, conversation); if (notify != Conversation.NotifySetting.OFF) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_file(file_transfer, conversation, is_image, conversation_display_name, participant_display_name); } break; case CallItem.TYPE: // handled in `on_call_incoming` break; } } private async void on_voice_request_received(Account account, Jid room_jid, Jid from_jid, string nick) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(room_jid, account, Conversation.Type.GROUPCHAT); if (conversation == null) return; NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_voice_request(conversation, from_jid); } private async void on_received_subscription_request(Jid jid, Account account) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); if (stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus(conversation)) return; NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_subscription_request(conversation); } private async void on_call_incoming(Call call, CallState call_state, Conversation conversation, bool video, bool multiparty) { if (!stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) return; string conversation_display_name = get_conversation_display_name(stream_interactor, conversation, null); NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_call(call, conversation, video, multiparty, conversation_display_name); call.notify["state"].connect(() => { if (call.state != Call.State.RINGING) { notifier.retract_call_notification.begin(call, conversation); } }); } private async void on_invite_received(Account account, Jid room_jid, Jid from_jid, string? password, string? reason) { string inviter_display_name; if (room_jid.equals_bare(from_jid)) { Conversation conversation = new Conversation(room_jid, account, Conversation.Type.GROUPCHAT); inviter_display_name = get_participant_display_name(stream_interactor, conversation, from_jid); } else { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); inviter_display_name = get_participant_display_name(stream_interactor, direct_conversation, from_jid); } NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_muc_invite(account, room_jid, from_jid, inviter_display_name); } private async void on_connection_error(Account account, ConnectionManager.ConnectionError error) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.notify_connection_error(account, error); } private async void on_focused_in(Conversation conversation) { NotificationProvider notifier = yield notifier.wait_async(); yield notifier.retract_content_item_notifications(); yield notifier.retract_conversation_notifications(conversation); } } public interface NotificationProvider : Object { public abstract double get_priority(); public abstract async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name); public abstract async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name); public abstract async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name); public abstract async void retract_call_notification(Call call, Conversation conversation); public abstract async void notify_subscription_request(Conversation conversation); public abstract async void notify_connection_error(Account account, ConnectionManager.ConnectionError error); public abstract async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name); public abstract async void notify_voice_request(Conversation conversation, Jid from_jid); public abstract async void retract_content_item_notifications(); public abstract async void retract_conversation_notifications(Conversation conversation); } } dino-0.5.0/libdino/src/service/presence_manager.vala0000664000000000000000000001174314776241610021175 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class PresenceManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("presence_manager"); public string id { get { return IDENTITY.id; } } public signal void show_received(Jid jid, Account account); public signal void received_offline_presence(Jid jid, Account account); public signal void received_subscription_request(Jid jid, Account account); public signal void received_subscription_approval(Jid jid, Account account); private StreamInteractor stream_interactor; private HashMap> resources = new HashMap>(Jid.hash_bare_func, Jid.equals_bare_func); private Gee.List subscription_requests = new ArrayList(Jid.equals_func); public static void start(StreamInteractor stream_interactor) { PresenceManager m = new PresenceManager(stream_interactor); stream_interactor.add_module(m); } private PresenceManager(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.account_added.connect(on_account_added); } public string? get_last_show(Jid jid, Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return null; Xmpp.Presence.Stanza? presence = stream.get_flag(Presence.Flag.IDENTITY).get_presence(jid); if (presence == null) return null; return presence.show; } public Gee.List? get_full_jids(Jid jid, Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { Xmpp.Presence.Flag flag = stream.get_flag(Presence.Flag.IDENTITY); if (flag == null) return null; return flag.get_resources(jid.bare_jid); } return null; } public bool exists_subscription_request(Account account, Jid jid) { return subscription_requests.contains(jid); } public void request_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Presence.Module.IDENTITY).request_subscription(stream, jid.bare_jid); } public void approve_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { stream.get_module(Xmpp.Presence.Module.IDENTITY).approve_subscription(stream, jid.bare_jid); subscription_requests.remove(jid); } } public void deny_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) { stream.get_module(Xmpp.Presence.Module.IDENTITY).deny_subscription(stream, jid.bare_jid); subscription_requests.remove(jid); } } public void cancel_subscription(Account account, Jid jid) { XmppStream stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Presence.Module.IDENTITY).cancel_subscription(stream, jid.bare_jid); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_available_show.connect((stream, jid, show) => on_received_available_show(account, jid, show) ); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_unavailable.connect((stream, presence) => on_received_unavailable(account, presence.from) ); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_subscription_request.connect((stream, jid) => { if (!subscription_requests.contains(jid)) { subscription_requests.add(jid); } received_subscription_request(jid, account); }); stream_interactor.module_manager.get_module(account, Presence.Module.IDENTITY).received_subscription_approval.connect((stream, jid) => { received_subscription_approval(jid, account); }); } private void on_received_available_show(Account account, Jid jid, string show) { lock (resources) { if (!resources.has_key(jid)){ resources[jid] = new ArrayList(Jid.equals_func); } if (!resources[jid].contains(jid)) { resources[jid].add(jid); } } show_received(jid, account); } private void on_received_unavailable(Account account, Jid jid) { lock (resources) { if (resources.has_key(jid)) { resources[jid].remove(jid); if (resources[jid].size == 0 || jid.is_bare()) { resources.unset(jid); } } } received_offline_presence(jid, account); } } } dino-0.5.0/libdino/src/service/reactions.vala0000664000000000000000000005270314776241610017667 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.Reactions : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("reactions"); public string id { get { return IDENTITY.id; } } public signal void reaction_added(Account account, int content_item_id, Jid jid, string reaction); public signal void reaction_removed(Account account, int content_item_id, Jid jid, string reaction); private StreamInteractor stream_interactor; private Database db; private HashMap> reaction_infos = new HashMap>(); public static void start(StreamInteractor stream_interactor, Database database) { Reactions m = new Reactions(stream_interactor, database); stream_interactor.add_module(m); } private Reactions(StreamInteractor stream_interactor, Database database) { this.stream_interactor = stream_interactor; this.db = database; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_new_item); } public void add_reaction(Conversation conversation, ContentItem content_item, string reaction) { Gee.List reactions = get_own_reactions(conversation, content_item); if (!reactions.contains(reaction)) { reactions.add(reaction); } try { send_reactions(conversation, content_item, reactions); reaction_added(conversation.account, content_item.id, conversation.account.bare_jid, reaction); } catch (IOError e) {} } public void remove_reaction(Conversation conversation, ContentItem content_item, string reaction) { Gee.List reactions = get_own_reactions(conversation, content_item); reactions.remove(reaction); try { send_reactions(conversation, content_item, reactions); reaction_removed(conversation.account, content_item.id, conversation.account.bare_jid, reaction); } catch (IOError e) {} } public Gee.List get_item_reactions(Conversation conversation, ContentItem content_item) { if (conversation.type_ == Conversation.Type.CHAT) { return get_chat_message_reactions(conversation.account, content_item); } else { return get_muc_message_reactions(conversation.account, content_item); } } public bool conversation_supports_reactions(Conversation conversation) { if (conversation.type_ == Conversation.Type.CHAT) { return true; } else { // The MUC server needs to 1) support stable stanza ids 2) either support occupant ids or be a private room (where we know real jids) var entity_info = stream_interactor.get_module(EntityInfo.IDENTITY); bool server_supports_sid = (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xep.UniqueStableStanzaIDs.NS_URI)) || (entity_info.has_feature_cached(conversation.account, conversation.counterpart.bare_jid, Xmpp.MessageArchiveManagement.NS_URI)); if (!server_supports_sid) return false; bool? supports_occupant_ids = entity_info.has_feature_cached(conversation.account, conversation.counterpart, Xep.OccupantIds.NS_URI); if (supports_occupant_ids) return true; return stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); } } private void send_reactions(Conversation conversation, ContentItem content_item, Gee.List reactions) throws IOError { string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (message_id == null) throw new IOError.FAILED("No message for content_item"); XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) throw new IOError.NOT_CONNECTED("No stream"); var reactions_module = stream.get_module(Xmpp.Xep.Reactions.Module.IDENTITY); if (conversation.type_ == Conversation.Type.GROUPCHAT) { reactions_module.send_reaction.begin(stream, conversation.counterpart, "groupchat", message_id, reactions); // We save the reaction when it gets reflected back to us } else if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { reactions_module.send_reaction.begin(stream, conversation.counterpart, "chat", message_id, reactions); } else if (conversation.type_ == Conversation.Type.CHAT) { int64 now_millis = GLib.get_real_time () / 1000; reactions_module.send_reaction.begin(stream, conversation.counterpart, "chat", message_id, reactions, (_, res) => { try { reactions_module.send_reaction.end(res); save_chat_reactions(conversation.account, conversation.account.bare_jid, content_item.id, now_millis, reactions); } catch (IOError e) {} }); } } private Gee.List get_own_reactions(Conversation conversation, ContentItem content_item) { if (conversation.type_ == Conversation.Type.CHAT) { return get_chat_user_reactions(conversation.account, content_item.id, conversation.account.bare_jid) .emojis; } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { string own_occupant_id = stream_interactor.get_module(MucManager.IDENTITY).get_own_occupant_id(conversation.account, content_item.jid); return get_muc_user_reactions(conversation.account, content_item.id, own_occupant_id, conversation.account.bare_jid) .emojis; } return new ArrayList(); } private class ReactionsTime { public Gee.List? emojis = null; public int64 time = -1; } private ReactionsTime get_chat_user_reactions(Account account, int content_item_id, Jid jid) { int jid_id = db.get_jid_id(jid); QueryBuilder query = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item_id) .with(db.reaction.jid_id, "=", jid_id); RowOption row = query.single().row(); ReactionsTime ret = new ReactionsTime(); if (row.is_present()) { ret.emojis = string_to_emoji_list(row[db.reaction.emojis]); ret.time = row[db.reaction.time]; } else { ret.emojis = new ArrayList(); ret.time = -1; } return ret; } private ReactionsTime get_muc_user_reactions(Account account, int content_item_id, string? occupant_id, Jid? real_jid) { if (occupant_id == null && real_jid == null) critical("Need occupant id or real jid of a reaction"); QueryBuilder query = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item_id) .outer_join_with(db.occupantid, db.occupantid.id, db.reaction.occupant_id); if (occupant_id != null) { query.with(db.occupantid.occupant_id, "=", occupant_id); } else if (real_jid != null) { query.with(db.reaction.jid_id, "=", db.get_jid_id(real_jid)); } RowOption row = query.single().row(); ReactionsTime ret = new ReactionsTime(); if (row.is_present()) { ret.emojis = string_to_emoji_list(row[db.reaction.emojis]); ret.time = row[db.reaction.time]; } else { ret.emojis = new ArrayList(); ret.time = -1; } return ret; } private Gee.List string_to_emoji_list(string emoji_str) { ArrayList ret = new ArrayList(); foreach (string emoji in emoji_str.split(",")) { if (emoji.length != 0) ret.add(emoji); } return ret; } public Gee.List get_chat_message_reactions(Account account, ContentItem content_item) { QueryBuilder select = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item.id) .order_by(db.reaction.time, "DESC"); var ret = new ArrayList(); var index = new HashMap(); foreach (Row row in select) { string emoji_str = row[db.reaction.emojis]; Jid jid = db.get_jid_by_id(row[db.reaction.jid_id]); foreach (string emoji in emoji_str.split(",")) { if (!index.has_key(emoji)) { index[emoji] = new ReactionUsers() { reaction=emoji, jids=new ArrayList(Jid.equals_func) }; ret.add(index[emoji]); } index[emoji].jids.add(jid); } } return ret; } public Gee.List get_muc_message_reactions(Account account, ContentItem content_item) { QueryBuilder select = db.reaction.select() .with(db.reaction.account_id, "=", account.id) .with(db.reaction.content_item_id, "=", content_item.id) .outer_join_with(db.occupantid, db.occupantid.id, db.reaction.occupant_id) .outer_join_with(db.jid, db.jid.id, db.reaction.jid_id) .order_by(db.reaction.time, "DESC"); string? own_occupant_id = stream_interactor.get_module(MucManager.IDENTITY).get_own_occupant_id(account, content_item.jid); var ret = new ArrayList(); var index = new HashMap(); foreach (Row row in select) { string emoji_str = row[db.reaction.emojis]; Jid jid = null; if (!db.jid.bare_jid.is_null(row)) { jid = new Jid(row[db.jid.bare_jid]); } else if (!db.occupantid.occupant_id.is_null(row)) { if (row[db.occupantid.occupant_id] == own_occupant_id) { jid = account.bare_jid; } else { string nick = row[db.occupantid.last_nick]; jid = content_item.jid.with_resource(nick); } } else { warning("Reaction with neither JID nor occupant id"); } foreach (string emoji in emoji_str.split(",")) { if (!index.has_key(emoji)) { index[emoji] = new ReactionUsers() { reaction=emoji, jids=new ArrayList(Jid.equals_func) }; ret.add(index[emoji]); } index[emoji].jids.add(jid); } } return ret; } private void on_account_added(Account account) { // TODO get time from delays stream_interactor.module_manager.get_module(account, Xmpp.Xep.Reactions.Module.IDENTITY).received_reactions.connect((stream, from_jid, message_id, reactions, stanza) => { on_reaction_received.begin(account, from_jid, message_id, reactions, stanza); }); } private async void on_reaction_received(Account account, Jid from_jid, string message_id, Gee.List reactions, MessageStanza stanza) { if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { // Apply the same restrictions for incoming reactions as we do on sending them Conversation muc_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).approx_conversation_for_stanza(from_jid, account.bare_jid, account, MessageStanza.TYPE_GROUPCHAT); bool muc_supports_reactions = conversation_supports_reactions(muc_conversation); if (!muc_supports_reactions) return; } Message reaction_message = yield stream_interactor.get_module(MessageProcessor.IDENTITY).parse_message_stanza(account, stanza); Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(reaction_message); int content_item_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_id_for_message_id(conversation, message_id); var reaction_info = new ReactionInfo() { conversation=conversation, from_jid=from_jid, reactions=reactions, stanza=stanza, received_time=new DateTime.now() }; if (content_item_id != -1) { process_reaction_for_message(content_item_id, reaction_info); return; } // Store reaction infos for later processing after we got the message debug("Got reaction for %s but dont have message yet %s", message_id, db.get_jid_id(stanza.from.bare_jid).to_string()); if (!reaction_infos.has_key(message_id)) { reaction_infos[message_id] = new ArrayList(); } reaction_infos[message_id].add(reaction_info); } /* * When we get a new ContentItem, check if we have any reactions cached that apply to it. * If so, process the reactions, map and store them. */ private void on_new_item(ContentItem item, Conversation conversation) { string? stanza_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, item); if (stanza_id == null) return; Gee.List? reaction_info_list = reaction_infos[stanza_id]; if (reaction_info_list == null) return; Message? message = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_for_content_item(conversation, item); if (message == null) return; // Check if the (or potentially which) reaction fits the message var applicable_reactions = new ArrayList(); applicable_reactions.add_all_iterator(reaction_info_list.filter(info => info.conversation.equals(conversation))); foreach (ReactionInfo applicable_reaction in applicable_reactions) { reaction_info_list.remove(applicable_reaction); if (reaction_info_list.is_empty) { reaction_infos.unset(stanza_id); } debug("Got ContentItem for reaction %s", stanza_id); process_reaction_for_message(item.id, applicable_reaction); } } private Message? get_message_for_reaction(Conversation conversation, string message_id) { // Query message from a specific account and counterpart. This also makes sure it's a valid reaction for the message. if (conversation.type_ == Conversation.Type.CHAT) { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_stanza_id(message_id, conversation); } else { return stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_server_id(message_id, conversation); } } private void process_reaction_for_message(int content_item_id, ReactionInfo reaction_info) { Account account = reaction_info.conversation.account; MessageStanza stanza = reaction_info.stanza; Jid from_jid = reaction_info.from_jid; Gee.List reactions = reaction_info.reactions; // Get reaction time DateTime? reaction_time = null; DelayedDelivery.MessageFlag? delayed_message_flag = DelayedDelivery.MessageFlag.get_flag(stanza); if (delayed_message_flag != null) { reaction_time = delayed_message_flag.datetime; } if (reaction_time == null) { MessageArchiveManagement.MessageFlag? mam_message_flag = MessageArchiveManagement.MessageFlag.get_flag(stanza); if (mam_message_flag != null) reaction_time = mam_message_flag.server_time; } var time_now = new DateTime.now_local(); if (reaction_time == null) reaction_time = time_now; if (reaction_time.compare(time_now) > 0) { reaction_time = reaction_info.received_time; } int64 reaction_time_long = (int64) (reaction_time.to_unix() * 1000 + reaction_time.get_microsecond() / 1000); // Get current reactions string? occupant_id = OccupantIds.get_occupant_id(stanza.stanza); Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(from_jid, account); if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT && occupant_id == null && real_jid == null) { warning("Attempting to add reaction to message w/o knowing occupant id or real jid"); return; } ReactionsTime reactions_time = null; if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { reactions_time = get_muc_user_reactions(account, content_item_id, occupant_id, real_jid); } else { reactions_time = get_chat_user_reactions(account, content_item_id, from_jid); } if (reaction_time_long <= reactions_time.time) { // We already have a more recent reaction return; } // Save reactions if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { save_muc_reactions(account, content_item_id, from_jid, occupant_id, real_jid, reaction_time_long, reactions); } else { save_chat_reactions(account, from_jid, content_item_id, reaction_time_long, reactions); } // Notify about reaction changes Gee.List? current_reactions = reactions_time.emojis; Jid signal_jid = from_jid; if (stanza.type_ == MessageStanza.TYPE_GROUPCHAT && signal_jid.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(from_jid, account))) { signal_jid = account.bare_jid; } else if (stanza.type_ == MessageStanza.TYPE_CHAT) { signal_jid = signal_jid.bare_jid; } foreach (string current_reaction in current_reactions) { if (!reactions.contains(current_reaction)) { reaction_removed(account, content_item_id, signal_jid, current_reaction); } } foreach (string new_reaction in reactions) { if (!current_reactions.contains(new_reaction)) { reaction_added(account, content_item_id, signal_jid, new_reaction); } } debug("reactions were: "); foreach (string reac in current_reactions) { debug(reac); } debug("reactions new : "); foreach (string reac in reactions) { debug(reac); } } private void save_chat_reactions(Account account, Jid jid, int content_item_id, int64 reaction_time, Gee.List reactions) { var emoji_builder = new StringBuilder(); for (int i = 0; i < reactions.size; i++) { if (i != 0) emoji_builder.append(","); emoji_builder.append(reactions[i]); } db.reaction.upsert() .value(db.reaction.account_id, account.id, true) .value(db.reaction.content_item_id, content_item_id, true) .value(db.reaction.jid_id, db.get_jid_id(jid), true) .value(db.reaction.emojis, emoji_builder.str, false) .value(db.reaction.time, (long)reaction_time, false) .perform(); } private void save_muc_reactions(Account account, int content_item_id, Jid jid, string? occupant_id, Jid? real_jid, int64 reaction_time, Gee.List reactions) { assert(occupant_id != null || real_jid != null); int jid_id = db.get_jid_id(jid); var emoji_builder = new StringBuilder(); for (int i = 0; i < reactions.size; i++) { if (i != 0) emoji_builder.append(","); emoji_builder.append(reactions[i]); } var builder = db.reaction.upsert() .value(db.reaction.account_id, account.id, true) .value(db.reaction.content_item_id, content_item_id, true) .value(db.reaction.emojis, emoji_builder.str, false) .value(db.reaction.time, (long)reaction_time, false); if (real_jid != null) { builder.value(db.reaction.jid_id, db.get_jid_id(real_jid), occupant_id == null); } if (occupant_id != null) { RowOption row = db.occupantid.select() .with(db.occupantid.account_id, "=", account.id) .with(db.occupantid.jid_id, "=", jid_id) .with(db.occupantid.occupant_id, "=", occupant_id) .single().row(); int occupant_db_id = -1; if (row.is_present()) { occupant_db_id = row[db.occupantid.id]; } else { occupant_db_id = (int)db.occupantid.upsert() .value(db.occupantid.account_id, account.id, true) .value(db.occupantid.jid_id, jid_id, true) .value(db.occupantid.occupant_id, occupant_id, true) .value(db.occupantid.last_nick, jid.resourcepart, false) .perform(); } builder.value(db.reaction.occupant_id, occupant_db_id, true); } builder.perform(); } } public class Dino.ReactionUsers { public string reaction { get; set; } public Gee.List jids { get; set; } } public class Dino.ReactionInfo { public Conversation conversation { get; set; } public Jid from_jid { get; set; } public Gee.List reactions { get; set; } public MessageStanza stanza { get; set; } public DateTime received_time { get; set; } } dino-0.5.0/libdino/src/service/registration.vala0000664000000000000000000001776414776241610020422 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class Register : StreamInteractionModule, Object{ public static ModuleIdentity IDENTITY = new ModuleIdentity("registration"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { Register m = new Register(stream_interactor, db); stream_interactor.add_module(m); } private Register(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public async ConnectionManager.ConnectionError.Source? add_check_account(Account account) { ConnectionManager.ConnectionError.Source? ret = null; Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Sasl.Module(account.bare_jid.to_string(), account.password)); XmppStreamResult stream_result = yield Xmpp.establish_stream(account.bare_jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(account.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.tls_errors != null) { ret = ConnectionManager.ConnectionError.Source.TLS; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = add_check_account.callback; stream.stream_negotiated.connect(() => { if (callback == null) return; Idle.add((owned)callback); }); stream.get_module(Sasl.Module.IDENTITY).received_auth_failure.connect((stream, node) => { if (callback == null) return; ret = ConnectionManager.ConnectionError.Source.SASL; Idle.add((owned)callback); }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { ret = ConnectionManager.ConnectionError.Source.CONNECTION; Idle.add((owned)callback); } }); yield; try { yield stream_result.stream.disconnect(); } catch (Error e) {} return ret; } public async string? change_password(Account account, string new_pw){ XmppStream stream = stream_interactor.get_stream(account); if (stream == null) return null; return (yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).change_password(stream, account.full_jid, new_pw)).condition; } public class ServerAvailabilityReturn { public bool available { get; set; } public TlsCertificateFlags? error_flags { get; set; } } public static async ServerAvailabilityReturn check_server_availability(Jid jid) { ServerAvailabilityReturn ret = new ServerAvailabilityReturn() { available=false }; Gee.List list = new ArrayList(); list.add(new Iq.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.io_error != null) { debug("Error connecting to stream: %s", stream_result.io_error.message); } if (stream_result.tls_errors != null) { ret.error_flags = stream_result.tls_errors; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = check_server_availability.callback; stream.stream_negotiated.connect(() => { if (callback != null) { ret.available = true; Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; try { yield stream.disconnect(); } catch (Error e) {} return ret; } public class RegistrationFormReturn { public Xep.InBandRegistration.Form? form { get; set; } public TlsCertificateFlags? error_flags { get; set; } } public static async RegistrationFormReturn get_registration_form(Jid jid) { RegistrationFormReturn ret = new RegistrationFormReturn(); Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { if (stream_result.io_error != null) { debug("Error connecting to stream: %s", stream_result.io_error.message); } if (stream_result.tls_errors != null) { ret.error_flags = stream_result.tls_errors; } return ret; } XmppStream stream = stream_result.stream; SourceFunc callback = get_registration_form.callback; stream.stream_negotiated.connect(() => { if (callback != null) { Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; if (stream.negotiation_complete) { ret.form = yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).get_from_server(stream, jid); } try { yield stream.disconnect(); } catch (Error e) {} return ret; } public static async string? submit_form(Jid jid, Xep.InBandRegistration.Form form) { Gee.List list = new ArrayList(); list.add(new Iq.Module()); list.add(new Xep.InBandRegistration.Module()); XmppStreamResult stream_result = yield Xmpp.establish_stream(jid.domain_jid, list, Application.print_xmpp, (peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(jid.domainpart, peer_cert, errors); } ); if (stream_result.stream == null) { return null; } XmppStream stream = stream_result.stream; SourceFunc callback = submit_form.callback; stream.stream_negotiated.connect(() => { if (callback != null) { Idle.add((owned)callback); } }); stream.loop.begin((_, res) => { try { stream.loop.end(res); } catch (Error e) { debug("Error connecting to stream: %s", e.message); } if (callback != null) { Idle.add((owned)callback); } }); yield; string? ret = null; if (stream.negotiation_complete) { ret = yield stream.get_module(Xep.InBandRegistration.Module.IDENTITY).submit_to_server(stream, jid, form); } try { yield stream.disconnect(); } catch (Error e) {} return ret; } } } dino-0.5.0/libdino/src/service/replies.vala0000664000000000000000000001103214776241610017331 0ustar rootrootusing Gee; using Qlite; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.Replies : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("reply"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private HashMap>> unmapped_replies = new HashMap>>(); private ReceivedMessageListener received_message_listener; public static void start(StreamInteractor stream_interactor, Database db) { Replies m = new Replies(stream_interactor, db); stream_interactor.add_module(m); } private Replies(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.received_message_listener = new ReceivedMessageListener(stream_interactor, this); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); } public ContentItem? get_quoted_content_item(Message message, Conversation conversation) { if (message.quoted_item_id == 0) return null; RowOption row_option = db.reply.select().with(db.reply.message_id, "=", message.id).row(); if (row_option.is_present()) { return stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, row_option[db.reply.quoted_content_item_id]); } return null; } private void on_incoming_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { // Check if a previous message was in reply to this one var reply_qry = db.reply.select(); if (conversation.type_ == Conversation.Type.GROUPCHAT) { reply_qry.with(db.reply.quoted_message_stanza_id, "=", message.server_id); } else { reply_qry.with(db.reply.quoted_message_stanza_id, "=", message.stanza_id); } reply_qry.join_with(db.message, db.reply.message_id, db.message.id) .with(db.message.account_id, "=", conversation.account.id) .with(db.message.counterpart_id, "=", db.get_jid_id(conversation.counterpart)) .with(db.message.time, ">", (long)message.time.to_unix()) .order_by(db.message.time, "DESC"); foreach (Row reply_row in reply_qry) { ContentItem? message_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); Message? reply_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(reply_row[db.message.id], conversation); if (message_item != null && reply_message != null) { reply_message.set_quoted_item(message_item.id); } } // Handle if this message is a reply Xep.Replies.ReplyTo? reply_to = Xep.Replies.get_reply_to(stanza); if (reply_to == null) return; ContentItem? quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_content_item_for_message_id(conversation, reply_to.to_message_id); if (quoted_content_item == null) return; message.set_quoted_item(quoted_content_item.id); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE", "STORE_CONTENT_ITEM" }; public override string action_group { get { return "Quote"; } } public override string[] after_actions { get { return after_actions_const; } } private Replies outer; public ReceivedMessageListener(StreamInteractor stream_interactor, Replies outer) { this.outer = outer; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { outer.on_incoming_message(message, stanza, conversation); return false; } } } namespace Dino { public string message_body_without_reply_fallback(Message message) { string body = message.body; foreach (var fallback in message.get_fallbacks()) { if (fallback.ns_uri == Xep.Replies.NS_URI && message.quoted_item_id > 0) { body = body[0:body.index_of_nth_char(fallback.locations[0].from_char)] + body[body.index_of_nth_char(fallback.locations[0].to_char):body.length]; } } return body; } } dino-0.5.0/libdino/src/service/roster_manager.vala0000664000000000000000000001346614776241610020713 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class RosterManager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("roster_manager"); public string id { get { return IDENTITY.id; } } public signal void removed_roster_item(Account account, Jid jid, Roster.Item roster_item); public signal void updated_roster_item(Account account, Jid jid, Roster.Item roster_item); public signal void mutual_subscription(Account account, Jid jid); private StreamInteractor stream_interactor; private Database db; private Gee.Map roster_stores = new HashMap(Account.hash_func, Account.equals_func); public static void start(StreamInteractor stream_interactor, Database db) { RosterManager m = new RosterManager(stream_interactor, db); stream_interactor.add_module(m); } public RosterManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.module_manager.initialize_account_modules.connect((account, modules) => { if (!roster_stores.has_key(account)) roster_stores[account] = new RosterStoreImpl(account, db); modules.add(new Roster.VersioningModule(roster_stores[account])); }); } public Collection get_roster(Account account) { if (roster_stores[account] == null) return new ArrayList(); return roster_stores[account].get_roster(); } public Roster.Item? get_roster_item(Account account, Jid jid) { if (roster_stores[account] == null) return null; return roster_stores[account].get_item(jid); } public void remove_jid(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).remove_jid(stream, jid.bare_jid); } public void add_jid(Account account, Jid jid, string? handle) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).add_jid(stream, jid.bare_jid, handle); } public void set_jid_handle(Account account, Jid jid, string? handle) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) stream.get_module(Xmpp.Roster.Module.IDENTITY).set_jid_handle(stream, jid.bare_jid, handle); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).received_roster.connect_after( (stream, roster) => { foreach (Roster.Item roster_item in roster) { on_roster_item_updated(account, roster_item); } }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).item_removed.connect_after( (stream, roster_item) => { removed_roster_item(account, roster_item.jid, roster_item); }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).item_updated.connect_after( (stream, roster_item) => { on_roster_item_updated(account, roster_item); }); stream_interactor.module_manager.get_module(account, Roster.Module.IDENTITY).mutual_subscription.connect_after( (stream, jid) => { mutual_subscription(account, jid); }); } private void on_roster_item_updated(Account account, Roster.Item roster_item) { updated_roster_item(account, roster_item.jid, roster_item); } } public class RosterStoreImpl : Roster.Storage, Object { private Account account; private Database db; private HashMap items = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); public class RosterStoreImpl(Account account, Database db) { this.account = account; this.db = db; foreach (Qlite.Row row in db.roster.select().with(db.roster.account_id, "=", account.id)) { try { Roster.Item item = new Roster.Item(); item.jid = new Jid(row[db.roster.jid]); item.name = row[db.roster.handle]; item.subscription = row[db.roster.subscription]; items[item.jid] = item; } catch (InvalidJidError e) { warning("Ignoring roster entry with invalid Jid: %s", e.message); } } } public string? get_roster_version() { return account.roster_version; } public Collection get_roster() { return items.values; } public Roster.Item? get_item(Jid jid) { return items.has_key(jid) ? items[jid] : null; } public void set_roster_version(string version) { account.roster_version = version; } public void set_roster(Collection items) { db.roster.delete().with(db.roster.account_id, "=", account.id).perform(); foreach (Roster.Item item in items) { set_item(item); } } public void set_item(Roster.Item item) { items[item.jid] = item; db.roster.upsert() .value(db.roster.account_id, account.id, true) .value(db.roster.jid, item.jid.to_string(), true) .value(db.roster.handle, item.name) .value(db.roster.subscription, item.subscription) .value(db.roster.ask, item.ask) .perform(); } public void remove_item(Roster.Item item) { items.unset(item.jid); db.roster.delete() .with(db.roster.account_id, "=", account.id) .with(db.roster.jid, "=", item.jid.to_string()).perform(); } } } dino-0.5.0/libdino/src/service/search_processor.vala0000664000000000000000000003553514776241610021250 0ustar rootrootusing Gee; using Xmpp; using Qlite; using Dino.Entities; namespace Dino { public class SearchProcessor : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("search_processor"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; public static void start(StreamInteractor stream_interactor, Database db) { SearchProcessor m = new SearchProcessor(stream_interactor, db); stream_interactor.add_module(m); } public SearchProcessor(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } private QueryBuilder prepare_search(string query, bool join_content) { string words = ""; string? with = null; string? in_ = null; string? from = null; foreach(string word in query.split(" ")) { if (word.has_prefix("with:")) { if (with == null) { @with = word.substring(5); } else { return db.message.select().where("0"); } } else if (word.has_prefix("in:")) { if (in_ == null) { in_ = word.substring(3); } else { return db.message.select().where("0"); } } else if (word.has_prefix("from:")) { if (from == null) { from = word.substring(5); } else { return db.message.select().where("0"); } } else { words += word + "* "; } } if (in_ != null && with != null) { return db.message.select().where("0"); } QueryBuilder rows = db.message .match(db.message.body, words) .order_by(db.message.id, "DESC") .join_with(db.jid, db.jid.id, db.message.counterpart_id) .join_with(db.account, db.account.id, db.message.account_id) .outer_join_with(db.real_jid, db.real_jid.message_id, db.message.id) .with(db.account.enabled, "=", true); if (join_content) { rows.join_on(db.content_item, "message.id=content_item.foreign_id AND content_item.content_type=1") .with(db.content_item.content_type, "=", 1); } if (with != null) { if (with.index_of("/") > 0) { rows.with(db.message.type_, "=", Message.Type.GROUPCHAT_PM) .with(db.jid.bare_jid, "LIKE", with.substring(0, with.index_of("/"))) .with(db.message.counterpart_resource, "LIKE", with.substring(with.index_of("/") + 1)); } else { rows.where(@"($(db.message.type_) = $((int)Message.Type.CHAT) AND $(db.jid.bare_jid) LIKE ?)" + @" OR ($(db.message.type_) = $((int)Message.Type.GROUPCHAT_PM) AND $(db.real_jid.real_jid) LIKE ?)" + @" OR ($(db.message.type_) = $((int)Message.Type.GROUPCHAT_PM) AND $(db.message.counterpart_resource) LIKE ?)", {with, with, with}); } } else if (in_ != null) { rows.with(db.jid.bare_jid, "LIKE", in_) .with(db.message.type_, "=", Message.Type.GROUPCHAT); } if (from != null) { rows.where(@"($(db.message.direction) = 1 AND $(db.account.bare_jid) LIKE ?)" + @" OR ($(db.message.direction) = 1 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.message.our_resource) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) = $((int)Message.Type.CHAT) AND $(db.jid.bare_jid) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.real_jid.real_jid) LIKE ?)" + @" OR ($(db.message.direction) = 0 AND $(db.message.type_) IN ($((int)Message.Type.GROUPCHAT), $((int)Message.Type.GROUPCHAT_PM)) AND $(db.message.counterpart_resource) LIKE ?)", {from, from, from, from, from}); } return rows; } public Gee.List suggest_auto_complete(string query, int cursor_position, int limit = 5) { int after_prev_space = query.substring(0, cursor_position).last_index_of(" ") + 1; int next_space = query.index_of(" ", after_prev_space); if (next_space < 0) next_space = query.length; string current_query = query.substring(after_prev_space, next_space - after_prev_space); Gee.List suggestions = new ArrayList(); if (current_query.has_prefix("from:")) { if (cursor_position < after_prev_space + 5) return suggestions; string current_from = current_query.substring(5); string[] splitted = query.split(" "); foreach(string s in splitted) { if (s.has_prefix("from:") && s != "from:" + current_from) { // Already have an from: filter -> no useful autocompletion possible return suggestions; } } string? current_in = null; string? current_with = null; foreach(string s in splitted) { if (s.has_prefix("in:")) { current_in = s.substring(3); } else if (s.has_prefix("with:")) { current_with = s.substring(5); } } if (current_in != null && current_with != null) { // in: and with: -> no useful autocompletion possible return suggestions; } if (current_with != null) { // Can only be the other one or us // Normal chat QueryBuilder chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .with(db.jid.bare_jid, "=", current_with) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.CHAT) .order_by(db.conversation.last_active, "DESC"); foreach(Row chat in chats) { try { if (suggestions.size == 0) { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "from:"+chat[db.jid.bare_jid], after_prev_space, next_space)); } suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.account.bare_jid]), "from:"+chat[db.account.bare_jid], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } return suggestions; } if (current_in != null) { // All members of the MUC with history QueryBuilder msgs = db.message.select() .select_string(@"account.*, $(db.message.counterpart_resource), conversation.*") .join_with(db.jid, db.jid.id, db.message.counterpart_id) .join_with(db.account, db.account.id, db.message.account_id) .join_on(db.conversation, @"$(db.conversation.account_id)=$(db.account.id) AND $(db.conversation.jid_id)=$(db.jid.id)") .with(db.jid.bare_jid, "=", current_in) .with(db.account.enabled, "=", true) .with(db.message.type_, "=", Message.Type.GROUPCHAT) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT) .with(db.message.counterpart_resource, "LIKE", @"%$current_from%") .group_by({db.message.counterpart_resource}) .order_by_name(@"MAX($(db.message.time))", "DESC") .limit(5); foreach(Row msg in msgs) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, msg), new Jid(current_in).with_resource(msg[db.message.counterpart_resource]), "from:"+msg[db.message.counterpart_resource], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } } // TODO: auto complete from } else if (current_query.has_prefix("with:")) { if (cursor_position < after_prev_space + 5) return suggestions; string current_with = current_query.substring(5); string[] splitted = query.split(" "); foreach(string s in splitted) { if ((s.has_prefix("with:") && s != "with:" + current_with) || s.has_prefix("in:")) { // Already have an in: or with: filter -> no useful autocompletion possible return suggestions; } } // Normal chat QueryBuilder chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .outer_join_on(db.roster, @"$(db.jid.bare_jid) = $(db.roster.jid) AND $(db.account.id) = $(db.roster.account_id)") .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.roster.handle) LIKE ?", {@"%$current_with%", @"%$current_with%"}) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.CHAT) .order_by(db.conversation.last_active, "DESC") .limit(limit); foreach(Row chat in chats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "with:"+chat[db.jid.bare_jid], after_prev_space, next_space) { order = chat[db.conversation.last_active]}); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } // Groupchat PM if (suggestions.size < 5) { chats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .where(@"$(db.jid.bare_jid) LIKE ? OR $(db.conversation.resource) LIKE ?", {@"%$current_with%", @"%$current_with%"}) .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT_PM) .order_by(db.conversation.last_active, "DESC") .limit(limit - suggestions.size); foreach(Row chat in chats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]).with_resource(chat[db.conversation.resource]), "with:"+chat[db.jid.bare_jid]+"/"+chat[db.conversation.resource], after_prev_space, next_space) { order = chat[db.conversation.last_active]}); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } suggestions.sort((a, b) => (int)(b.order - a.order)); } } else if (current_query.has_prefix("in:")) { if (cursor_position < after_prev_space + 3) return suggestions; string current_in = current_query.substring(3); string[] splitted = query.split(" "); foreach(string s in splitted) { if ((s.has_prefix("in:") && s != "in:" + current_in) || s.has_prefix("with:")) { // Already have an in: or with: filter -> no useful autocompletion possible return suggestions; } } QueryBuilder groupchats = db.conversation.select() .join_with(db.jid, db.jid.id, db.conversation.jid_id) .join_with(db.account, db.account.id, db.conversation.account_id) .with(db.jid.bare_jid, "LIKE", @"%$current_in%") .with(db.account.enabled, "=", true) .with(db.conversation.type_, "=", Conversation.Type.GROUPCHAT) .order_by(db.conversation.last_active, "DESC") .limit(limit); foreach(Row chat in groupchats) { try { suggestions.add(new SearchSuggestion(new Conversation.from_row(db, chat), new Jid(chat[db.jid.bare_jid]), "in:"+chat[db.jid.bare_jid], after_prev_space, next_space)); } catch (InvalidJidError e) { warning("Ignoring search suggestion with invalid Jid: %s", e.message); } } } else { // Other auto complete? } return suggestions; } public Gee.List match_messages(string query, int offset = -1) { Gee.List ret = new ArrayList(); QueryBuilder rows = prepare_search(query, true).limit(10); if (offset > 0) { rows.offset(offset); } foreach (Row row in rows) { try { Message message = new Message.from_row(db, row); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_for_message(message); ret.add(new MessageItem(message, conversation, row[db.content_item.id])); } catch (InvalidJidError e) { warning("Ignoring search result with invalid Jid: %s", e.message); } } return ret; } public int count_match_messages(string query) { return (int)prepare_search(query, false).select({db.message.id}).count(); } } public class SearchSuggestion : Object { public Account account { get { return conversation.account; } } public Conversation conversation { get; private set; } public Jid? jid { get; private set; } public string completion { get; private set; } public int start_index { get; private set; } public int end_index { get; private set; } public long order { get; set; } public SearchSuggestion(Conversation conversation, Jid? jid, string completion, int start_index, int end_index) { this.conversation = conversation; this.jid = jid; this.completion = completion; this.start_index = start_index; this.end_index = end_index; } } } dino-0.5.0/libdino/src/service/sfs_metadata.vala0000664000000000000000000000672614776241610020337 0ustar rootrootusing Gdk; using GLib; using Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; namespace Dino { public interface FileMetadataProvider : Object { public abstract bool supports_file(File file); public abstract async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata); } class GenericFileMetadataProvider: Dino.FileMetadataProvider, Object { public bool supports_file(File file) { return true; } public async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata) { FileInfo info = file.query_info("*", FileQueryInfoFlags.NONE); metadata.name = info.get_display_name(); metadata.mime_type = info.get_content_type(); metadata.size = info.get_size(); metadata.date = info.get_modification_date_time(); var checksum_types = new ArrayList.wrap(new ChecksumType[] { ChecksumType.SHA256, ChecksumType.SHA512 }); var file_hashes = yield compute_file_hashes(file, checksum_types); metadata.hashes.add(new CryptographicHashes.Hash.with_checksum(ChecksumType.SHA256, file_hashes[ChecksumType.SHA256])); metadata.hashes.add(new CryptographicHashes.Hash.with_checksum(ChecksumType.SHA512, file_hashes[ChecksumType.SHA512])); } } public class ImageFileMetadataProvider: Dino.FileMetadataProvider, Object { public bool supports_file(File file) { string mime_type = file.query_info("*", FileQueryInfoFlags.NONE).get_content_type(); return Dino.Util.is_pixbuf_supported_mime_type(mime_type); } private const int[] THUMBNAIL_DIMS = { 1, 2, 3, 4, 8 }; private const string IMAGE_TYPE = "png"; private const string MIME_TYPE = "image/png"; public async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata) { Pixbuf pixbuf = new Pixbuf.from_stream(yield file.read_async()); metadata.width = pixbuf.get_width(); metadata.height = pixbuf.get_height(); float ratio = (float)metadata.width / (float) metadata.height; int thumbnail_width = -1; int thumbnail_height = -1; float diff = float.INFINITY; for (int i = 0; i < THUMBNAIL_DIMS.length; i++) { int test_width = THUMBNAIL_DIMS[i]; int test_height = THUMBNAIL_DIMS[THUMBNAIL_DIMS.length - 1 - i]; float test_ratio = (float)test_width / (float)test_height; float test_diff = (test_ratio - ratio).abs(); if (test_diff < diff) { thumbnail_width = test_width; thumbnail_height = test_height; diff = test_diff; } } Pixbuf thumbnail_pixbuf = pixbuf.scale_simple(thumbnail_width, thumbnail_height, InterpType.BILINEAR); uint8[] buffer; thumbnail_pixbuf.save_to_buffer(out buffer, IMAGE_TYPE); string base_64 = GLib.Base64.encode(buffer); string uri = @"data:$MIME_TYPE;base64,$base_64"; Xep.JingleContentThumbnails.Thumbnail thumbnail = new Xep.JingleContentThumbnails.Thumbnail(); thumbnail.uri = uri; thumbnail.media_type = MIME_TYPE; thumbnail.width = thumbnail_width; thumbnail.height = thumbnail_height; metadata.thumbnails.add(thumbnail); } } } dino-0.5.0/libdino/src/service/stateless_file_sharing.vala0000664000000000000000000001664114776241610022422 0ustar rootrootusing Gdk; using Gee; using Xmpp; using Xmpp.Xep; using Dino.Entities; public class Dino.StatelessFileSharing : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("sfs"); public string id { get { return IDENTITY.id; } } public const int SFS_PROVIDER_ID = 2; public StreamInteractor stream_interactor { owned get { return Application.get_default().stream_interactor; } private set { } } public FileManager file_manager { owned get { return stream_interactor.get_module(FileManager.IDENTITY); } private set { } } public Database db { owned get { return Application.get_default().db; } private set { } } private StatelessFileSharing(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this)); } public static void start(StreamInteractor stream_interactor, Database db) { StatelessFileSharing m = new StatelessFileSharing(stream_interactor, db); stream_interactor.add_module(m); } public async void create_file_transfer(Conversation conversation, Message message, string? file_sharing_id, Xep.FileMetadataElement.FileMetadata metadata, Gee.List? sources) { FileTransfer file_transfer = new FileTransfer(); file_transfer.file_sharing_id = file_sharing_id; file_transfer.account = message.account; file_transfer.counterpart = message.counterpart; file_transfer.ourpart = message.ourpart; file_transfer.direction = message.direction; file_transfer.time = message.time; file_transfer.local_time = message.local_time; file_transfer.provider = SFS_PROVIDER_ID; file_transfer.file_metadata = metadata; file_transfer.info = message.id.to_string(); if (sources != null) { file_transfer.sfs_sources = sources; } stream_interactor.get_module(FileTransferStorage.IDENTITY).add_file(file_transfer); conversation.last_active = file_transfer.time; file_manager.received_file(file_transfer, conversation); } public void on_received_sources(Jid from, Conversation conversation, string attach_to_message_id, string? attach_to_file_id, Gee.List sources) { Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_referencing_id(attach_to_message_id, conversation); if (message == null) return; FileTransfer? file_transfer = null; if (attach_to_file_id != null) { file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_files_by_message_and_file_id(message.id, attach_to_file_id, conversation); } else { file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_message_id(message.id, conversation); } if (file_transfer == null) return; // "If no is provided or the elements provided use unsupported algorithms, receiving clients MUST ignore // any attached sources from other senders and only obtain the file from the sources announced by the original sender." // For now we only allow the original sender if (from.equals(file_transfer.from) && Xep.CryptographicHashes.get_supported_hashes(file_transfer.hashes).is_empty) { warning("Ignoring sfs source: Not from original sender or no known file hashes"); return; } foreach (var source in sources) { file_transfer.add_sfs_source(source); } if (file_manager.is_sender_trustworthy(file_transfer, conversation) && file_transfer.state == FileTransfer.State.NOT_STARTED && file_transfer.size >= 0 && file_transfer.size < 5000000) { file_manager.download_file(file_transfer); } } /* public async void create_sfs_for_legacy_transfer(FileProvider file_provider, string info, Jid from, DateTime time, DateTime local_time, Conversation conversation, FileReceiveData receive_data, FileMeta file_meta) { FileTransfer file_transfer = file_manager.create_file_transfer_from_provider_incoming(file_provider, info, from, time, local_time, conversation, receive_data, file_meta); HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) return; var sources = new ArrayList(); Xep.StatelessFileSharing.HttpSource source = new Xep.StatelessFileSharing.HttpSource(); source.url = http_receive_data.url; sources.add(source); if (file_manager.is_jid_trustworthy(from, conversation)) { try { file_meta = yield file_provider.get_meta_info(file_transfer, http_receive_data, file_meta); } catch (Error e) { warning("Http meta request failed: %s", e.message); } } var metadata = new Xep.FileMetadataElement.FileMetadata(); metadata.size = file_meta.size; metadata.name = file_meta.file_name; metadata.mime_type = file_meta.mime_type; file_transfer.provider = SFS_PROVIDER_ID; file_transfer.file_metadata = metadata; file_transfer.sfs_sources = sources; } */ private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "MESSAGE_REINTERPRETING"; } } public override string[] after_actions { get { return after_actions_const; } } private StatelessFileSharing outer; private StreamInteractor stream_interactor; public ReceivedMessageListener(StatelessFileSharing outer) { this.outer = outer; this.stream_interactor = outer.stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { Gee.List file_shares = Xep.StatelessFileSharing.get_file_shares(stanza); if (file_shares != null) { // For now, only accept file shares that have at least one supported hash foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) { if (!Xep.CryptographicHashes.has_supported_hashes(file_share.metadata.hashes)) { return false; } } foreach (Xep.StatelessFileSharing.FileShare file_share in file_shares) { outer.create_file_transfer(conversation, message, file_share.id, file_share.metadata, file_share.sources); } return true; } var source_attachments = Xep.StatelessFileSharing.get_source_attachments(stanza); if (source_attachments != null) { foreach (var source_attachment in source_attachments) { outer.on_received_sources(stanza.from, conversation, source_attachment.to_message_id, source_attachment.to_file_transfer_id, source_attachment.sources); return true; } } return false; } } }dino-0.5.0/libdino/src/service/stream_interactor.vala0000664000000000000000000000646414776241610021430 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; namespace Dino { public class StreamInteractor : Object { public signal void account_added(Account account); public signal void account_removed(Account account); public signal void stream_resumed(Account account, XmppStream stream); public signal void stream_negotiated(Account account, XmppStream stream); public signal void stream_attached_modules(Account account, XmppStream stream); public ModuleManager module_manager; public ConnectionManager connection_manager; private ArrayList modules = new ArrayList(); public StreamInteractor(Database db) { module_manager = new ModuleManager(); connection_manager = new ConnectionManager(module_manager); connection_manager.stream_opened.connect(on_stream_opened); connection_manager.stream_attached_modules.connect((account, stream) => { stream_attached_modules(account, stream); }); } public void connect_account(Account account) { module_manager.initialize(account); account_added(account); connection_manager.connect_account(account); } public async void disconnect_account(Account account) { yield connection_manager.disconnect_account(account); account_removed(account); } public ArrayList get_accounts() { ArrayList ret = new ArrayList(Account.equals_func); foreach (Account account in connection_manager.get_managed_accounts()) { ret.add(account); } return ret; } public XmppStream? get_stream(Account account) { return connection_manager.get_stream(account); } public void add_module(StreamInteractionModule module) { modules.add(module); } public T? get_module(ModuleIdentity? identity) { if (identity == null) return null; foreach (StreamInteractionModule module in modules) { if (identity.matches(module)) return identity.cast(module); } return null; } public new T? get() { foreach (StreamInteractionModule module in modules) { if (module.get_type() == typeof(T)) return (T?) module; } return null; } private void on_stream_opened(Account account, XmppStream stream) { stream.stream_negotiated.connect( (stream) => { var flag = stream.get_flag(Xep.StreamManagement.Flag.IDENTITY); if (flag == null || flag.resumed == false) { stream_negotiated(account, stream); } else if (flag != null && flag.resumed == true) { stream_resumed(account, stream); } }); } } public class ModuleIdentity : Object { public string id { get; private set; } public ModuleIdentity(string id) { this.id = id; } public T? cast(StreamInteractionModule module) { #if VALA_0_56_11 // We can't typecheck due to compiler bug return (T) module; #else return module.get_type().is_a(typeof(T)) ? (T?) module : null; #endif } public bool matches(StreamInteractionModule module) { return module.id == id; } } public interface StreamInteractionModule : Object { public abstract string id { get; } } } dino-0.5.0/libdino/src/service/util.vala0000664000000000000000000000270214776241610016647 0ustar rootrootusing Dino.Entities; using Qlite; namespace Dino { public class Util { public static Message.Type get_message_type_for_conversation(Conversation conversation) { switch (conversation.type_) { case Conversation.Type.CHAT: return Entities.Message.Type.CHAT; case Conversation.Type.GROUPCHAT: return Entities.Message.Type.GROUPCHAT; case Conversation.Type.GROUPCHAT_PM: return Entities.Message.Type.GROUPCHAT_PM; default: assert_not_reached(); } } public static Conversation.Type get_conversation_type_for_message(Message message) { switch (message.type_) { case Entities.Message.Type.CHAT: return Conversation.Type.CHAT; case Entities.Message.Type.GROUPCHAT: return Conversation.Type.GROUPCHAT; case Entities.Message.Type.GROUPCHAT_PM: return Conversation.Type.GROUPCHAT_PM; default: assert_not_reached(); } } public static bool is_pixbuf_supported_mime_type(string mime_type) { if (mime_type == null) return false; foreach (Gdk.PixbufFormat pixbuf_format in Gdk.Pixbuf.get_formats()) { foreach (string pixbuf_mime in pixbuf_format.get_mime_types()) { if (pixbuf_mime == mime_type) return true; } } return false; } } } dino-0.5.0/libdino/src/util/0000775000000000000000000000000014776241610014341 5ustar rootrootdino-0.5.0/libdino/src/util/display_name.vala0000664000000000000000000001265314776241610017662 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public static string get_conversation_display_name(StreamInteractor stream_interactor, Conversation conversation, string? muc_pm_format) { if (conversation.type_ == Conversation.Type.CHAT) { string? display_name = get_real_display_name(stream_interactor, conversation.account, conversation.counterpart); if (display_name != null) return display_name; return conversation.counterpart.to_string(); } if (conversation.type_ == Conversation.Type.GROUPCHAT) { return get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart); } if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { return (muc_pm_format ?? "%s / %s").printf(get_occupant_display_name(stream_interactor, conversation, conversation.counterpart), get_groupchat_display_name(stream_interactor, conversation.account, conversation.counterpart.bare_jid)); } return conversation.counterpart.to_string(); } public static string get_participant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid participant, string? self_word = null) { if (conversation.type_ == Conversation.Type.CHAT) { return get_real_display_name(stream_interactor, conversation.account, participant, self_word) ?? participant.bare_jid.to_string(); } if ((conversation.type_ == Conversation.Type.GROUPCHAT || conversation.type_ == Conversation.Type.GROUPCHAT_PM)) { return get_occupant_display_name(stream_interactor, conversation, participant); } return participant.bare_jid.to_string(); } public static string? get_real_display_name(StreamInteractor stream_interactor, Account account, Jid jid, string? self_word = null) { if (jid.equals_bare(account.bare_jid)) { if (self_word != null && (account.alias == null || account.alias.length == 0)) { return self_word; } if (account.alias != null && account.alias.length == 0) return null; return account.alias; } Roster.Item roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, jid); if (roster_item != null && roster_item.name != null && roster_item.name != "") { return roster_item.name; } return null; } public static string get_groupchat_display_name(StreamInteractor stream_interactor, Account account, Jid jid) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); string? room_name = muc_manager.get_room_name(account, jid); if (room_name != null && room_name != jid.localpart) { return room_name; } if (muc_manager.is_private_room(account, jid)) { Gee.List? other_occupants = muc_manager.get_other_offline_members(jid, account); if (other_occupants != null && other_occupants.size > 0) { var builder = new StringBuilder (); foreach(Jid occupant in other_occupants) { if (builder.len != 0) { builder.append(", "); } builder.append((get_real_display_name(stream_interactor, account, occupant) ?? occupant.localpart ?? occupant.domainpart).split(" ")[0]); } return builder.str; } } return jid.to_string(); } public static string get_occupant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid jid, string? self_word = null, bool muc_real_name = false) { if (muc_real_name) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); if (muc_manager.is_private_room(conversation.account, conversation.counterpart)) { Jid? real_jid = null; if (jid.equals_bare(conversation.counterpart)) { muc_manager.get_real_jid(jid, conversation.account); } else { real_jid = jid; } if (real_jid != null) { string? display_name = get_real_display_name(stream_interactor, conversation.account, real_jid, self_word); if (display_name != null) return display_name; } } } // If it's us (jid=our real full JID), display our nick if (conversation.type_ == Conversation.Type.GROUPCHAT_PM && conversation.account.bare_jid.equals_bare(jid)) { var muc_conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(conversation.counterpart.bare_jid, conversation.account, Conversation.Type.GROUPCHAT); if (muc_conv != null && muc_conv.nickname != null) { return muc_conv.nickname; } } // If it's someone else's real jid, recover nickname if (!jid.equals_bare(conversation.counterpart)) { MucManager muc_manager = stream_interactor.get_module(MucManager.IDENTITY); Jid? occupant_jid = muc_manager.get_occupant_jid(conversation.account, conversation.counterpart.bare_jid, jid); if (occupant_jid != null && occupant_jid.resourcepart != null) { return occupant_jid.resourcepart; } } return jid.resourcepart ?? jid.to_string(); } }dino-0.5.0/libdino/src/util/limit_input_stream.vala0000664000000000000000000000614614776241610021125 0ustar rootrootpublic class Dino.LimitInputStream : InputStream, PollableInputStream { private InputStream inner; public int64 max_bytes { public get; private set; } public int64 retrieved_bytes { public get; private set; } public int64 remaining_bytes { get { return max_bytes < 0 ? -1 : max_bytes - retrieved_bytes; }} public LimitInputStream(InputStream inner, int64 max_bytes) { this.inner = inner; this.max_bytes = max_bytes; } public bool can_poll() { return inner is PollableInputStream && ((PollableInputStream)inner).can_poll(); } public PollableSource create_source(Cancellable? cancellable = null) { if (!can_poll()) throw new IOError.NOT_SUPPORTED("Stream is not pollable"); return ((PollableInputStream)inner).create_source(cancellable); } public bool is_readable() { if (!can_poll()) throw new IOError.NOT_SUPPORTED("Stream is not pollable"); return remaining_bytes == 0 || ((PollableInputStream)inner).is_readable(); } private ssize_t check_limit(ssize_t read) throws IOError { if (remaining_bytes - (int64) read < 0) throw new IOError.FAILED("Stream length exceeded limit"); this.retrieved_bytes += read; return read; } public override ssize_t read(uint8[] buffer, Cancellable? cancellable = null) throws IOError { if (remaining_bytes == 0) return 0; int original_buffer_length = buffer.length; if (remaining_bytes != -1 && (int64) buffer.length > remaining_bytes) { // Never read more than remaining_bytes by limiting the buffer length buffer.length = (int) remaining_bytes; } ssize_t read_bytes = inner.read(buffer, cancellable); this.retrieved_bytes += read_bytes; buffer.length = original_buffer_length; return read_bytes; } public override async ssize_t read_async(uint8[]? buffer, int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { if (remaining_bytes == 0) return 0; int original_buffer_length = buffer.length; if (remaining_bytes != -1 && (int64) buffer.length > remaining_bytes) { // Never read more than remaining_bytes by limiting the buffer length buffer.length = (int) remaining_bytes; } ssize_t read_bytes = yield inner.read_async(buffer, io_priority, cancellable); this.retrieved_bytes += read_bytes; buffer.length = original_buffer_length; return read_bytes; } public ssize_t read_nonblocking_fn(uint8[] buffer) throws Error { if (!is_readable()) throw new IOError.WOULD_BLOCK("Stream is not readable"); return read(buffer); } public override bool close(Cancellable? cancellable = null) throws IOError { return inner.close(cancellable); } public override async bool close_async(int io_priority = GLib.Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { return yield inner.close_async(io_priority, cancellable); } }dino-0.5.0/libdino/src/util/send_message.vala0000664000000000000000000000501414776241610017643 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino { public void send_message(Conversation conversation, string text, int reply_to_id, Message? correction_to, Gee.List markups) { StreamInteractor stream_interactor = Application.get_default().stream_interactor; Message out_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(text, conversation); if (correction_to != null) { string correction_to_stanza_id = correction_to.edit_to ?? correction_to.stanza_id; out_message.edit_to = correction_to_stanza_id; stream_interactor.get_module(MessageCorrection.IDENTITY).set_correction(conversation, out_message, correction_to); } if (reply_to_id != 0) { ContentItem reply_to = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, reply_to_id); out_message.set_quoted_item(reply_to.id); // Store body with fallback string fallback = FallbackBody.get_quoted_fallback_body(reply_to); out_message.body = fallback + out_message.body; // Store fallback location var fallback_location = new Xep.FallbackIndication.FallbackLocation(0, (int)fallback.char_count()); var fallback_list = new ArrayList(); fallback_list.add(new Xep.FallbackIndication.Fallback(Xep.Replies.NS_URI, new Xep.FallbackIndication.FallbackLocation[] { fallback_location })); out_message.set_fallbacks(fallback_list); // Adjust markups to new prefix foreach (var span in markups) { span.start_char += fallback.length; span.end_char += fallback.length; } } if (!markups.is_empty) { out_message.persist_markups(markups, out_message.id); } if (correction_to != null) { stream_interactor.get_module(MessageCorrection.IDENTITY).on_received_correction(conversation, out_message.id); stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(out_message, conversation); return; } stream_interactor.get_module(ContentItemStore.IDENTITY).insert_message(out_message, conversation); stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(out_message, conversation); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent(out_message, conversation); } }dino-0.5.0/libdino/src/util/util.vala0000664000000000000000000001363714776241610016175 0ustar rootrootusing Gee; using Xmpp; namespace Dino { private extern const string SYSTEM_LIBDIR_NAME; private extern const string SYSTEM_PLUGIN_DIR; public class SearchPathGenerator { public string? exec_path { get; private set; } public SearchPathGenerator(string? exec_path) { this.exec_path = exec_path; } public string get_locale_path(string gettext_package, string locale_install_dir) { string? locale_dir = null; string dirname = Path.get_dirname(exec_path); // Does our environment look like a CMake build dir? if (dirname.contains("dino") || dirname == "." || dirname.contains("build")) { string exec_locale = Path.build_filename(dirname, "locale"); if (FileUtils.test(Path.build_filename(exec_locale, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) { locale_dir = exec_locale; } } // Does our environment look like a meson build dir? if (dirname == "." || Path.get_basename(dirname) == "main") { if (gettext_package == "dino") { string exec_locale = Path.build_filename(dirname, "po"); if (FileUtils.test(Path.build_filename(exec_locale, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) { locale_dir = exec_locale; } } else if (gettext_package.has_prefix("dino-")) { // This is a plugin, so fetch from plugin subdir string exec_locale = Path.build_filename(dirname, "..", "plugins", gettext_package.substring(5), "po"); if (FileUtils.test(Path.build_filename(exec_locale, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) { locale_dir = exec_locale; } } } return locale_dir ?? locale_install_dir; } public string[] get_plugin_paths() { string[] search_paths = new string[0]; if (Environment.get_variable("DINO_PLUGIN_DIR") != null) { search_paths += Environment.get_variable("DINO_PLUGIN_DIR"); } search_paths += Path.build_filename(Environment.get_home_dir(), ".local", "lib", "dino", "plugins"); string? exec_path = this.exec_path; if (exec_path != null) { if (!exec_path.contains(Path.DIR_SEPARATOR_S)) { exec_path = Environment.find_program_in_path(this.exec_path); } string dirname = Path.get_dirname(exec_path); // Does our environment look like a CMake build dir? if (dirname.contains("dino") || dirname == "." || dirname.contains("build") || Path.get_basename(dirname) == "main") { search_paths += Path.build_filename(Path.get_dirname(exec_path), "plugins"); } // Does our environment look like a meson build dir? if (dirname == "." || Path.get_basename(dirname) == "main") { try { Dir plugin_dir = Dir.open(Path.build_path(Path.DIR_SEPARATOR_S, dirname, "..", "plugins")); string? entry = null; while ((entry = plugin_dir.read_name()) != null) { string plugin_subdir = Path.build_path(Path.DIR_SEPARATOR_S, dirname, "..", "plugins", entry); try { Dir.open(plugin_subdir); search_paths += plugin_subdir; } catch (FileError e) { // ignore } } } catch (FileError e) { // ignore } } if (Path.get_basename(dirname) == "bin") { search_paths += Path.build_filename(Path.get_dirname(Path.get_dirname(exec_path)), SYSTEM_LIBDIR_NAME, "dino", "plugins"); } } search_paths += SYSTEM_PLUGIN_DIR; return search_paths; } } public static string get_storage_dir() { return Path.build_filename(Environment.get_user_data_dir(), "dino"); } public static string get_cache_dir() { return Path.build_filename(Environment.get_user_cache_dir(), "dino"); } [CCode (cname = "dino_gettext", cheader_filename = "dino_i18n.h")] public static extern unowned string _(string s); [CCode (cname = "dino_ngettext", cheader_filename = "dino_i18n.h")] public static extern unowned string n(string msgid, string plural, ulong number); [CCode (cname = "bindtextdomain", cheader_filename = "libintl.h")] private static extern unowned string? bindtextdomain(string domainname, string? dirname); [CCode (cname = "bind_textdomain_codeset", cheader_filename = "libintl.h")] private static extern unowned string? bind_textdomain_codeset(string domainname, string? codeset); public static void internationalize(string gettext_package, string locales_dir) { Intl.bind_textdomain_codeset(gettext_package, "UTF-8"); Intl.bindtextdomain(gettext_package, locales_dir); } public static async HashMap compute_file_hashes(File file, Gee.List checksum_types) { var checksums = new Checksum[checksum_types.size]; for (int i = 0; i < checksum_types.size; i++) { checksums[i] = new Checksum(checksum_types.get(i)); } FileInputStream stream = yield file.read_async(); uint8 fbuf[1024]; size_t size; while ((size = yield stream.read_async(fbuf)) > 0) { for (int i = 0; i < checksum_types.size; i++) { checksums[i].update(fbuf, size); } } var ret = new HashMap(); for (int i = 0; i < checksum_types.size; i++) { var checksum_type = checksum_types.get(i); uint8[] digest = new uint8[64]; size_t length = digest.length; checksums[i].get_digest(digest, ref length); string computed_hash = GLib.Base64.encode(digest[0:length]); ret[checksum_type] = computed_hash; } return ret; } } dino-0.5.0/libdino/src/util/weak_map.vala0000664000000000000000000000743514776241610017003 0ustar rootrootusing Gee; public class WeakMap : Gee.AbstractMap { private HashMap hash_map; private HashMap notify_map; public HashDataFunc? key_hash_func = null; public EqualDataFunc? key_equal_func = null; public EqualDataFunc? value_equal_func = null; public WeakMap(owned HashDataFunc? key_hash_func = null, owned EqualDataFunc? key_equal_func = null, owned EqualDataFunc? value_equal_func = null) { if (!typeof(V).is_object()) { error("WeakMap only takes values that are Objects"); } this.key_hash_func = (owned)key_hash_func; this.key_equal_func = (owned)key_equal_func; this.value_equal_func = (owned)value_equal_func; if (this.key_equal_func == null || this.key_equal_func == null || this.value_equal_func == null) { hash_map = new HashMap(); notify_map = new HashMap(); } else { hash_map = new HashMap((v) => { return this.key_hash_func != null ? this.key_hash_func(v) : 0; }, (a, b) => { return this.key_equal_func != null ? this.key_equal_func(a, b) : a == b; }, (a, b) => { return this.value_equal_func != null ? this.value_equal_func(a, b) : a == b; }); notify_map = new HashMap((v) => { return this.key_hash_func != null ? this.key_hash_func(v) : 0; }, (a, b) => { return this.key_equal_func != null ? this.key_equal_func(a, b) : a == b; }); } } public override void clear() { foreach (K key in notify_map.keys) { Object o = (Object) hash_map[key]; o.weak_unref(notify_map[key].func); } hash_map.clear(); notify_map.clear(); } public override V @get(K key) { if (!hash_map.has_key(key)) return null; var v = hash_map[key]; return (owned) v; } public override bool has(K key, V value) { return has_key(key) && (this.value_equal_func != null ? this.value_equal_func(hash_map[key], value) : hash_map[key] == value); } public override bool has_key(K key) { return hash_map.has_key(key); } public override Gee.MapIterator map_iterator() { assert_not_reached(); } public override void @set(K key, V value) { assert(value != null); unset(key); Object v_obj = (Object) value; var notify_wrap = new WeakNotifyWrapper((obj) => { hash_map.unset(key); notify_map.unset(key); }); notify_map[key] = notify_wrap; v_obj.weak_ref(notify_wrap.func); hash_map[key] = value; } public override bool unset(K key, out V value = null) { if (!hash_map.has_key(key)) { value = null; return false; } Object v_obj = (Object) hash_map[key]; v_obj.weak_unref(notify_map[key].func); notify_map.unset(key); return hash_map.unset(key, out value); } public override Gee.Set> entries { owned get; } public override Gee.Set keys { owned get { return hash_map.keys; } } public override bool read_only { get { assert_not_reached(); } } public override int size { get { return hash_map.size; } } public override Gee.Collection values { owned get { assert_not_reached(); } } public override void dispose() { foreach (K key in notify_map.keys) { Object o = (Object) hash_map[key]; o.weak_unref(notify_map[key].func); } } } internal class WeakNotifyWrapper { public WeakNotify func; public WeakNotifyWrapper(owned WeakNotify func) { this.func = (owned) func; } }dino-0.5.0/libdino/src/util/weak_timeout.vala0000664000000000000000000000332714776241610017710 0ustar rootrootpublic class Dino.WeakTimeout { // XXX: If you get an error saying your function doesn't match the delegate, make sure it's static! // These are marked as "has_target=false" so you can't close over "this" and leak it in your lambda. [CCode (has_target = false, instance_pos = 0)] public delegate bool SourceFunc (T object); [CCode (has_target = false, instance_pos = 0)] public delegate void SourceOnceFunc (T object); public static uint add(uint interval, T object, owned SourceFunc function, int priority = GLib.Priority.DEFAULT) { var weak = WeakRef((Object)object); return GLib.Timeout.add(interval, () => { var strong = weak.get(); if (strong == null) return false; return function(strong); }, priority); } public static uint add_once(uint interval, T object, owned SourceOnceFunc function, int priority = GLib.Priority.DEFAULT) { var weak = WeakRef((Object)object); return GLib.Timeout.add(interval, () => { var strong = weak.get(); if (strong == null) return false; function(strong); return false; }, priority); } public static uint add_seconds(uint interval, T object, owned SourceFunc function, int priority = GLib.Priority.DEFAULT) { return add(interval * 1000, object, (owned) function, priority); } // This one doesn't have an upstream equivalent, but it seems pretty obvious to me public static uint add_seconds_once(uint interval, T object, owned SourceOnceFunc function, int priority = GLib.Priority.DEFAULT) { return add_once(interval * 1000, object, (owned) function, priority); } } dino-0.5.0/libdino/src/version.vala.in0000664000000000000000000000010014776241610016312 0ustar rootrootnamespace Dino { public const string VERSION = "%VERSION%"; } dino-0.5.0/libdino/tests/0000775000000000000000000000000014776241610013737 5ustar rootrootdino-0.5.0/libdino/tests/common.vala0000664000000000000000000000373714776241610016106 0ustar rootrootnamespace Dino.Test { int main(string[] args) { GLib.Test.init(ref args); GLib.Test.set_nonfatal_assertions(); TestSuite.get_root().add_suite(new WeakMapTest().get_suite()); return GLib.Test.run(); } bool fail_if(bool exp, string? reason = null) { if (exp) { if (reason != null) GLib.Test.message(reason); GLib.Test.fail(); return true; } return false; } void fail_if_reached(string? reason = null) { fail_if(true, reason); } delegate void ErrorFunc() throws Error; void fail_if_not_error_code(ErrorFunc func, int expectedCode, string? reason = null) { try { func(); fail_if_reached(@"$(reason + ": " ?? "")no error thrown"); } catch (Error e) { fail_if_not_eq_int(e.code, expectedCode, @"$(reason + ": " ?? "")catched unexpected error"); } } bool fail_if_not(bool exp, string? reason = null) { return fail_if(!exp, reason); } bool fail_if_eq_int(int left, int right, string? reason = null) { return fail_if(left == right, @"$(reason + ": " ?? "")$left == $right"); } bool fail_if_not_eq_int(int left, int right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_str(string left, string right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_uint8_arr(uint8[] left, uint8[] right, string? reason = null) { if (fail_if_not_eq_int(left.length, right.length, @"$(reason + ": " ?? "")array length not equal")) return true; return fail_if_not_eq_str(Base64.encode(left), Base64.encode(right), reason); } bool fail_if_not_zero_int(int zero, string? reason = null) { return fail_if_not_eq_int(zero, 0, reason); } bool fail_if_zero_int(int zero, string? reason = null) { return fail_if_eq_int(zero, 0, reason); } bool fail_if_null(void* what, string? reason = null) { return fail_if(what == null || (size_t)what == 0, reason); } } dino-0.5.0/libdino/tests/jid.vala0000664000000000000000000000221514776241610015352 0ustar rootrootusing Dino.Entities; namespace Dino.Test { class JidTest : Gee.TestCase { public JidTest() { base("Jid"); add_test("parse", test_parse); add_test("components", test_components); add_test("with_res", test_with_res); } private void test_parse() { Jid jid = new Jid("user@example.com/res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } private void test_components() { Jid jid = new Jid.components("user", "example.com", "res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } private void test_with_res() { Jid jid = new Jid.with_resource("user@example.com", "res"); fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res"); } } } dino-0.5.0/libdino/tests/testcase.vala0000664000000000000000000000451314776241610016422 0ustar rootroot/* testcase.vala * * Copyright (C) 2009 Julien Peeters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Julien Peeters */ public abstract class Gee.TestCase : Object { private GLib.TestSuite suite; private Adaptor[] adaptors = new Adaptor[0]; public delegate void TestMethod (); protected TestCase (string name) { this.suite = new GLib.TestSuite (name); } public void add_test (string name, owned TestMethod test) { var adaptor = new Adaptor (name, (owned)test, this); this.adaptors += adaptor; this.suite.add (new GLib.TestCase (adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down )); } public virtual void set_up () { } public virtual void tear_down () { } public GLib.TestSuite get_suite () { return (owned) this.suite; } private class Adaptor { [CCode (notify = false)] public string name { get; private set; } private TestMethod test; private TestCase test_case; public Adaptor (string name, owned TestMethod test, TestCase test_case) { this.name = name; this.test = (owned)test; this.test_case = test_case; } public void set_up (void* fixture) { this.test_case.set_up (); } public void run (void* fixture) { this.test (); } public void tear_down (void* fixture) { this.test_case.tear_down (); } } } dino-0.5.0/libdino/tests/weak_map.vala0000664000000000000000000000475614776241610016404 0ustar rootrootusing Dino.Entities; namespace Dino.Test { class WeakMapTest : Gee.TestCase { public WeakMapTest() { base("WeakMapTest"); add_test("set", test_set); add_test("set2", test_set2); add_test("set3", test_set3); add_test("set4", test_unset); add_test("remove_when_out_of_scope", test_remove_when_out_of_scope); // add_test("non_object_construction", test_non_object_construction); } private void test_set() { WeakMap map = new WeakMap(); var o = new Object(); map[1] = o; assert(map.size == 1); assert(map.has_key(1)); assert(map[1] == o); } private void test_set2() { WeakMap map = new WeakMap(); var o = new Object(); var o2 = new Object(); map[1] = o; map[1] = o2; assert(map.size == 1); assert(map.has_key(1)); assert(map[1] == o2); } private void test_set3() { WeakMap map = new WeakMap(); var o1 = new Object(); var o2 = new Object(); map[0] = o1; map[3] = o2; { var o3 = new Object(); var o4 = new Object(); map[7] = o3; map[50] = o4; } var o5 = new Object(); map[5] = o5; assert(map.size == 3); assert(map.has_key(0)); assert(map.has_key(3)); assert(map.has_key(5)); assert(map[0] == o1); assert(map[3] == o2); assert(map[5] == o5); } private void test_unset() { WeakMap map = new WeakMap(); var o1 = new Object(); map[7] = o1; map.unset(7); assert_true(map.size == 0); assert_true(map.is_empty); assert_false(map.has_key(7)); } private void test_remove_when_out_of_scope() { WeakMap map = new WeakMap(); { map[0] = new Object(); } assert_false(map.has_key(0)); } private void test_non_object_construction() { WeakMap map = new WeakMap(); assert_not_reached(); } } } dino-0.5.0/libdino/version.py0000664000000000000000000000473514776241610014645 0ustar rootrootimport argparse import subprocess import re def compute_version_from_file(file): try: with open(file) as f: version_from_file = f.read().strip() if version_from_file != "": if version_from_file.startswith("RELEASE "): return version_from_file[8:].strip() if version_from_file.startswith("PRERELEASE "): return version_from_file[11:].strip() return version_from_file except FileNotFoundError: pass return None def compute_version_from_git(git_repo, git): try: git_release_tag = subprocess.check_output([git, "describe", "--tags", "--abbrev=0"], cwd=git_repo, text=True).strip() if re.match("^v?([0-9]+[.]?[0-9]*[.]?[0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$", git_release_tag) is None: return None git_describe = subprocess.check_output([git, "describe", "--tags"], cwd=git_repo, text=True).strip() if git_release_tag == git_describe: return git_release_tag matches = re.match("^.*-([0-9]+)-g([0-9a-f]+)$", git_describe) if matches is None: return None git_tag_offset = matches.groups()[0] git_commit_hash = matches.groups()[1] git_commit_time = subprocess.check_output([git, "show", "--format=%cd", "--date=format:%Y%m%d", "-s"], cwd=git_repo, text=True).strip() return "%s~git%s.%s.%s" % (git_release_tag, git_tag_offset, git_commit_time, git_commit_hash) except subprocess.CalledProcessError: pass return None def compute_version(file, git_repo, git): version_from_file = compute_version_from_file(file) if version_from_file is not None: return version_from_file version_from_git = compute_version_from_git(git_repo, git) if version_from_git is not None: return version_from_git return "" def main(): p = argparse.ArgumentParser(description="Compute the Dino version") p.add_argument("--git-repo", help="Path to checked out git repository") p.add_argument("--git", help="Path to git executable", default="git") p.add_argument("version_file", metavar="VERSION_FILE", help="Use this file's contents as version if the file exists") args = p.parse_args() version = compute_version(args.version_file, args.git_repo, args.git) print(version) if __name__ == "__main__": main() dino-0.5.0/main/0000775000000000000000000000000014776241610012101 5ustar rootrootdino-0.5.0/main/data/0000775000000000000000000000000014776241610013012 5ustar rootrootdino-0.5.0/main/data/account_picker_row.ui0000664000000000000000000000210414776241610017226 0ustar rootroot dino-0.5.0/main/data/add_conversation/0000775000000000000000000000000014776241610016334 5ustar rootrootdino-0.5.0/main/data/add_conversation/add_contact_dialog.ui0000664000000000000000000001264214776241610022462 0ustar rootroot dino-0.5.0/main/data/add_conversation/add_groupchat_dialog.ui0000664000000000000000000002200614776241610023016 0ustar rootroot dino-0.5.0/main/data/add_conversation/conference_details_fragment.ui0000664000000000000000000003502314776241610024375 0ustar rootroot dino-0.5.0/main/data/add_conversation/list_row.ui0000664000000000000000000000425414776241610020542 0ustar rootroot horizontal 8 6 6 6 6 30 30 center vertical center horizontal 6 end 0 8 end 0 dino-0.5.0/main/data/add_conversation/select_jid_fragment.ui0000664000000000000000000001130614776241610022664 0ustar rootroot dino-0.5.0/main/data/call_widget.ui0000664000000000000000000001504714776241610015636 0ustar rootroot dino-0.5.0/main/data/chat_input.ui0000664000000000000000000001161014776241610015506 0ustar rootroot dino-0.5.0/main/data/conversation_content_view/0000775000000000000000000000000014776241610020310 5ustar rootrootdino-0.5.0/main/data/conversation_content_view/item_metadata_header.ui0000664000000000000000000000211214776241610024751 0ustar rootroot dino-0.5.0/main/data/conversation_content_view/view.ui0000664000000000000000000000727414776241610021633 0ustar rootroot dino-0.5.0/main/data/conversation_details.css0000664000000000000000000000120014776241610017734 0ustar rootroot.extended-headerbar-end { padding-bottom: 24px; border-bottom: 1px solid @borders; } .extended-headerbar { background-color: @headerbar_bg_color; padding-bottom: 0; } .dino-underlined-tabs .toggle { background: none; padding: 0; margin: 0 12px 0 0; border-left: none; min-width: 0; } .dino-underlined-tabs .toggle label { border-bottom: 2px solid transparent; margin: 0; padding: 6px; } .dino-underlined-tabs .toggle:checked label { border-bottom-color: @accent_color; } .dino-underlined-tabs .toggle:hover:not(:checked) label { border-bottom-color: alpha(@accent_color, 0.25); }dino-0.5.0/main/data/conversation_details.ui0000664000000000000000000002446614776241610017604 0ustar rootroot
Enable notifications notification.on Disable notifications notification.off
Reset to default notification.default
Notify for all messages notification.on Notify only for mentions notification.highlight Disable notifications notification.off
Reset to default notification.default
dino-0.5.0/main/data/conversation_item_widget.ui0000664000000000000000000000470714776241610020454 0ustar rootroot 7 2 35 35 start 2 0 0 2 end 0 baseline 1 0 0 baseline 2 0 0.4 12 2 3 0 0.4 13 2 start True 4 0 dino-0.5.0/main/data/conversation_list_titlebar.ui0000664000000000000000000000232614776241610021007 0ustar rootroot False False list-add-symbolic normal False open-menu-symbolic normal dino-0.5.0/main/data/conversation_row.ui0000664000000000000000000001707314776241610016762 0ustar rootroot dino-0.5.0/main/data/conversation_view.ui0000664000000000000000000001006214776241610017114 0ustar rootroot dino-0.5.0/main/data/dino-conversation-list-placeholder-arrow.svg0000664000000000000000000000124114776241610023553 0ustar rootroot dino-0.5.0/main/data/file_default_widget.ui0000664000000000000000000000721614776241610017345 0ustar rootroot dino-0.5.0/main/data/file_send_overlay.ui0000664000000000000000000000642714776241610017053 0ustar rootroot 0 0 center center vertical 20 20 10 10 Send a file 1 1 window-close-symbolic 15 15 5 Send 1 dino-0.5.0/main/data/global_search.ui0000664000000000000000000001366614776241610016152 0ustar rootroot vertical 12 12 12 12 empty system-search-symbolic No active search Type to start a search no-result face-uncertain-symbolic No matching messages Check the spelling or try to remove filters results vertical 0 1 17 never 1 1 vertical 25 10 10 10 10 start 42 12 12 start browse dino-0.5.0/main/data/gresource.xml0000664000000000000000000001020214776241610015525 0ustar rootroot account_picker_row.ui add_conversation/add_contact_dialog.ui add_conversation/add_groupchat_dialog.ui add_conversation/conference_details_fragment.ui add_conversation/list_row.ui add_conversation/select_jid_fragment.ui call_widget.ui chat_input.ui conversation_content_view/item_metadata_header.ui conversation_content_view/view.ui conversation_details.css conversation_details.ui conversation_item_widget.ui conversation_list_titlebar.ui conversation_row.ui conversation_view.ui dino-conversation-list-placeholder-arrow.svg file_default_widget.ui file_send_overlay.ui global_search.ui gtk/help-overlay.ui icons/scalable/actions/dino-emoticon-add-symbolic.svg icons/scalable/actions/dino-qr-code-symbolic.svg icons/scalable/actions/small-x-symbolic.svg icons/scalable/apps/im.dino.Dino-symbolic.svg icons/scalable/apps/im.dino.Dino.svg icons/scalable/devices/dino-device-desktop-symbolic.svg icons/scalable/devices/dino-device-phone-symbolic.svg icons/scalable/devices/dino-phone-hangup-symbolic.svg icons/scalable/devices/dino-phone-in-talk-symbolic.svg icons/scalable/devices/dino-phone-missed-symbolic.svg icons/scalable/devices/dino-phone-ring-symbolic.svg icons/scalable/devices/dino-phone-symbolic.svg icons/scalable/mimetypes/dino-file-document-symbolic.svg icons/scalable/mimetypes/dino-file-download-symbolic.svg icons/scalable/mimetypes/dino-file-image-symbolic.svg icons/scalable/mimetypes/dino-file-music-symbolic.svg icons/scalable/mimetypes/dino-file-symbolic.svg icons/scalable/mimetypes/dino-file-table-symbolic.svg icons/scalable/mimetypes/dino-file-video-symbolic.svg icons/scalable/status/dino-double-tick-symbolic.svg icons/scalable/status/dino-bell-large-none-symbolic.svg icons/scalable/status/dino-bell-large-symbolic.svg icons/scalable/status/dino-block-symbolic.svg icons/scalable/status/dino-party-popper-symbolic.svg icons/scalable/status/dino-security-high-symbolic.svg icons/scalable/status/dino-status-away.svg icons/scalable/status/dino-status-chat.svg icons/scalable/status/dino-status-dnd.svg icons/scalable/status/dino-status-online.svg icons/scalable/status/dino-status-offline.svg icons/scalable/status/dino-tick-symbolic.svg icons/scalable/status/dino-video-off-symbolic.svg icons/scalable/status/dino-video-symbolic.svg preferences_window/account_preferences_subpage.ui preferences_window/add_account_dialog.ui preferences_window/change_password_dialog.ui preferences_window/general_preferences_page.ui preferences_window/preferences_window.ui join_room_dialog.ui join_room_dialog1.ui join_room_dialog2.ui menu_add.ui menu_app.ui menu_conversation.ui menu_encryption.ui message_item_widget_edit_mode.ui muc_member_list_row.ui occupant_list.ui occupant_list_item.ui quote.ui search_autocomplete.ui style-dark.css style.css unified_main_content.ui unified_window_placeholder.ui dino-0.5.0/main/data/gtk/0000775000000000000000000000000014776241610013577 5ustar rootrootdino-0.5.0/main/data/gtk/help-overlay.ui0000664000000000000000000000751714776241610016557 0ustar rootroot True shortcuts General <ctrl>T Start Conversation <ctrl>G Join Channel <ctrl>comma Show preferences <ctrl>question Keyboard shortcuts Conversation <ctrl>F Search messages <ctrl>U Send a file Navigation <ctrl>Tab Jump to next conversation <ctrl><Shift>Tab Jump to previous conversation dino-0.5.0/main/data/icons/0000775000000000000000000000000014776241610014125 5ustar rootrootdino-0.5.0/main/data/icons/scalable/0000775000000000000000000000000014776241610015673 5ustar rootrootdino-0.5.0/main/data/icons/scalable/actions/0000775000000000000000000000000014776241610017333 5ustar rootrootdino-0.5.0/main/data/icons/scalable/actions/dino-emoticon-add-symbolic.svg0000664000000000000000000000205614776241610025170 0ustar rootroot dino-0.5.0/main/data/icons/scalable/actions/dino-qr-code-symbolic.svg0000664000000000000000000000107014776241610024152 0ustar rootroot dino-0.5.0/main/data/icons/scalable/actions/small-x-symbolic.svg0000664000000000000000000000137114776241610023252 0ustar rootroot dino-0.5.0/main/data/icons/scalable/apps/0000775000000000000000000000000014776241610016636 5ustar rootrootdino-0.5.0/main/data/icons/scalable/apps/im.dino.Dino-symbolic.svg0000664000000000000000000000410414776241610023422 0ustar rootroot dino-0.5.0/main/data/icons/scalable/apps/im.dino.Dino.svg0000664000000000000000000001630614776241610021612 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/0000775000000000000000000000000014776241610017315 5ustar rootrootdino-0.5.0/main/data/icons/scalable/devices/dino-device-desktop-symbolic.svg0000664000000000000000000000053214776241610025512 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-device-phone-symbolic.svg0000664000000000000000000000075214776241610025156 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-phone-hangup-symbolic.svg0000664000000000000000000000333214776241610025176 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-phone-in-talk-symbolic.svg0000664000000000000000000000127714776241610025261 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-phone-missed-symbolic.svg0000664000000000000000000000310714776241610025200 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-phone-ring-symbolic.svg0000664000000000000000000000275614776241610024664 0ustar rootroot dino-0.5.0/main/data/icons/scalable/devices/dino-phone-symbolic.svg0000664000000000000000000000176714776241610023730 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/0000775000000000000000000000000014776241610017707 5ustar rootrootdino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-document-symbolic.svg0000664000000000000000000000073314776241610025734 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-download-symbolic.svg0000664000000000000000000000071114776241610025721 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-image-symbolic.svg0000664000000000000000000000124314776241610025175 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-music-symbolic.svg0000664000000000000000000000211714776241610025234 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-symbolic.svg0000664000000000000000000000064114776241610024116 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-table-symbolic.svg0000664000000000000000000000112714776241610025203 0ustar rootroot dino-0.5.0/main/data/icons/scalable/mimetypes/dino-file-video-symbolic.svg0000664000000000000000000000123514776241610025222 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/0000775000000000000000000000000014776241610017216 5ustar rootrootdino-0.5.0/main/data/icons/scalable/status/dino-bell-large-none-symbolic.svg0000664000000000000000000000175714776241610025462 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-bell-large-symbolic.svg0000664000000000000000000000133614776241610024516 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-block-symbolic.svg0000664000000000000000000000056114776241610023601 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-double-tick-symbolic.svg0000664000000000000000000000171714776241610024715 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-party-popper-symbolic.svg0000664000000000000000000001025714776241610025154 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-security-high-symbolic.svg0000664000000000000000000000246414776241610025277 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-status-away.svg0000664000000000000000000000066214776241610023154 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-status-chat.svg0000664000000000000000000000146714776241610023136 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-status-dnd.svg0000664000000000000000000000062114776241610022753 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-status-offline.svg0000664000000000000000000000131414776241610023630 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-status-online.svg0000664000000000000000000000045614776241610023500 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-tick-symbolic.svg0000664000000000000000000000120314776241610023433 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-video-off-symbolic.svg0000664000000000000000000000131014776241610024356 0ustar rootroot dino-0.5.0/main/data/icons/scalable/status/dino-video-symbolic.svg0000664000000000000000000000103314776241610023610 0ustar rootroot dino-0.5.0/main/data/im.dino.Dino.appdata.xml0000664000000000000000000007747014776241610017411 0ustar rootroot im.dino.Dino CC0-1.0 Dino Modern XMPP chat client 現代化的 XMPP 用戶端聊天軟件 现代 XMPP 聊天客户端 Сучасний кліент XMPP Modern XMPP Sohbet İstemcisi நவீன எக்ச்எம்பிபி அரட்டை வாங்கி Modern XMPP-chattklient Klient Modern Fjalosjesh XMPP Современный XMPP клиент Client XMPP de discuții modern Cliente de Chat XMPP Moderno Moderno cliente de chat XMPP Nowoczesny komunikator XMPP Client XMPP modèrn Een moderne XMPP-chatclient Moderne XMPP-sludreklient Šiuolaikinė XMPP pokalbių kliento programa Modernen XMPP Chat Client 현대 XMPP 채팅 클라이언트 現代的な XMPP チャット クライアント Client di chat moderno per XMPP Nútímalegt XMPP-spjallforrit Un modern client de conversationes XMPP Aplikasi chat XMPP modern Ժամանակակից XMPP կլիենտ Modern XMPP-csevegési ügyfélprogram आधुनिक XMPP चैट क्लाइंट Cliente moderno para conversas XMPP Client de discussion XMPP moderne Nykyaikainen XMPP-asiakasohjelma کلاینت نوین گپ XMPP XMPP txat bezero modernoa Moodne XMPP-põhine vestlusrakendus Un cliente de XMPP moderno Moderna XMPP-Retebabililo Σύγχρονος XMPP Chat Client Modernes XMPP-Chat-Programm Moderne XMPP-Chatklient Moderní XMPP klient Client de xat XMPP modern عميل دردشة ميفاق الحضور والمراسلة القابل للتوسعة (XMPP) عصري im.dino.Dino https://dino.im/img/appdata/icon-dino-0.4-128x128.png

Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.

Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。

Dino 是适用于桌面的现代开源聊天客户端,专注于提供简洁可靠的 Jabber/XMPP 体验,同时注重您的隐私。

Dino là một ứng dụng chat mã nguồn mở hiện đại cho máy tính. Nó tập trung vào việc cung cấp một trải nghiệm Jabber/XMPP gọn gàng và tin cậy trong khi vẫn quan tâm đến quyền riêng tư của bạn.

Dino - це сучасний клієнт чату з відкритим вихідним кодом для комп’ютера. Він зосереджений на забезпеченні чистого та надійного досвіду Jabber/XMPP, зважаючи на вашу конфіденційність.

Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır.

டினோ டெச்க்டாப்பிற்கான நவீன திறந்த மூல அரட்டை வாங்கி ஆகும். இது உங்கள் தனியுரிமையை மனதில் கொண்டிருக்கும்போது தூய்மையான மற்றும் நம்பகமான சாபர்/எக்ச்எம்பிபி அனுபவத்தை வழங்குவதில் கவனம் செலுத்துகிறது.

Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser efter din integritet.

Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj.

Dino — это современный клиент для чатов с открытым исходным кодом, направленный на надёжное и приватное использование Jabber/XMPP на персональных компьютерах.

Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, ținând cont de confidențialitatea dumneavoastră.

Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em oferecer uma experiência Jabber/XMPP transparente e confiável levando em consideração a sua privacidade.

Dino é um moderno chat de código aberto para desktop. Ele é focado em prover uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua privacidade.

Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej obsłudze sieci Jabber/XMPP dbając o twoją prywatność.

Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte vòstra confidencialitat.

Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het achterhoofd.

Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om personvernet.

Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą nepamirštant apie jūsų privatumą.

Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op Privatsphär.

Dino는 데스크탑을 위한 현대 오픈소스 채팅 클라이언트입니다. 깔끔하고 신뢰 할 수 있는 Jabber/XMPP 경험을 개인정보 보호 중시와 함께 제공할 수 있도록 주력하고 있습니다.

Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プライバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供を第一に考えて開発されています。

Dino è un client di chat per il desktop, moderno e open-source. Si concentra nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente la tua privacy.

Dino er nútímalegt spjallforrit og frjáls hugbúnaður fyrir skjáborðið. Það leggur áherslu á að veita hreina og áreiðanlega Jabber/XMPP upplifun með friðhelgi þína í huga.

Dino es un modern cliente de conversationes con fonte apert. It foca se sur provider un nett e fidibil experientie de Jabber/XMPP con un attention a confidentialitá.

Dino adalah aplikasi chat open source modern untuk PC. Menyediakan pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda.

Dino֊ն՝ ժամանակակից, բաց կոդով կլիենտ է։ Այն միտուած է պարզ եւ վստահելի Jabber/XMPP փորձառութիւն ապահովելուն, միաժամանակ մտքում ունենալով քո գաղտնիութիւնը։

A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, miközben a magánszféra megőrzését is fontosnak tartja.

डिनो डेस्कटॉप के लिए एक आधुनिक ओपन-सोर्स चैट क्लाइंट है। यह आपकी गोपनीयता को ध्यान में रखते हुए एक स्वच्छ और विश्वसनीय जैबर/एक्सएमपीपी अनुभव प्रदान करने पर केंद्रित है।

Dino é un cliente moderno e de código aberto para o escritorio. Orientado a fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e seguranza presentes.

Dino est un client de discussion libre et moderne pour ordinateur. Il se concentre sur le fait d’offrir une expérience XMPP simple et fiable tout en ayant toujours à l’esprit votre vie privée.

Dino on nykyaikainen avoimen lähdekoodin keskusteluohjelma työpöydälle. Se tarjoaa selkeän ja luotettavan Jabber/XMPP-kokemuksen huomioiden yksityisyytesi.

دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد.

Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan hartzeaz gain.

Dino on moodne avatud lähtekoodil põhinev vestlusrakendus töölauale. Rakenduse eesmärgiks on tagada selge ja usaldusväärne Jabber/XMPP võrgul põhinev suhtlusvõimalus, mis arvestab sinu privaatsusega.

Dino es un cliente de mensajería moderno y libre para escritorio y móvil. Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable teniendo la privacidad en mente.

Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian privatecon.

Το Dino είναι ένας σύγχρονος πελάτης συνομιλίας ανοιχτού κώδικα για desktop υπολογιστές. Επικεντρώνεται στην παροχή μιας καθαρής και αξιόπιστης εμπειρίας Jabber/XMPP έχοντας παράλληλα υπόψη την προστασία των προσωπικών δεδομένων σας.

Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen Schwerpunkt auf Privatsphäre.

Dino er en moderne open-source chatklient til skrivebordet. Den fokuserer på at give en ren og pålidelig Jabber/XMPP-oplevelse som samtidig har dit privatliv på sinde.

Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na zachování vašeho soukromí.

Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en compte la vostra privacitat.

إنّ Dino برنامج عصري ومفتوح المصدر للدردشة مصُمّم لسطح المكتب. ويُركّز علي تقديم تجربة نظيفة وموثوق منها لجابر/ميفاق الحضور والمراسلة القابل للتوسعة (XMPP) مع أخذ خصوصيتكم بعين الاعتبار.

It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications.

它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已讀回條和正在輸入提示。

它支持 OMEMO 和 OpenPGP 端到端加密并允许配置隐私相关的功能,如已读回执和输入通知。

Nó hỗ trợ mã hoá đầu cuối với OMEMO và OpenPGP, đồng thời cho phép cấu hình các tính năng liên quan đến quyền riêng tư như thông báo trạng thái tin nhắn và trạng thái đang nhập.

Він підтримує наскрізне шифрування за допомогою OMEMO та OpenPGP і дозволяє налаштовувати пов’язані з конфіденційністю функції, такі як сповіщення про прочитання та сповіщення про введення тексту.

OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve "okundu" bilgisi, "yazıyor..." bildirimi gibi gizlilikle alakalı özelliklerin ayarlanabilmesini sağlar.

இது OMEMO மற்றும் OpenPGP உடன் இறுதி முதல் இறுதி குறியாக்கத்தை ஆதரிக்கிறது மற்றும் தனியுரிமை தொடர்பான அம்சங்களான வாசிப்பு ரசீதுகள் மற்றும் தட்டச்சு அறிவிப்புகள் போன்றவற்றை உள்ளமைக்க அனுமதிக்கிறது.

Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter konfigurering av funktioner med integritetspåverkan som läsbekräftelser och skriftaviseringar.

Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime shtypjeje.

Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет настраивать такие функции, как уведомления о прочтении и наборе сообщений.

Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, și permite configurarea caracteristicilor legate de confidențialitate precum trimiterea notificărilor de primire și tastare.

Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, permite configurar funcionalidades relativas à privacidade como notificações de leitura, recebimento e escrita.

Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar privacidade—características relacionadas às notificações de leitura, recebimento e escrita.

Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o pisaniu czy odczytaniu wiadomości.

Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de configurar de foncionalitats ligadas amb la confidencialitat coma los acusats de lectura e las notificacions d’escritura.

Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-typenmeldingen, in te stellen.

Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og skrivevarsling.

Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie žinučių skaitymą ir rašymą.

Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen.

OMEMO와 OpenPGP를 통한 종단간 암호화를 지원하며 프라이버시와 관련된 읽음 확인이나 입력 알림기능을 설정할 수 있습니다.

OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送信や入力通知などのプライバシー関連の設定も可能です。

Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di configurare le funzioni relative alla privacy come le ricevute di lettura e le notifiche di digitazione.

Það styður dulkóðun frá enda til enda með OMEMO og OpenPGP og gerir kleift að stilla persónuverndartengda eiginleika eins og lestrarkvittanir og innsláttartilkynningar.

It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar sensitiv functiones quam confirmation de lectada e notificationes de tippada.

Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan pengaturan fitur terkait privasi seperti tanda pesan dibaca dan pemberitahuan pengetikan.

Սատարում է ծայրից ծայրի կրիպտաւորմանը OMEMO֊ի եւ OpenPGP֊ի միջոցով եւ թոյլ է տալիս կարգաւորել գաղտնիութեանը վերաբերող հնարաւորութիւններ, ինչպիսին են՝ ստացուած գրութիւնը ընթերնելու կամ տպելու մասին ծանուցելը։

Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint például az olvasási visszaigazolást és a gépelési értesítéseket.

यह OMEMO और OpenPGP के साथ शुरू-से-अंत कूटलेखन का समर्थन करता है और गोपनीयता-संबंधी सुविधाओं जैसे कि पठन रसीद और टाइपिंग अधिसूचनाओं को विन्यस्त करने की अनुमति देता है।

Suporta a cifraxe de punto-a-punto con OMEMO e OpenPGP e permite configurar funcións orientadas á privacidade tales coma confirmación de lectura e notificacións de escritura.

Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et permet de configurer les fonctions liées à la confidentialité telles que les accusés de réception et les notifications d’écriture.

Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja kirjoitusilmoitusten asetusten määrittämisen.

از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و اعلان در حال نوشتن بودن.

Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta idazketa jakinarazpenak bezala.

Rakendus toetab nii OMEMO, kui OpenPGP standardil põhinevat läbivat krüptimist ja võimaldab seadistada privaatsusega seotud funktsionaalsusi, nagu lugemis- ja kirjutamisteatised.

Soporta cifrado fin-a-fin a través de OMEMO y OpenPGP y permite configurar características relacionadas con la privacidad, como confirmaciones de lectura y notificaciones de escritura.

Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado.

Υποστηρίζει κρυπτογράφηση από άκρο σε άκρο με OMEMO και OpenPGP και επιτρέπει την ρύθμιση λειτουργιών που σχετίζονται με το απόρρητο, όπως αποδείξεις ανάγνωσης και ειδοποιήσεις πληκτρολόγησης.

Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und enthält Privatsphäre-Einstellungen zu Lesebestätigungen und Tippbenachrichtigungen.

Den understøtter end-to-end kryptering med OMEMO og OpenPGP og tillader at konfigurere privatlivs-relaterede features så som læsekvitteringer og besked om skrivning.

Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení a oznámení o psaní.

Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar funcionalitats relacionades amb la privacitat com per exemple rebuts de lectura i notificacions d'escriptura.

وهو يدعم التشفير بواسطة OMEMO وOpenPGP مما يسمح بإعداد مزايا الخصوصية كالإيصالات المقروءة والإخطارات عند الكتابة.

Dino fetches history from the server and synchronizes messages with other devices.

Dino 從伺服器取得訊息並與其他裝置同步。

Dino 从服务器获取历史记录并与其他设备同步消息。

Dino lấy lịch sử từ server và đồng bộ tin nhắn với các thiết bị khác.

Dino отримує історію з сервера та синхронізує повідомлення з іншими пристроями.

Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara senkronize eder.

டினோ சேவையகத்திலிருந்து வரலாற்றைப் பெறுகிறது மற்றும் பிற சாதனங்களுடன் செய்திகளை ஒத்திசைக்கிறது.

Dino hämtar historik från servern och synkroniserar meddelanden med andra enheter.

Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje të tjera.

Dino загружает историю с сервера и синхронизирует сообщения с другими устройствами.

Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu celelalte dispozitive.

Dino obtém o histórico do servidor e sincroniza mensagens com outros dispositivos.

Dino obtém o histórico do servidor e sincroniza mensagens com outros aparelhos.

Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi urządzeniami.

Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres periferics.

Dino haalt de geschiedenis op van de server en synchroniseert berichten met andere apparaten.

Dino henter historikk fra tjeneren og synkroniserer meldinger med andre enheter.

Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais įrenginiais.

Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat anere Geräter.

Dino는 서버와 타 장치와의 메세지 동기화로부터 기록을 불러옵니다.

Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。

Dino recupera la cronologia dal server e sincronizza i messaggi con gli altri dispositivi.

Dino sækir feril af netþjóni og samstillir skilaboð með öðrum tækjum.

Dino obtene li diarium del servitore e sincronisa missages inter altri apparates.

Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan perangkat lain.

Dino֊ն ունակ է ստանալ սպասարկչից գրութիւնների պատմութիւն եւ սինքրոնացնել այն այլ սարքերի հետ։

A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az üzeneteket a többi eszközzel.

डिनो सर्वर से इतिहास प्राप्त करता है और संदेशों को अन्य डिवाइसों के साथ समन्वयित करता है।

Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros dispositivos.

Dino récupère l’historique du serveur et synchronise les messages avec les autres clients.

Dino hakee historian palvelimelta ja synkronoi viestit muiden laitteiden kanssa.

دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی می‌کند.

Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak sinkronizatzen ditu.

Dino laadib vestluste ajaloo serverist ja sünkroniseerib sõnumeid kõikide sinu seadmete vahel.

Dino recupera el historial de mensajes desde el servidor y sincroniza los mensajes con otros dispositivos.

Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj aparatoj.

Το Dino ανακτά το ιστορικό από τον διακομιστή και συγχρονίζει τα μηνύματα με άλλες συσκευές.

Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit anderen Geräten.

Dino henter tidligere beskeder fra serveren og synkroniserer beskeder med andre enheder.

Dino načítá historii ze serveru a synchronizuje zprávy s ostatními zařízeními.

Dino recupera l'historial del servidor i sincronitza els missatges amb altres dispositius.

يجلب Dino السِجلّ مِن الخادوم، وثم يُزامِن الرسائل مع الأجهزة الأخرى.

Network InstantMessaging Chat GTK Jabber XMPP https://dino.im https://github.com/dino/dino/issues https://dino.im/#donate https://github.com/dino/dino https://hosted.weblate.org/projects/dino/ im.dino.Dino.desktop

The 0.5 release features improved file transfers and two completely reworked dialogs.

The 0.4 release adds support for message reactions and replies. Also, Dino is ported to GTK4.

The 0.3 release is all about calls. Dino now supports calls between two or more people!

The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.

The first Dino release! Dino is a secure and open-source application for decentralized messaging.

x-scheme-handler/xmpp always pointing keyboard touch small GPL-3.0+ Dino Development Team https://dino.im/ Dino Development Team https://dino.im/img/appdata/screenshot-dino-0.4-main-2244x1644@2.png Main screen https://dino.im/img/appdata/screenshot-dino-0.4-call-1441x929@2.png Video call screen https://dino.im/img/appdata/screenshot-dino-0.4-search-2244x1644@2.png Main screen featuring search overlay https://dino.im/img/appdata/screenshot-dino-0.4-mobile-964x1552@2.png Main screen in mobile size window dino intense intense appstream@dino.im #B2DFDB #004D40
dino-0.5.0/main/data/im.dino.Dino.appdata.xml.in0000664000000000000000000001107314776241610020001 0ustar rootroot im.dino.Dino CC0-1.0 Dino Modern XMPP chat client im.dino.Dino https://dino.im/img/appdata/icon-dino-0.4-128x128.png

Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.

It supports end-to-end encryption with OMEMO and OpenPGP and allows configuring privacy-related features such as read receipts and typing notifications.

Dino fetches history from the server and synchronizes messages with other devices.

Network InstantMessaging Chat GTK Jabber XMPP https://dino.im https://github.com/dino/dino/issues https://dino.im/#donate https://github.com/dino/dino https://hosted.weblate.org/projects/dino/ im.dino.Dino.desktop

The 0.5 release improves the user experience around file transfers and includes two completely reworked dialogs.

The 0.4 release adds support for message reactions and replies. Also, Dino is ported to GTK4.

The 0.3 release is all about calls. Dino now supports calls between two or more people!

The 0.2 release adds message correction, improves the file upload functionality and provides more information about message encryption.

The first Dino release! Dino is a secure and open-source application for decentralized messaging.

x-scheme-handler/xmpp always pointing keyboard touch small GPL-3.0+ Dino Development Team https://dino.im/ Dino Development Team https://dino.im/img/appdata/screenshot-dino-0.4-main-2244x1644@2.png Main screen https://dino.im/img/appdata/screenshot-dino-0.4-call-1441x929@2.png Video call screen https://dino.im/img/appdata/screenshot-dino-0.4-search-2244x1644@2.png Main screen featuring search overlay https://dino.im/img/appdata/screenshot-dino-0.4-mobile-964x1552@2.png Main screen in mobile size window dino intense intense appstream@dino.im #B2DFDB #004D40
dino-0.5.0/main/data/im.dino.Dino.desktop0000664000000000000000000000052714776241610016636 0ustar rootroot[Desktop Entry] Version=1.0 Name=Dino GenericName=Jabber/XMPP Client Keywords=chat;talk;im;message;xmpp;jabber; Exec=dino %U Icon=im.dino.Dino StartupNotify=true Terminal=false Type=Application Categories=GTK;Network;Chat;InstantMessaging; X-GNOME-UsesNotifications=true MimeType=x-scheme-handler/xmpp; X-Purism-FormFactor=Workstation;Mobile; dino-0.5.0/main/data/im.dino.Dino.service0000664000000000000000000000005414776241610016620 0ustar rootroot[D-BUS Service] Name=im.dino.Dino Exec=dino dino-0.5.0/main/data/join_room_dialog.ui0000664000000000000000000000306114776241610016663 0ustar rootroot 500 600 True False channel_selection model confirmation model dino-0.5.0/main/data/join_room_dialog1.ui0000664000000000000000000001725514776241610016756 0ustar rootroot dino-0.5.0/main/data/join_room_dialog2.ui0000664000000000000000000002472714776241610016761 0ustar rootroot dino-0.5.0/main/data/menu_add.ui0000664000000000000000000000102014776241610015116 0ustar rootroot
app.add_chat Start Conversation app.add_conference Join Channel
dino-0.5.0/main/data/menu_app.ui0000664000000000000000000000131514776241610015155 0ustar rootroot
app.preferences Preferences win.show-help-overlay Keyboard Shortcuts app.about About Dino
dino-0.5.0/main/data/menu_conversation.ui0000664000000000000000000000053214776241610017107 0ustar rootroot
app.contact_details Contact Details
dino-0.5.0/main/data/menu_encryption.ui0000664000000000000000000000171714776241610016575 0ustar rootroot vertical 10 10 10 10 Unencrypted 1 1 dino-0.5.0/main/data/message_item_widget_edit_mode.ui0000664000000000000000000000514514776241610021374 0ustar rootroot dino-0.5.0/main/data/muc_member_list_row.ui0000664000000000000000000000703014776241610017406 0ustar rootroot dino-0.5.0/main/data/occupant_list.ui0000664000000000000000000000226214776241610016222 0ustar rootroot dino-0.5.0/main/data/occupant_list_item.ui0000664000000000000000000000220214776241610017232 0ustar rootroot 3 7 3 7 10 30 30 1 end 1 0 1 0 dino-0.5.0/main/data/preferences_window/0000775000000000000000000000000014776241610016702 5ustar rootrootdino-0.5.0/main/data/preferences_window/account_preferences_subpage.ui0000664000000000000000000001535214776241610024772 0ustar rootroot dino-0.5.0/main/data/preferences_window/add_account_dialog.ui0000664000000000000000000010150114776241610023022 0ustar rootroot dino-0.5.0/main/data/preferences_window/change_password_dialog.ui0000664000000000000000000000626414776241610023737 0ustar rootroot dino-0.5.0/main/data/preferences_window/general_preferences_page.ui0000664000000000000000000000530214776241610024233 0ustar rootroot dino-0.5.0/main/data/preferences_window/preferences_window.ui0000664000000000000000000000256514776241610023141 0ustar rootroot dino-0.5.0/main/data/quote.ui0000664000000000000000000000506114776241610014510 0ustar rootroot 5 15 15 center 0 0 end start baseline 0 1 0 end start baseline 0 True 2 0 end start 0 0 1 3 window-close-symbolic False center 3 0 2 dino-0.5.0/main/data/search_autocomplete.ui0000664000000000000000000000145114776241610017400 0ustar rootroot horizontal 4 4 6 6 24 24 end dino-0.5.0/main/data/style-dark.css0000664000000000000000000000010614776241610015600 0ustar rootroot.overlay-toolbar { background-color: shade(@view_bg_color, 1.5); }dino-0.5.0/main/data/style.css0000664000000000000000000002346314776241610014674 0ustar rootroot/** * This theme file is applied after the operating system theme * It provides sane defaults for things that are very Dino-specific. */ @import url("conversation_details.css"); statuspage { opacity: 0.5; } window.dino-main .dino-header-right { background: @theme_base_color; } window.dino-main .dino-header-left { background: @insensitive_bg_color; } window.dino-main headerbar.dino-left label.title { opacity: 0; font-size: 0; color: transparent; } window.dino-main .dino-conversation { background: @theme_base_color; } window.dino-main .dino-conversation undershoot, window.dino-main .dino-conversation viewport /* Some themes set this */ { background: none; } @keyframes highlight { from { background-color: alpha(@accent_color, 0.5); } to { background-color: transparent; } } window.dino-main .dino-conversation .highlight-once { animation-duration: 3s; animation-timing-function: ease-out; animation-iteration-count: 1; animation-name: highlight; } window.dino-main .dino-conversation .message-box.highlight:not(.highlight-once) { background: @window_bg_color; } window.dino-main .dino-conversation .message-box { padding: 3px 15px 3px 15px; } window.dino-main .dino-conversation .has-skeleton { margin-top: 10px; } window.dino-main .dino-conversation .message-box:not(.has-skeleton) { padding-left: 58px; } window.dino-main .unread-count-notify { background-color: alpha(@theme_fg_color, 0.8); color: @theme_base_color; font-family: monospace; border-radius: 999em; padding: 0 .35em; } window.dino-main .unread-count-notify:backdrop { background-color: alpha(@theme_unfocused_fg_color, 0.8); color: @theme_unfocused_base_color; } window.dino-main .unread-count { background-color: alpha(@theme_fg_color, 0.1); color: @theme_fg_color; font-family: monospace; border-radius: 999em; padding: .2em .41em; } window.dino-main .unread-count:backdrop { background-color: alpha(@theme_unfocused_fg_color, 0.1); color: @theme_unfocused_fg_color; } window.dino-main .dino-sidebar > frame { background: @insensitive_bg_color; border-left: 1px solid @borders; border-bottom: 1px solid @borders; } /* Message */ .message-box { transition: background .05s ease; } window.dino-main .dino-conversation .message-box.edit-mode { background: alpha(@theme_selected_bg_color, 0.1); } window.dino-main .dino-conversation .message-box.edit-mode:hover { background: alpha(@theme_selected_bg_color, 0.12); } window.dino-main .dino-conversation .message-box.error { background: alpha(@error_color, 0.1); } window.dino-main .dino-conversation .message-box.error:hover { background: alpha(@error_color, 0.12); } window.dino-main .dino-quote { border-left: 3px solid alpha(@theme_fg_color, 0.2); background: alpha(@theme_fg_color, 0.05); border-color: alpha(@theme_fg_color, 0.2); padding: 10px; } window.dino-main .dino-quote:hover { background: alpha(@theme_fg_color, 0.08); } picture.avatar { border-radius: 3px; } /* Overlay Toolbar */ .overlay-toolbar { padding: 2px; border-radius: 6px; border-spacing: 0; } .overlay-toolbar > * { margin-top: 0; margin-bottom: 0; } /* File Widget */ window.dino-main .file-box-outer, window.dino-main .call-box-outer { background: @theme_base_color; border-radius: 3px; border: 1px solid alpha(@theme_fg_color, 0.1); } window.dino-main .file-box, window.dino-main .call-box { margin: 12px 16px 12px 12px; } window.dino-main .file-image-widget { border: 1px solid alpha(@theme_fg_color, 0.1); border-radius: 6px; } window.dino-main .file-image-widget .file-box-outer { color: #eee; background: rgba(0, 0, 0, 0.5); } window.dino-main .file-image-widget .file-box-outer button { color: #eee; background: transparent; border: none; box-shadow: none; } window.dino-main .file-image-widget .file-box-outer button:hover { background: rgba(100, 100, 100, 0.5); } .dino-main .file-image-widget picture { border-radius: 6px; } .dino-main .file-details { color: white; background: alpha(black, 0.3); border-radius: 5px; padding: 5px 10px; } .dino-main .circular-osd { background: alpha(@theme_bg_color, 0.75); border-radius: 100%; } /* Call widget */ window.dino-main .call-box-outer.incoming { border-color: alpha(@theme_selected_bg_color, 0.3); } window.dino-main .incoming-call-box { background: alpha(@theme_selected_bg_color, 0.1); } window.dino-main .multiparty-participants { border-top: 1px solid alpha(@theme_fg_color, 0.05); background: alpha(@theme_fg_color, 0.04); } /* Reactions */ .dino-main .reaction-grid button { min-height: 16px; min-width: 30px; padding: 4px; } .dino-main .reaction-grid button.own-reaction, .dino-main .reaction-grid .own-reaction button { background-color: alpha(@accent_bg_color, 0.1); border: 1px solid @accent_bg_color; padding: 3px; color: @accent_color; } .dino-main .reaction-grid button.own-reaction:hover, .dino-main .reaction-grid .own-reaction button:hover { background-color: alpha(@accent_bg_color, 0.2); } /* Sidebar */ window.dino-main .dino-sidebar > frame.collapsed { border-bottom: 1px solid @borders; } window.dino-main .dino-sidebar frame.auto-complete { background: @theme_base_color; } window.dino-main .dino-sidebar frame.auto-complete list > row { transition: none; } /* File overlay */ window.dino-main .dino-white-overlay { background: @theme_base_color; } window.dino-main .dino-file-overlay { border-radius: 5px; border: 1px solid alpha(black, 0.2); box-shadow: 0 2px 3px alpha(black, 0.1); } /* Chat Input*/ window.dino-main .dino-chatinput frame box { background: transparent; } window.dino-main .dino-attach-button { min-width: 24px; /* Make button the same width as avatars */ } window.dino-main .dino-attach-button, window.dino-main .dino-chatinput-button button { border: none; background: transparent; box-shadow: none; min-height: 0; padding: 7px 5px; color: alpha(@theme_fg_color, 0.7); outline: none; } window.dino-main .dino-attach-button:hover, window.dino-main .dino-chatinput-button button:hover { color: @theme_selected_bg_color; } window.dino-main .dino-attach-button:backdrop, window.dino-main .dino-chatinput-button button:backdrop { color: alpha(@theme_unfocused_fg_color, 0.6); } window.dino-main .dino-attach-button:active, window.dino-main .dino-attach-button:checked, window.dino-main .dino-chatinput-button button:active, window.dino-main .dino-chatinput-button button:checked { color: alpha(@theme_selected_bg_color, 0.8); } window.dino-main .dino-attach-button:checked:backdrop, window.dino-main .dino-chatinput-button button:checked:backdrop { color: alpha(@theme_unfocused_selected_bg_color, 0.8); } .dino-unread-line label { color: @theme_selected_bg_color; } .dino-unread-line separator { background-color: @theme_selected_bg_color; } .dino-chatinput, .dino-chatinput textview, .dino-chatinput textview text { background-color: @theme_base_color; } /*Chat input warning*/ box.dino-input-warning frame border { border-color: @warning_color; } box.dino-input-warning frame separator { background-color: @warning_color; border: none; } box.dino-input-warning label { color: mix(@warning_color, @theme_fg_color, 0.5); } /*Chat input error*/ box.dino-input-error frame { border-color: @error_color; } box.dino-input-error frame separator { background-color: @error_color; border: none; } box.dino-input-error .chat-input-status { color: @error_color; } @keyframes input-error-highlight { 0% { transform: translate(0,0); } 25% { transform: translate(-10px,0); } 75% { transform: translate(10px,0); } 100% { transform: translate(0,0); } } box.dino-input-error .chat-input-status.input-status-highlight-once { animation-duration: 0.5s; animation-timing-function: ease-in-out; animation-iteration-count: 1; animation-name: input-error-highlight; } /* Call window */ .dino-call-window decoration { border-radius: 0; } .dino-call-window .titlebar { min-height: 0; } .dino-call-window headerbar { box-shadow: none; } .dino-call-window .call-button { border-radius: 100%; background-color: @theme_bg_color; } .dino-call-window menubutton.call-mediadevice-settings-button button.toggle { min-width: 18px; min-height: 18px; } .dino-call-window .participant-header-bar .titles { background: none; border: none; border-radius: 0; color: #ededec; text-shadow: 0 0 2px black; } .dino-call-window .participant-header-bar { background: linear-gradient(rgba(0,0,0,0.4), rgba(0,0,0,0)); border: 0; border-radius: 0; } .dino-call-window .participant-header-bar button:hover:not(.close) { background: rgba(255,255,255,0.15); border-color: rgba(255,255,255,0); box-shadow: none; } .dino-call-window .participant-header-bar button image { color: alpha(white, 0.7); -gtk-icon-shadow: none; } .dino-call-window .participant-header-bar button:hover image { color: white; } .dino-call-window .participant-header-bar button.unencrypted image { color: @error_color; } .dino-call-window .call-bottom-bar { background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,0.3)); border: 0; } .dino-call-window { background-color: #212121; } .dino-call-window .participant-name { color: white; text-shadow: 1px 1px 2px black; } .dino-call-window .text-no-controls { color: black; background: white; border-radius: 5px; padding: 5px 10px; } .dino-call-window .own-video { box-shadow: 0 0 2px 0 rgba(0,0,0,0.5); } .qrcode-container > contents { background: white; /* Color of the quiet zone. MUST have the same "reflectance" as light modules of the QR code. */ } dino-0.5.0/main/data/unified_main_content.ui0000664000000000000000000001577414776241610017550 0ustar rootroot slide true true vertical False content never 1 placeholder 20 20 20 20 260 You have no open chats Click + to start a chat or join a channel vertical end true true false natural true 600 false content placeholder im.dino.Dino-symbolic True True false 400 400 400 true dino-0.5.0/main/data/unified_window_placeholder.ui0000664000000000000000000000454114776241610020731 0ustar rootroot dino-0.5.0/main/meson.build0000664000000000000000000001616214776241610014251 0ustar rootrootsubdir('po') dependencies = [ dep_dino, dep_gee, dep_glib, dep_gmodule, dep_gtk4, dep_icu_uc, dep_libadwaita, dep_m, dep_qlite, dep_xmpp_vala, meson.get_compiler('vala').find_library('posix'), ] sources = files( 'src/main.vala', 'src/ui/add_conversation/add_conference_dialog.vala', 'src/ui/add_conversation/add_contact_dialog.vala', 'src/ui/add_conversation/add_groupchat_dialog.vala', 'src/ui/add_conversation/conference_details_fragment.vala', 'src/ui/add_conversation/conference_list.vala', 'src/ui/add_conversation/list_row.vala', 'src/ui/add_conversation/roster_list.vala', 'src/ui/add_conversation/select_contact_dialog.vala', 'src/ui/add_conversation/select_jid_fragment.vala', 'src/ui/application.vala', 'src/ui/call_window/audio_settings_popover.vala', 'src/ui/call_window/call_bottom_bar.vala', 'src/ui/call_window/call_connection_details_window.vala', 'src/ui/call_window/call_encryption_button.vala', 'src/ui/call_window/call_window.vala', 'src/ui/call_window/call_window_controller.vala', 'src/ui/call_window/participant_widget.vala', 'src/ui/call_window/video_settings_popover.vala', 'src/ui/chat_input/chat_input_controller.vala', 'src/ui/chat_input/chat_text_view.vala', 'src/ui/chat_input/encryption_button.vala', 'src/ui/chat_input/occupants_tab_completer.vala', 'src/ui/chat_input/smiley_converter.vala', 'src/ui/chat_input/view.vala', 'src/ui/contact_details/permissions_provider.vala', 'src/ui/contact_details/settings_provider.vala', 'src/ui/conversation_content_view/call_widget.vala', 'src/ui/conversation_content_view/chat_state_populator.vala', 'src/ui/conversation_content_view/content_populator.vala', 'src/ui/conversation_content_view/conversation_item_skeleton.vala', 'src/ui/conversation_content_view/conversation_view.vala', 'src/ui/conversation_content_view/date_separator_populator.vala', 'src/ui/conversation_content_view/file_default_widget.vala', 'src/ui/conversation_content_view/file_image_widget.vala', 'src/ui/conversation_content_view/file_transmission_progress.vala', 'src/ui/conversation_content_view/file_widget.vala', 'src/ui/conversation_content_view/item_actions.vala', 'src/ui/conversation_content_view/message_widget.vala', 'src/ui/conversation_content_view/quote_widget.vala', 'src/ui/conversation_content_view/reactions_widget.vala', 'src/ui/conversation_content_view/subscription_notification.vala', 'src/ui/conversation_content_view/unread_indicator_populator.vala', 'src/ui/conversation_details.vala', 'src/ui/conversation_list_titlebar.vala', 'src/ui/conversation_selector/conversation_selector.vala', 'src/ui/conversation_selector/conversation_selector_row.vala', 'src/ui/conversation_titlebar/call_entry.vala', 'src/ui/conversation_titlebar/conversation_titlebar.vala', 'src/ui/conversation_titlebar/menu_entry.vala', 'src/ui/conversation_titlebar/occupants_entry.vala', 'src/ui/conversation_titlebar/search_entry.vala', 'src/ui/conversation_view.vala', 'src/ui/conversation_view_controller.vala', 'src/ui/file_send_overlay.vala', 'src/ui/global_search.vala', 'src/ui/main_window.vala', 'src/ui/main_window_controller.vala', 'src/ui/notifier_freedesktop.vala', 'src/ui/notifier_gnotifications.vala', 'src/ui/occupant_menu/list.vala', 'src/ui/occupant_menu/list_row.vala', 'src/ui/occupant_menu/view.vala', 'src/ui/util/accounts_combo_box.vala', 'src/ui/util/config.vala', 'src/ui/util/data_forms.vala', 'src/ui/util/file_metadata_providers.vala', 'src/ui/util/helper.vala', 'src/ui/util/label_hybrid.vala', 'src/ui/util/preference_group.vala', 'src/ui/util/size_request_box.vala', 'src/ui/util/sizing_bin.vala', 'src/ui/widgets/avatar_picture.vala', 'src/ui/widgets/date_separator.vala', 'src/ui/widgets/fixed_ratio_picture.vala', 'src/ui/widgets/natural_size_increase.vala', 'src/view_model/account_details.vala', 'src/view_model/conversation_details.vala', 'src/view_model/preferences_row.vala', 'src/view_model/preferences_window.vala', 'src/windows/preferences_window/account_preferences_subpage.vala', 'src/windows/preferences_window/accounts_preferences_page.vala', 'src/windows/preferences_window/add_account_dialog.vala', 'src/windows/preferences_window/change_password_dialog.vala', 'src/windows/preferences_window/encryption_preferences_page.vala', 'src/windows/preferences_window/general_preferences_page.vala', 'src/windows/preferences_window/preferences_window.vala', 'src/windows/conversation_details.vala', ) sources += gnome.compile_resources( 'resources', 'data/gresource.xml', source_dir: 'data', ) c_args = [ '-DG_LOG_DOMAIN="dino"', '-DGETTEXT_PACKAGE="dino"', '-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')), ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] if dep_gtk4.version() == 'unknown' or dep_gtk4.version().version_compare('>=4.6') vala_args += ['-D', 'GTK_4_6'] endif if dep_gtk4.version() == 'unknown' or dep_gtk4.version().version_compare('>=4.8') vala_args += ['-D', 'GTK_4_8'] endif if dep_gtk4.version() == 'unknown' or dep_gtk4.version().version_compare('>=4.12') vala_args += ['-D', 'GTK_4_12'] endif if dep_gtk4.version() == 'unknown' or dep_gtk4.version().version_compare('>=4.14') vala_args += ['-D', 'GTK_4_14'] sources += files('src/ui/conversation_content_view/file_transmission_progress.c') endif if dep_libadwaita.version() == 'unknown' or dep_libadwaita.version().version_compare('>=1.3') vala_args += ['-D', 'Adw_1_3'] endif if dep_libadwaita.version() == 'unknown' or dep_libadwaita.version().version_compare('>=1.4') vala_args += ['-D', 'Adw_1_4'] endif if meson.get_compiler('vala').version().version_compare('>=0.56.5') and meson.get_compiler('vala').version().version_compare('<0.58') vala_args += ['-D', 'VALA_0_56_GREATER_5'] endif if meson.get_compiler('vala').version().version_compare('>=0.56.11') and meson.get_compiler('vala').version().version_compare('<0.58') vala_args += ['-D', 'VALA_0_56_GREATER_11'] endif if meson.get_compiler('vala').version().version_compare('=0.56.11') vala_args += ['-D', 'VALA_0_56_11'] endif if meson.get_compiler('vala').version().version_compare('=0.56.12') vala_args += ['-D', 'VALA_0_56_12'] endif exe_dino = executable('dino', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, install: true, install_rpath: default_install_rpath) install_data('data/icons/scalable/apps/im.dino.Dino-symbolic.svg', install_dir: get_option('datadir') / 'icons/hicolor/symbolic/apps') install_data('data/icons/scalable/apps/im.dino.Dino.svg', install_dir: get_option('datadir') / 'icons/hicolor/scalable/apps') install_data('data/im.dino.Dino.appdata.xml', install_dir: get_option('datadir') / 'metainfo') install_data('data/im.dino.Dino.desktop', install_dir: get_option('datadir') / 'applications') install_data('data/im.dino.Dino.service', install_dir: get_option('datadir') / 'dbus-1/services') dino-0.5.0/main/po/0000775000000000000000000000000014776241610012517 5ustar rootrootdino-0.5.0/main/po/LINGUAS0000664000000000000000000000022414776241610013542 0ustar rootrootar ca cs da de el en eo es et eu fa fi fr gl hi hu hy ia id ie is it ja kab ko lb lt lv nb nl oc pl pt pt_BR ro ru si sq sv ta tr uk vi zh_CN zh_TW dino-0.5.0/main/po/ar.po0000664000000000000000000013152414776241610013467 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-11-02 17:00+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 5.8.2\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "ألغِ" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "اتصل" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "عَطِّل الحساب" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "فَعِّل الحساب" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "اختر الصورة الرمزية" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "اختر" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "الصور" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "كافة الملفات" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "أأزيلُ الحساب %s؟" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "جارٍ الاتصال …" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "متصل" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "غير متصل" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "كلمة السر خاطئة" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "شهادة ميفاق طبقة المنافذ الآمنة غير صالحة" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "خطأ" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "الحسابات" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "الحسابات المعطلة" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "إضافة حساب" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "لا حسابات نشطة" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "لِج" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "لم يتمكن الخادوم من إثبات أنه %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "لم يثق نظام تشغيلك بشهادة الأمان الخاصة به." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "مُنحت شهادة الأمان الخاصة به لنطاق آخر." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "ستصبح شهادة الأمان الخاصة به صالحة في المستقبل حصرًا." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "انتهت صلاحية شهادة الأمان الخاصة به." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "إنشاء حساب" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "يمكنك الآن استخدام الحساب %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "اسم المستخدم أو كلمة السر خاطئة" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "حدث خطأ" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "تعذر الاتصال إلى %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "العنوان غير صالح" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "لا ردّ مِن طرف الخادوم" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "سَجِّل على %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "يتطلب الخادوم التسجيل من خلال موقع وِب" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "افتح موقع الوِب" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "سَجِّل" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "طالِع %s للمزيد من المعلومات حول التسجيل" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "احظر المستخدم" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "احظر النطاق بأكمله" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "ألغِ الحظر" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "مُغرَز" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "أغرِز" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "محظور" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "النطاق محظور" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "احظر" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "اكتم" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "التنبيهات مُفَعَّلة" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "التنبيهات عند الإشارة" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "مكتوم" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "التنبيهات مُعَطَّلة" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "عَنْ" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "الإعدادات" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "ضبط الغرفة" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "التعمية" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "الأعضاء" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "مالك" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "مدير" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "عضو" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "حجم الملف يتجاوز الحد الذي يسمح به الخادوم." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "الرسالة طويلة جدًا" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "مُعَدَّلة" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "في انتظار الإرسال…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "فشل التسليم" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "غير مشفرة" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "تعذر إرسال الرسالة" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x، %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x، %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d، %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d، %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a، %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a، %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "منذ لحظات" msgstr[1] "منذ دقيقة واحدة" msgstr[2] "منذ دقيقتين" msgstr[3] "منذ %i دقائق" msgstr[4] "منذ %i دقيقة" msgstr[5] "منذ %i دقيقة" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "الآن" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "أنا" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "أُرسلت الصورة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "أُرسل الملف" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "استُلِمَت صورة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "استُلِم ملف" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "مكالمة صادرة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "مكالمة واردة" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "البارحة" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "ابدأ محادثة" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "مستخدِم" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "ادعُ" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "ابدأ محادثة خاصة" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "اطرد" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "امنح تصريح الكتابة" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "أبطِل تصريح الكتابة" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "ادعُ إلى المؤتمر" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "اختر ملفًا" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s مِن %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "اسم الغرفة" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "وصف الغرفة" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "دائمة" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "ستبقى الغرفة بعد مغادرة آخر مشارِك" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "البحث للعلن" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "تغيير المشارِكين للموضوع" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "السماح بعرض معرف جابِر (JID)" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "من يمكنه رؤية معرفات جابِر (JID) الخاصة بالمشارِكين؟" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "كلمة السر" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "تحت الإشراف" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "السماح بالكتابة فقط لحاملي الصلاحية" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "الأعضاء فقط" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "يمكن للأعضاء فقط الانضمام للغرفة" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "هذا المؤتمر لا يسمح لك بإرسال الرسائل." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "اطلب الإذن" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "أرسِل ملفًا" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "ابدأ مكالمة" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "مكالمة صوتية" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "مكالمة مرئية" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "ابحث في الرسائل" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "تفاصيل المحادثة" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "أغلِق المحادثة" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "معلومات التنقيح" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "يتصل…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "يرِن…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "أنهى %s المكالمة" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "رفض %s المكالمة" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "الكاميرات" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "لم يُعثر على كاميرا." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "الميكرفونات" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "لم يُعثر على ميكرفون." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "السماعات" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "لم يُعثر على سماعات." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "دعوة إلى المكالمة" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "ابدأ" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "انضم إلى قناة" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "التالي" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "انضم" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "ينضم…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "كلمة السر إلزامية لدخول الغرفة" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "ممنوع من الانضمام أو إنشاء مؤتمرات" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "الغرفة غير موجودة" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "لا تصريح لإنشاء غرفة" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "غرفة خاصة للأعضاء" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "اختر لقبًا آخر" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "الغرفة مكتظة" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "أضِف" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "عَنْ Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "اليوم" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a، %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "لا نتائج" msgstr[1] "نتيجة واحدة" msgstr[2] "نتيجتان" msgstr[3] "%i نتائج" msgstr[4] "%i نتيجة" msgstr[5] "%i نتيجة" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "في %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "مع %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "مكالمة مرئية واردة" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "مكالمة مرئية جماعية واردة" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "مكالمة جماعية واردة" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "ارفض" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "اقبل" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "طلب اشتراك" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "ارفض" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "دعوة إلى %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "دعاك %s إلى %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "طلب إذن" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "طلب %s الإذن بالكتابة في %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "إرسال إشعارات الكتابة" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "إرسال إيصالات القراءة" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "نشط" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "متوقف" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "المبدئي: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "اطلب" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "اطلب تصريحًا لإرسال الرسائل" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "مرحبًا بكم في Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "لِج أو أنشئ حسابًا للبدأ." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "هيئ الحساب" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "أدِر الحسابات" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "عنوان ميفاق الحضور والمراسلة القابل للتوسعة (XMPP)" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "اسم العرض" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "الموضوع" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "عَدِّل الرسالة" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "أنت" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "أضِف رد فعل" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "افتح" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "احفظ باسم…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s، و%s، و%i آخرون يكتبون…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s، و%s، و%s يكتبون…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s و%s يكتبان…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s يكتب…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "بدأتْ المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "بدأتْ منذ %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "توليت هذه المكالمة من جهاز آخر" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "انتهت المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "انتهت في %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "دامتْ %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "مكالمة فائتة" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "فَوَّت هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "فاتت %s هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "رُفِضَّت المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "رَفَضت هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "رفض %s هذه المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "فشلتْ المكالمة" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "أقل من ساعة" msgstr[1] "ساعة" msgstr[2] "ساعتان" msgstr[3] "%i ساعات" msgstr[4] "%i ساعة" msgstr[5] "%i ساعة" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "أقل من دقيقة" msgstr[1] "دقيقة واحدة" msgstr[2] "دقيقتان" msgstr[3] "%i دقائق" msgstr[4] "%i دقيقة" msgstr[5] "%i دقيقة" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "بضع ثوان" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "سُلِّمت" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "قُرِأَت" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "يرغب هذا المراسل بإضافتك لقائمة مراسليه" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "لا تدعم هذه المحادثة ردود الفعل." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "ردّ" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "لا تدعم هذه المحادثة الردود." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "جارٍ تنزيل %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "قدَّم %s: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "الملف المقدّم: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "قُدِّم الملف" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "فشل نقل الملف" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "ملف" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "جديد" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "تفاصيل المُراسل" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "حَدِّث الرسالة" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "تعذّر إنشاء اتصال آمن" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "عُد" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "اختر خادومًا عموميًا" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "أو أدخل عنوان الخادوم" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "لِج بدلًا من ذلك" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "كل شيء جاهز!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "أنهِ" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "أرسل تنبيهات ال_كتابة" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "أرسل إيصالات ال_قراءة" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "ال_تنبيهات" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "نبِّه عند تلقي رسالة جديدة" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "حوّل الا_بتسامات إلى إيموجيات" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "الكنية المحلية" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "حالة الاتصال" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "أزِل الحساب" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "عام" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "ليس لديك محادثات مفتوحة" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "انقر على + لبدء محادثة أو الانضمام إلى قناة" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "اختصارات لوحة المفاتيح" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "المحادثة" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "التصفح" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "الانتقال إلى المحادثة التالية" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "الانتقال إلى المحادثة السابقة" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "الحساب" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "اللقب" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "الكنية" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "أضف مُراسِلًا" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "عميل دردشة ميفاق الحضور والمراسلة القابل للتوسعة (XMPP) عصري" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "إنّ Dino برنامج عصري ومفتوح المصدر للدردشة مصُمّم لسطح المكتب. ويُركّز علي تقديم " "تجربة نظيفة وموثوق منها لجابر/ميفاق الحضور والمراسلة القابل للتوسعة (XMPP) " "مع أخذ خصوصيتكم بعين الاعتبار." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "وهو يدعم التشفير بواسطة OMEMO وOpenPGP مما يسمح بإعداد مزايا الخصوصية " "كالإيصالات المقروءة والإخطارات عند الكتابة." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "يجلب Dino السِجلّ مِن الخادوم، وثم يُزامِن الرسائل مع الأجهزة الأخرى." #: main/data/global_search.ui:27 msgid "No active search" msgstr "لا بحث نشط" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "اكتب للشروع في البحث" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "لا رسائل متطابقة" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "تحقق مِن التدقيق الإملائي أو حاول إزالة المرشحات" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "أرسل" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "التفضيلات" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "اختصارات لوحة المفاتيح" #~ msgid "Remove" #~ msgstr "أزِل" #, c-format #~ msgid "Sign in to %s" #~ msgstr "لِج إلى %s" #~ msgid "No accounts configured" #~ msgstr "لا حسابات مُعَدة" #~ msgid "Add an account" #~ msgstr "أضِف حسابًا" #~ msgid "Pick another server" #~ msgstr "اختر خادومًا آخَر" #~ msgid "Local Settings" #~ msgstr "الإعدادات المحلية" #~ msgid "Notifications" #~ msgstr "الإشعارات" #~ msgid "Pin conversation" #~ msgstr "تثبيت المحادثة" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "يثبت المحادثة في أعلى قائمة المحادثات" #~ msgid "Only when mentioned" #~ msgstr "فقط عندما أُذكر" #~ msgid "Permissions" #~ msgstr "التصريحات" #~ msgid "Conference Details" #~ msgstr "تفاصيل فريق المحادثة" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "حجب الإتصال وتحديثات الحالة في كلا الإتجاهين" #~ msgid "A password to restrict access to the room" #~ msgstr "كلمة مرور لتقييد الوصول إلى الغرفة" #~ msgid "Message history" #~ msgstr "إدارة التأريخ" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "الحد الأقصى للسجِل المؤرَّخ الصادر عن فريق المحادثة" #~ msgid "Convert smileys to emojis" #~ msgstr "تحويل الوجوه المبتسمة إلى إيموجي" #~ msgid "Check spelling" #~ msgstr "التدقيق الإملائي" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "اضغط هنا لبداية المحادثة أو للإنضمام إلى قناة." #~ msgid "No active conversations" #~ msgstr "ليس هناك أية محادثات نشطة" #~ msgid "Main window with conversations" #~ msgstr "النافذة الرئيسية بالمُحادثات" #~ msgid "%s, %s and %i others" #~ msgstr "%s و %s و %i آخَرين" #~ msgid "You can now start using %s" #~ msgstr "بإمكانك الآن الشروع في استخدام %s" #~ msgid "Open Registration" #~ msgstr "التسجيلات مفتوحة" #~ msgid "Save" #~ msgstr "حفظ" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s و %s" #~ msgid "%s and %s" #~ msgstr "%s و %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "يكتب…" #~ msgstr[1] "يكتب…" #~ msgstr[2] "يكتُبان…" #~ msgstr[3] "يكتبون…" #~ msgstr[4] "يكتبون…" #~ msgstr[5] "يكتبون…" #~ msgid "has stopped typing" #~ msgstr "توقفَ عن الكتابة" #~ msgid "%i search results" #~ msgstr "%i نتيجة بحث" #~ msgid "Discover real JIDs" #~ msgstr "استكشاف مُعرّفي JID الحقيقيين" #~ msgid "Who may discover real JIDs?" #~ msgstr "من يمكنهم استكشاف مُعرّفي JID الحقيقيين؟" #~ msgid "Password required for room entry, if any" #~ msgstr "مطلوب كلمة السر لدخول الغرفة، إن وُجِدت" #~ msgid "Failed connecting to %s" #~ msgstr "فشل الاتصال بـ %s" #~ msgid "Join Conference" #~ msgstr "الإنضمام إلى فريق محادثة" #~ msgid "Communicate happiness." #~ msgstr "أنشر السعادة." #~ msgid "Quit" #~ msgstr "الخروج" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "يجب أن يكون JID على شكل “user@example.com”" #~ msgid "Copy Link Address" #~ msgstr "نسخ العنوان" #~ msgid "Copy" #~ msgstr "نسخ" #~ msgid "Select All" #~ msgstr "اختيار الكل" #~ msgid "Search" #~ msgstr "البحث" #~ msgid "Start Chat" #~ msgstr "إبدأ الدردشة" dino-0.5.0/main/po/ca.po0000664000000000000000000012331514776241610013447 0ustar rootroot# Catalan translation for Dino. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018-2019. # msgid "" msgstr "" "Project-Id-Version: dino 20180510\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-29 12:09+0000\n" "Language-Team: Catalan; Valencian \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.1-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancel·la" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Connecta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desactiva el compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activa el compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Seleccioneu l'àvatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Selecciona" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imatges" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Tots els fitxers" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Voleu suprimir el compte %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "S'està connectant…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Connectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconnectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Contrasenya incorrecta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "El certificat TLS és invàlid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Error" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Comptes" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Comptes desactivats" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Afegeix un compte" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "No hi ha cap compte actiu" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Iniciar sessió" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "El servidor no ha pogut demostrar que és %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "El seu certificat de seguretat no és de confiança pel seu sistema operatiu." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "El seu certificat de seguretat s'emet a un altre domini." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "El seu certificat de seguretat només serà vàlid en el futur." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "El seu certificat de seguretat ha caducat." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crea un compte" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Ara podeu emprar el compte %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Usuari o contrasenya incorrecta" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Ha fallat alguna cosa" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "No s'ha pogut connectar a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "L'adreça no és vàlida" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "No s'ha rebut una resposta del servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registre en %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "El servidor requereix un registre a través d'una pàgina web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Obre el lloc web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registre" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Mireu %s per obtenir informació sobre com registrar-vos" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloqueja l'usuari" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloqueja tot el domini" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Desbloqueja" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fixat" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fixa" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqueja" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domini bloquejat" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloca" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Silencia" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificacions activades" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificacions de mencions" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silenciat" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificacions desactivades" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Quant a" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Preferències" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuració de la sala" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Xifratge" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propietari" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "El fitxer supera la mida màxima de càrrega del servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "El missatge és massa llarg" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "pendent…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "l'enviament ha fallat" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sense xifrar" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "No s'ha pogut enviar el missatge" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l.%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fa %i minut" msgstr[1] "fa %i minuts" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Ara mateix" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Jo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imatge enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fitxer enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imatge rebuda" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fitxer rebut" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Trucada sortint" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Trucada entrant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ahir" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Inicia una conversa" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuari" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invita" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Inicia una convesa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsa" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Autoritza el permís d'escriptura" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca el permís d'escriptura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invita a la conferència" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Selecciona el fitxer" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nom de la sala" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descripció de la sala" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistent" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "La sala romandrà després de la sortida del l'últim ocupant" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Cercable públicament" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Els ocupants poden canviar el tema" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permís per a veure JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui té permís per a veure els JID dels membres?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Contrasenya" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderat" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Només els ocupants amb veu poden enviar missatges" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Només per membres" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Només els membres poden entrar a la sala" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Aquesta conferència no us permet enviar missatges." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Sol·licita el permís" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Envia un fitxer" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Inicia la trucada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Trucada d'àudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Trucada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Cerca els missatges" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detalls de la conversa" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Tanca la conversa" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informació de depuració" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "S'està trucant…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Està sonant…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s trucada finalitzada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s ha rebutjat la trucada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Càmeres" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "No s'ha trobat cap càmera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Micròfons" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "No s'ha trobat cap micròfon." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altaveus" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "No s'ha trobat cap altaveu." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convida a la trucada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Inicia" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Uneix-m'hi al canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Següent" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Uneix-me" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "S'està unint…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Es requereix una contrasenya per entrar a la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Heu estat bandejat d'unir-vos o crear conferències" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala no existeix" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "No esteu autoritzat a crear una sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala només per a membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Seleccioneu un sobrenom diferent" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Massa ocupants a la sala" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Afegeix" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Quant al Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Avui" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultat de cerca" msgstr[1] "%i resultats de cerca" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Amb %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Trucada de vídeo entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Videotrucada de grup entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Trucada de grup entrant" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rebutja" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accepta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Petició de subscripció" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Rebutja" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitació a %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s us ha convidat a %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Sol·licitud de permís" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demana el permís per escriure a %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Envia notificacions de tecleig" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Envia rebuts de lectura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Habilitat" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Inhabilitat" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Per defecte: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Sol·licita" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Sol·licita permís per a enviar missatges" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Us donem la benvinguda al Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Entreu o creeu un compte per a començar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configureu un compte" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gestiona els comptes" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adreça XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nom del contacte" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Tema" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Edita el missatge" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tu" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Afegeix reacció" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Obre" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Desa com a…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s i %i altres estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s i %s estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s i %s estan escrivint…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s està escrivint…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "S'ha iniciat la trucada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Iniciada fa %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Heu gestionat aquesta trucada en un altre dispositiu" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "La trucada ha finalitzat" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Acabada a les %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durada %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Trucada perduda" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Us heu perdut aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s s'ha perdut aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Trucada rebutjada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Heu rebutjat aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s ha rebutjat aquesta trucada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "La trucada ha fallat" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i hores" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minuts" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "uns segons" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Enviat" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Llegit" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Aquest contacte vol afegir-vos a la seva llista de contactes" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Aquesta conversa no admet reaccions." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Respon" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Aquesta conversa no admet respostes." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "S'està baixant %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ha ofert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fitxer ofert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fitxer ofert" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Ha fallat la transferència del fitxer" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fitxer" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nou" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalls del contacte" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualitza el missatge" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "No s'ha pogut establir una connexió segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Enrere" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Trieu un servidor públic" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "O especifiqueu l'adreça d'un servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Inicia sessió" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tot llest!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finalitza" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Envia notificacions d'escriptura" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Envia _confirmació de lectura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificacions" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notifica'm quan arriba un missatge nou" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "C_onverteix emoticones a emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Àlias local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Estat de la connexió" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Elimina el compte" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "General" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "No teniu cap xat obert" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Feu clic a + per iniciar un xat o unir-vos a un canal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Dreceres de teclat" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegació" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Salta a la següent conversa" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Salta a la conversa anterior" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Sobrenom" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Àlias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Afegeix el contacte" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Client de xat XMPP modern" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino és un client de xat lliure i modern per a l'escriptori. Està centrat en " "proveir una experiència neta i fiable de Jabber/XMPP, sempre tenint en " "compte la vostra privacitat." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Implementa xifratge punt a punt amb OMEMO i OpenPGP, i permet configurar " "funcionalitats relacionades amb la privacitat com per exemple rebuts de " "lectura i notificacions d'escriptura." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera l'historial del servidor i sincronitza els missatges amb " "altres dispositius." #: main/data/global_search.ui:27 msgid "No active search" msgstr "No hi ha cap cerca activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Teclegeu per iniciar una cerca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "No hi ha missatges coincidents" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comproveu l'ortografia o proveu de suprimir filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Envia" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferències" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Dreceres de teclat" #~ msgid "Remove" #~ msgstr "Suprimeix" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Entra a %s" #~ msgid "No accounts configured" #~ msgstr "No hi ha cap compte configurat" #~ msgid "Add an account" #~ msgstr "Afegeix un compte" #~ msgid "Pick another server" #~ msgstr "Trieu un altre servidor" #~ msgid "Local Settings" #~ msgstr "Preferències locals" #~ msgid "Notifications" #~ msgstr "Notificacions" #~ msgid "Pin conversation" #~ msgstr "Fixa la conversa" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fixa la conversa a la part superior de la llista de converses" #~ msgid "Only when mentioned" #~ msgstr "Només quan rebeu una menció" #~ msgid "Permissions" #~ msgstr "Permisos" #~ msgid "Conference Details" #~ msgstr "Detalls de la conferència" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "La comunicació i actualitzacions d'estat en ambdúes direccions estan " #~ "blocades" #~ msgid "A password to restrict access to the room" #~ msgstr "Una contrasenya per a restringir l'accés a la sala" #~ msgid "Message history" #~ msgstr "Historial dels missatges" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Quantitat màxima de l'historial proporcionat per la sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Converteix «smileys» a emoji" #~ msgid "Check spelling" #~ msgstr "Revisa l'ortografia" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Feu clic aquí per a començar una conversa o unir-vos a un canal." #~ msgid "No active conversations" #~ msgstr "Cap conversa activa" #~ msgid "Main window with conversations" #~ msgstr "Finestra principal amb converses" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s i %i altres" #~ msgid "You can now start using %s" #~ msgstr "Ara podeu començar a emprar %s" #~ msgid "Open Registration" #~ msgstr "Registre obert" #~ msgid "Save" #~ msgstr "Desa" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s i %s" #~ msgid "%s and %s" #~ msgstr "%s i %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "està escrivint…" #~ msgstr[1] "estan escrivint…" #~ msgid "has stopped typing" #~ msgstr "ha parat d'escriure" #~ msgid "%i search results" #~ msgstr "%i resultats de cerca" #~ msgid "Discover real JIDs" #~ msgstr "Descobreix els JID reals" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui pot descobrir els JID reals?" #~ msgid "Password required for room entry, if any" #~ msgstr "La contrasenya requerida per entrar a la sala, si cal" #~ msgid "Failed connecting to %s" #~ msgstr "No s'ha pogut connectar a %s" #~ msgid "Join Conference" #~ msgstr "Uneix-te a la conferència" #~ msgid "Quit" #~ msgstr "Surt" #~ msgid "Copy Link Address" #~ msgstr "Copia l'adreça de l'enllaç" #~ msgid "Copy" #~ msgstr "Copia" #~ msgid "Select All" #~ msgstr "Selecciona-ho tot" #~ msgid "Search" #~ msgstr "Cerca" #~ msgid "Send message marker" #~ msgstr "Envia marcador de missatge" #~ msgid "Start Chat" #~ msgstr "Inicia un xat" dino-0.5.0/main/po/cs.po0000664000000000000000000011771714776241610013502 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-05-19 07:48+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.18-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Zrušit" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Připojit" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Vybrat avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Vybrat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Obrázky" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Všechny soubory" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Odstranit účet %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Připojování…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Připojeno" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Odpojeno" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Nesprávné heslo" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Neplatný TLS certifikát" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Chyba" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Účty" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Přidat účet" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Žádné aktivní účty" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Přihlásit se" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Server nemůže dokázat, že je %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Operační systém nedůvěřuje jeho bezpečnostnímu certifikátu." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Jeho bezpečnostní certifikát byl vydán pro jinou doménu." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Jeho bezpečnostní certifikát bude platný až v budoucnu." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Platnost jeho bezpečnostního certifikátu vypršela." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Vytvořit účet" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Účet %s můžete nyní používat." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nesprávné uživatelské jméno nebo heslo" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Něco se pokazilo" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Připojení k %s se nezdařilo" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Nesprávná adresa" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Žádná odpověď od serveru" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrovat na %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Server vyžaduje přihlášení prostřednictvím webové stránky" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Otevřít internetovou stránku" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrovat" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Podívejte se na %s pro získání informací ohledně způsobu přihlášení" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Zablokovat" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Nastavení" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Nastavení místnosti" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Členové" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Vlastník" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Člen" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Velikost souboru překračuje maximální limit nastavený na serveru." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Zpráva je příliš dlouhá" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "upraveno" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "čeká na vyřízení…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "doručení selhalo" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifrované" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Zprávu nelze odeslat" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Před %i minutou" msgstr[1] "Před %i minutami" msgstr[2] "Před %i minutami" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Nyní" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Já" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Obrázek byl odeslán" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Soubor byl odeslán" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Obrázek byl přijat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Soubor byl přijat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Odchozí hovor" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Příchozí hovor" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Včera" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Začít konverzaci" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Uživatel" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Pozvat" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Začít soukromou konverzaci" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Vyhodit" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Udělit práva pro zapisování" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Odebrat práva pro zapisování" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Pozvat na konferenci" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Vybrat soubor" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s z %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Název místnosti" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Popis místnosti" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Stálá" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Místnost bude zachována i poté, co ji opustí poslední účastník" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Veřejně vyhledatelná" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Účastníci mohou měnit předmět" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Oprávnění k náhledu JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kdo může vidět JID účastníků?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Heslo" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderováno" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Pouze účastníci s hlasem mohou zasílat zprávy" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Pouze pro členy" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Do místnosti mohou vstoupit pouze členové" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Na této konferenci nemůžete posílat zprávy." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Požádat o oprávnění" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Odeslat soubor" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Začít hovor" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hlasový hovor" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Video hovor" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Hledat zprávy" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informace pro ladění" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Volám…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Vyzvánění…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s ukončil hovor" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s odmítl(a) hovor" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Webkamery" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nebyla nalezena žádná webkamera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofony" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nebyly nalezeny žádné mikrofony." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Reproduktory" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nebyl nalezen žádný reproduktor." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Pozvat do hovoru" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Začít" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Připojit se ke kanálu" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Další" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Připojit" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Připojování…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pro vstup do místnosti je zapotřebí heslo" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Zákaz připojování ke konferencím nebo jejich vytváření" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Místnost neexistuje" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Nemáte oprávnění pro vytvoření místnosti" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Místnost pouze pro členy" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Vyberte jinou přezdívku" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "V místnosti je příliš mnoho účastníků" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Přidat" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "O Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Dnes" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d. %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i výsledek hledání" msgstr[1] "%i výsledky vyhledávání" msgstr[2] "%i výsledků vyhledávání" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "V %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "S %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Příchozí video hovor" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Příchozí skupinový videohovor" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Příchozí skupinový hovor" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Odmítnout" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Přijmout" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Požadavek na odběr" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Zamítnout" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Pozvánka do %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s Vás pozval do %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Žádost o oprávnění" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s žádá o oprávnění pro zápis do %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Odesílat notifikace o psaní" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Odesiílat potvrzení o přečtení" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Zapnuto" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Vypnuto" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Výchozí: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Žádost" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Požádat o oprávnění k odesílání zpráv" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Vítejte v Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Chcete-li začít, přihlaste se nebo si vytvořte účet." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Nastavit účet" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Správa účtů" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Upravit zprávu" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Vy" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Přidat reakci" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Otevřít" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Uložit jako…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s a %i dalších píše …" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s a %s se píší…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s a %s píší…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s píše…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Hovor začal" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Začal před %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Tento hovor byl přijat na jiném zařízení" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Hovor skončil" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Skončil v %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Trval %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Nepřijatý hovor" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Máte zmeškaný hovor" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s zmeškal(a) tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Hovor byl odmítnut" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Odmítl(a) jste tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s odmítl(a) tento hovor" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Hovor selhal" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hodina" msgstr[1] "%i hodiny" msgstr[2] "%i hodin" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuta" msgstr[1] "%i minut" msgstr[2] "%i minut" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "pár vteřin" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Doručeno" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Přečteno" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Tento kontakt by si vás rád přidal do svého seznamu kontaktů" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Tato konverzace nepodporuje reakce." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Odpovědět" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Tato konverzace neumožňuje odpovědi." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Stahování %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ke stažení: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Soubor ke stažení: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Soubor ke stažení" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Přenos souboru se nezdařil" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Soubor" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detaily Kontaktu" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aktualizovat zprávu" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Nepodařilo se navázat zabezpečené připojení" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Zpět" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Vyberte veřejný server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Nebo zadejte adresu serveru" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Místo toho se přihlásit" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Vše připraveno!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Dokončit" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Upozornit, jakmile přijde nová zpráva" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Místní alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Obecné" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nemáte žádné otevřené chaty" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Kliknutím na + zahájíte konverzaci nebo se připojíte ke kanálu" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Konverzace" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigace" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Přejít na další konverzaci" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Přejít na předchozí konverzaci" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Účet" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Přezdívka" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Přidat kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moderní XMPP klient" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino je moderní open-source chatovací klient pro stolní počítače. Jeho cílem " "je poskytování čistého a spolehlivého prostředí Jabber/XMPP s důrazem na " "zachování vašeho soukromí." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Podporuje šifrování end-to-end pomocí OMEMO a OpenPGP a umožňuje " "konfigurovat funkce související se soukromím, jako jsou potvrzení o přečtení " "a oznámení o psaní." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino načítá historii ze serveru a synchronizuje zprávy s ostatními " "zařízeními." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Žádné aktivní vyhledávání" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Pro zahájení vyhledávání začněte psát" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Žádné odpovídající zprávy" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Zkontrolujte pravopis nebo zkuste odstranit filtry" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Odeslat" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Klávesové zkratky" #~ msgid "Remove" #~ msgstr "Odstranit" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Přihlásit se k %s" #~ msgid "No accounts configured" #~ msgstr "Nejsou nakonfigurovány žádné účty" #~ msgid "Add an account" #~ msgstr "Přidat účet" #~ msgid "Pick another server" #~ msgstr "Vybrat jiný server" #~ msgid "Local Settings" #~ msgstr "Lokální Nastavení" #~ msgid "Notifications" #~ msgstr "Notifikace" #~ msgid "Pin conversation" #~ msgstr "Připnout konverzaci" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Připne konverzaci na začátek seznamu konverzací" #~ msgid "Only when mentioned" #~ msgstr "Pouze pokud je zmíněno" #~ msgid "Permissions" #~ msgstr "Oprávnění" #~ msgid "Conference Details" #~ msgstr "Detaily Konference" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Komunikace a aktualizace stavu jsou blokovány v obou směrech" #~ msgid "A password to restrict access to the room" #~ msgstr "Heslo pro omezení přístupu do místnosti" #~ msgid "Message history" #~ msgstr "Historie zpráv" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maximální výše nevyřízených žádostí vydaných místností" #~ msgid "Convert smileys to emojis" #~ msgstr "Převádět smajlíky na emotikony" #~ msgid "Check spelling" #~ msgstr "Kontrola pravopisu" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Kliknutím sem zahájíte konverzaci nebo se připojíte ke kanálu." dino-0.5.0/main/po/da.po0000664000000000000000000011610114776241610013443 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-01-06 01:07+0000\n" "Language-Team: none\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.4-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Fortryd" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Forbind" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Vælg avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Vælg" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Billeder" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Alle filer" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Fjern konto %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Forbinder…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Forbundet" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Afbrudt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Forkert kodeord" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ugyldigt TLS-certifikat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Fejl" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Konti" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Tilføj konto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Ingen aktive konti" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Log ind" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Serveren kunne ikke bevise at den er %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Dit operativsystem har ikke tillid til dens sikkerhedscertifikat." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Dens sikkerhedscertifikat er udstedt til et andet domæne." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Dets sikkerhedscertifikat bliver først gyldig i fremtiden." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Dets sikkerhedscertifikat er udløbet." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Opret en konto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Du kan nu bruge kontoen %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Forkert brugernavn eller kodeord" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Der gik noget galt" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Kunne ikke forbinde til %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ugyldig adresse" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Intet svar fra serveren" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrer på %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Serveren kræver at man logger ind via en webside" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Åbn webside" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrer" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Besøg %s for information om hvordan man tilmelder sig" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloker" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Indstillinger" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Rumopsætning" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Medlemmer" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Ejer" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Medlem" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Filen overstiger serverens maksimale uploadstørrelse." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Beskeden er for lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redigeret" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "afventer …" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "modtagelsen fejlede" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ukrypteret" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Ikke i stand til at sende beskeden" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minut siden" msgstr[1] "%i minutter siden" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Netop nu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Mig" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Billede sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Billede modtaget" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil modtaget" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Udgående opkald" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Indkommende opkald" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "I går" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Start samtale" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Bruger" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviter" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Start en privat samtale" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Spark" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Tildel skrivetilladelse" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Fratag skrivetilladelse" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviter til konference" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Vælg fil" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s fra %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Rummets navn" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Beskrivelse af rummet" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Vedvarende" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Rummet vil bestå efter den sidste deltager forlader det" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Søgbart for offentligheden" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Deltagerne kan ændre emnet" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Tilladelse til at se JID'er" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Hvem har tilladelse til at se deltagernes JID'er?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Kodeord" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Modereret" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Kun deltagere med audio må sende beskeder" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Kun for medlemmer" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Kun medlemmer må komme ind i rummet" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Denne konference tillader ikke at du sender meddelelser." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Anmod om tilladelse" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Send en fil" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Start opkald" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Lydopkald" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videoopkald" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Søg meddelelser" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Debuginformation" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Starter opkald…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Ringer…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s afsluttede opkaldet" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s afslog opkaldet" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameraer" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ingen kameraer fundet." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofoner" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Der er ikke fundet nogen mikrofoner." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Højtalere" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Der er ikke fundet nogen højtalere." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviter til opkald" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Start" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Forbind til kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Næste" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Forbind" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Forbinder…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Adgang til rummet kræver kodeord" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Udelukket fra at forbinde til eller oprette konference" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Rummet findes ikke" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ikke tilladt at oprette rum" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Rum kun for medlemmer" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Vælg et andet kaldenavn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Der er for mange personer i rummet" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Tilføj" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Om Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "I dag" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, d. %d. %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i søgeresultat" msgstr[1] "%i søgeresultater" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "I %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Med %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Indgående videoopkald" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Indgående videogruppeopkald" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Indgående gruppeopkald" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Afvis" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accepter" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abonnementsanmodning" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Afslå" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitation til %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s inviterede dig til %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Tilladelsesanmodning" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s anmoder om tilladelse til at skrive i %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Underret om at der skrives" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Send kvittering for læsning" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Til" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Fra" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Standardindstilling: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Anmodning" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Anmod om tilladelse til at sende beskeder" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Velkommen til Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Log ind eller opret en konto for at begynde." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Opret en konto" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Håndter konti" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Rediger besked" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Du" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Tilføj reaktion" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Åbn" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Gem som…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s og %i andre siger…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s skriver…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Opkald startet" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Begyndte for %s siden" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Du tog dette opkald på en anden enhed" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Opkald afsluttet" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Sluttede kl. %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Varede %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Opkald mistet" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Du gik glip af det opkald" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s gik glip af dette opkald" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Opkald afslået" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Du afslog dette opkald" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s afslog dette opkald" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Opkald fejlede" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i time" msgstr[1] "%i timer" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minutter" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "nogle få sekunder" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Afleveret" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Læs" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Denne kontakt vil gerne tilføje dig til sin kontaktliste" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Denne samtale understøtter ikke reaktioner." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Svar" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Denne samtale understøtter ikke svar." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Downloader %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s tilbudt: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fil tilbudt: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fil tilbudt" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Filoverførsel fejlede" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fil" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktoplysninger" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Opdater besked" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Kunne ikke oprette en sikker forbindelse" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tilbage" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Vælg en offentlig server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Eller angiv en serveradresse" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Log ind i stedet" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Alt er klart!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Afslut" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Giv besked når en ny meddelelse kommer ind" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokalt alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Generelt" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du har ingen åbne samtaler" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klik + for at starte en samtale eller forbinde til en kanal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Samtale" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigation" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Spring til næste samtale" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Spring til foregående samtale" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Kaldenavn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Tilføj kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moderne XMPP-Chatklient" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino er en moderne open-source chatklient til skrivebordet. Den fokuserer på " "at give en ren og pålidelig Jabber/XMPP-oplevelse som samtidig har dit " "privatliv på sinde." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Den understøtter end-to-end kryptering med OMEMO og OpenPGP og tillader at " "konfigurere privatlivs-relaterede features så som læsekvitteringer og besked " "om skrivning." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino henter tidligere beskeder fra serveren og synkroniserer beskeder med " "andre enheder." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ingen aktiv søgning" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skriv for at starte en søgning" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ingen modsvarende beskeder" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kontroller stavning eller prøv at fjerne nogle filtre" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Send" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Tastaturgenveje" #~ msgid "Remove" #~ msgstr "Fjern" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Log ind på %s" #~ msgid "No accounts configured" #~ msgstr "Ingen konti konfigureret" #~ msgid "Add an account" #~ msgstr "Tilføj en konto" #~ msgid "Pick another server" #~ msgstr "Vælg en anden server" #~ msgid "Local Settings" #~ msgstr "Lokale indstillinger" #~ msgid "Notifications" #~ msgstr "Underretninger" #~ msgid "Pin conversation" #~ msgstr "Fastgør samtale" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fastgør samtalerne til toppen af samtalelisten" #~ msgid "Only when mentioned" #~ msgstr "Kun når omtalt" #~ msgid "Permissions" #~ msgstr "Tilladelser" #~ msgid "Conference Details" #~ msgstr "Konferenceoplysninger" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Kommunikations- og statusopdateringer er blokerede i begge retninger" #~ msgid "A password to restrict access to the room" #~ msgstr "Et kodeord for at begrænse adgangen til rummet" #~ msgid "Message history" #~ msgstr "Beskedhistorie" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maksimalt antal tidligere beskeder udsendt af rummet" #~ msgid "Convert smileys to emojis" #~ msgstr "Konverter smileys til emojis" #~ msgid "Check spelling" #~ msgstr "Tjek stavning" dino-0.5.0/main/po/de.po0000664000000000000000000012533614776241610013461 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 23:17+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Abbrechen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Verbinden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Konto deaktivieren" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Konto aktivieren" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Profilbild auswählen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Auswählen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Bilder" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Alle Dateien" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Konto %s löschen?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Verbinden…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Verbunden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Nicht verbunden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Passwort falsch" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ungültiges TLS-Zertifikat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Fehler" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Konten" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Deaktivierte Konten" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Konto hinzufügen" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Keine Konten aktiv" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Einloggen" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Der Server konnte nicht beweisen, dass er %s ist." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Sein Sicherheitszertifikat wird von deinem Betriebssystem nicht akzeptiert." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Sein Sicherheitszertifikat ist für eine andere Domain ausgestellt." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Sein Sicherheitszertifikat ist erst in der Zukunft gültig." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Sein Sicherheitszertifikat ist abgelaufen." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Konto erstellen" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Du kannst das Konto %s jetzt benutzen." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Benutzername oder Passwort falsch" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Etwas ist schief gelaufen" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Verbindung zu %s konnte nicht hergestellt werden" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ungültige Adresse" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Keine Antwort vom Server" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Konto auf %s erstellen" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Der Server erfordert die Registrierung durch eine Webseite" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Webseite öffnen" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrieren" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Eventuell sind auf %s Informationen zur Registrierung zu finden" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Benutzer sperren" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Gesamte Domain sperren" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Entsperren" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Angeheftet" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Anheften" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Geblockt" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domain gesperrt" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blockieren" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Stummschalten" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Benachrichtigungen aktiviert" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Benachrichtigungen bei Erwähnungen" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Stummgeschaltet" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Benachrichtigungen deaktiviert" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Info" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Einstellungen" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Raumkonfiguration" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Verschlüsselung" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Mitglieder" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigentümer" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Mitglied" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Die Datei übersteigt die maximale Uploadgröße des Servers." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Nachricht zu lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "bearbeitet" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "ausstehend…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "Zustellung fehlgeschlagen" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Unverschlüsselt" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Nachricht kann nicht gesendet werden" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "vor %i Minute" msgstr[1] "vor %i Minuten" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Gerade eben" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ich" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild gesendet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Datei gesendet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild empfangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Datei empfangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Ausgehender Anruf" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Eingehender Anruf" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gestern" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Unterhaltung starten" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Gast" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Einladen" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Private Unterhaltung beginnen" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Hinauswerfen" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Schreibberechtigung erteilen" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Schreibberechtigung entziehen" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Zur Konferenz einladen" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Datei auswählen" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s aus %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Name des Raums" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Beschreibung des Raums" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Dauerhaft" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" "Der Raum bleibt erhalten, nachdem der letzte Benutzer ihn verlassen hat" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Öffentlich sichtbar" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Nutzer können das Thema ändern" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Erlaubnis JIDs zu sehen" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wer darf die JIDs von Nutzern sehen?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Passwort" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderiert" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Nur Nutzer mit Stimme können Nachrichten senden" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Ausschließlich Mitglieder" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Nur Mitglieder dürfen den Raum betreten" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Diese Konferenz erlaubt es dir nicht, Nachrichten zu senden." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Berechtigung anfordern" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Datei senden" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Anruf starten" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audioanruf" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videoanruf" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Nachrichten suchen" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Unterhaltungsdetails" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Unterhaltung schließen" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Debug-Information" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Anrufen…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Klingeln…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s hat den Anruf beendet" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s hat den Anruf abgelehnt" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Keine Kamera gefunden." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofone" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Kein Mikrofon gefunden." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Lautsprecher" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Kein Lautsprecher gefunden." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Zum Anruf einladen" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Starten" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Kanal beitreten" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Weiter" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Beitreten" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Beitreten…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Raum erfordert Passwort" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Beitreten oder erstellen der Konferenz verboten" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Raum existiert nicht" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Raum erzeugen verboten" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Raum nur für Mitglieder" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wähle einen anderen Spitznamen" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Zu viele Nutzer im Raum" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Hinzufügen" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Info zu Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Heute" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d.%b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i Suchergebnis" msgstr[1] "%i Suchergebnisse" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Mit %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Eingehender Videoanruf" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Eingehender Gruppen-Videoanruf" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Eingehender Gruppenanruf" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Ablehnen" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Annehmen" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Kontaktanfrage" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Ablehnen" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Einladung zu %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s hat dich in %s eingeladen" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Berechtigungsanfrage" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s erbittet die Berechtigung zum Schreiben in %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Tippbenachrichtigungen senden" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Lesebestätigungen senden" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "An" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Aus" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Anfragen" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Berechtigung zum Senden von Nachrichten anfordern" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Willkommen bei Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Melde dich an oder erstelle ein Konto, um loszulegen." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Konto einrichten" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Konten verwalten" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP-Adresse" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Angezeigter Name" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Thema" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Nachricht bearbeiten" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Du" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Reaktion hinzufügen" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Öffnen" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Speichern unter…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s und %i andere tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s und %s tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s und %s tippen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s tippt…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Anruf gestartet" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Gestartet vor %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Du hast diesen Anruf mit einem anderen Gerät geführt" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Anruf beendet" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Endete um %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Dauerte %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Du hast diesen Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s hat diesen Anruf verpasst" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Du hast diesen Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s hat diesen Anruf abgelehnt" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Fehlgeschlagener Anruf" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i Stunde" msgstr[1] "%i Stunden" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i Minute" msgstr[1] "%i Minuten" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "ein paar Sekunden" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Zugestellt" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Gelesen" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Anfrage senden" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Du erhältst noch keine Statusaktualisierungen von diesem Kontakt." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Dieser Kontakt will dich zu seiner Kontaktliste hinzufügen" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Diese Unterhaltung unterstützt keine Reaktionen." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Antworten" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Diese Unterhaltung unterstützt keine Antworten." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Herunterladen von %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Lade %s herunter…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Hochladen von %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s angeboten: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Datei angeboten: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Datei angeboten" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Dateiübertragung fehlgeschlagen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Datei" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Neu" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetails" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Nachricht aktualisieren" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Anmelden" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Es konnte keine sichere Verbindung hergestellt werden" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Zurück" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Wähle einen öffentlichen Server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Oder gib eine Serveradresse an" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Stattdessen einloggen" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Alles eingerichtet!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Fertig" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Tippbenachrichtigungen senden" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "_Lesebestätigungen senden" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Benachrichtigungen" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Benachrichtigen bei Empfang einer neuen Nachricht" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "Smileys in Emoji _umwandeln" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokaler Alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Verbindungsstatus" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Konto entfernen" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Passwort ändern" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Ändern" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Aktuelles Passwort" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Neues Passwort" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Passwort bestätigen" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Allgemein" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du hast keine offenen Chats" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klicke auf +, um einen Chat zu starten oder einem Kanal beizutreten" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Einstellungen anzeigen" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Tastenkombinationen" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Unterhaltung" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigation" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Zur nächsten Unterhaltung springen" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Zur letzten Unterhaltung springen" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nickname" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontakt hinzufügen" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Modernes XMPP-Chat-Programm" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino ist ein modernes, quelloffenes Chat-Programm für den Desktop. Es bietet " "ein aufgeräumtes und robustes Jabber-/XMPP-Erlebnis und legt einen " "Schwerpunkt auf Privatsphäre." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Er unterstützt Ende-zu-Ende Verschlüsselung mit OMEMO und OpenPGP und " "enthält Privatsphäre-Einstellungen zu Lesebestätigungen und " "Tippbenachrichtigungen." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino ruft Gesprächsverläufe vom Server ab und synchronisiert Nachrichten mit " "anderen Geräten." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Keine Suche gestartet" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tippe um eine Suche zu beginnen" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Keine Nachrichten gefunden" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Überprüfe die Schreibweise oder entferne Filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Senden" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Einstellungen" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Tastenkombinationen" #~ msgid "Remove" #~ msgstr "Löschen" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Bei %s einloggen" #~ msgid "No accounts configured" #~ msgstr "Keine Konten konfiguriert" #~ msgid "Add an account" #~ msgstr "Ein Konto hinzufügen" #~ msgid "Pick another server" #~ msgstr "Anderen Server wählen" #~ msgid "Local Settings" #~ msgstr "Lokale Einstellungen" #~ msgid "Notifications" #~ msgstr "Benachrichtigungen" #~ msgid "Pin conversation" #~ msgstr "Unterhaltung anheften" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Heftet die Unterhaltung an den Anfang der Unterhaltungsliste" #~ msgid "Only when mentioned" #~ msgstr "Nur bei Erwähnung" #~ msgid "Permissions" #~ msgstr "Berechtigungen" #~ msgid "Conference Details" #~ msgstr "Konferenzdetails" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Kommunikation und Statusaktualisierungen sind in beide Richtungen " #~ "blockiert" #~ msgid "A password to restrict access to the room" #~ msgstr "Ein Passwort, um den Zugang zum Raum zu beschränken" #~ msgid "Message history" #~ msgstr "Gesprächsverlauf" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Anzahl der für diesen Raum gespeicherten Nachrichten" #~ msgid "Convert smileys to emojis" #~ msgstr "Smileys zu Emojis umwandeln" #~ msgid "Check spelling" #~ msgstr "Rechtschreibung prüfen" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klicke hier, um eine Unterhaltung zu starten oder einem Kanal beizutreten." #~ msgid "Video call incoming" #~ msgstr "Eingehender Videoanruf" #~ msgid "Call incoming" #~ msgstr "Eingehender Anruf" #~ msgid "Establishing call" #~ msgstr "Anruf aufbauen" #~ msgid "Video call establishing" #~ msgstr "Videoanruf aufbauen" #~ msgid "Call establishing" #~ msgstr "Rufaufbau" #~ msgid "Call in progress…" #~ msgstr "Anruf wird ausgeführt…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Dauerte %s" #~ msgid "seconds" #~ msgstr "Sekunden" #~ msgid "No active conversations" #~ msgstr "Keine Unterhaltung aktiv" #~ msgid "Main window with conversations" #~ msgstr "Hauptfenster mit Konversationen" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s und %i weitere" #~ msgid "You can now start using %s" #~ msgstr "Du kannst %s ab jetzt nutzen" #~ msgid "Open Registration" #~ msgstr "Registrierung öffnen" #~ msgid "Save" #~ msgstr "Speichern" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s und %s" #~ msgid "%s and %s" #~ msgstr "%s und %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippt gerade…" #~ msgstr[1] "tippen gerade…" #~ msgid "has stopped typing" #~ msgstr "hat aufgehört zu tippen" #~ msgid "%i search results" #~ msgstr "%i Suchergebnisse" #~ msgid "Discover real JIDs" #~ msgstr "Echte JIDs finden" #~ msgid "Who may discover real JIDs?" #~ msgstr "Wer kann echte JIDs sehen?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passwort zum Betreten des Raums, falls gesetzt" #~ msgid "Failed connecting to %s" #~ msgstr "Verbindung zu %s fehlgeschlagen" #~ msgid "Join Conference" #~ msgstr "Konferenz beitreten" #~ msgid "Communicate happiness." #~ msgstr "Glücklich(keit) kommunizieren." #~ msgid "Quit" #~ msgstr "Beenden" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID sollte das Format “benutzer@beispiel.de” haben" #~ msgid "Copy Link Address" #~ msgstr "Link-Adresse Kopieren" #~ msgid "Copy" #~ msgstr "Kopieren" #~ msgid "Select All" #~ msgstr "Alles Auswählen" #~ msgid "Search" #~ msgstr "Suchen" #~ msgid "Send message marker" #~ msgstr "Lesebestätigung senden" #~ msgid "Start Chat" #~ msgstr "Chat starten" #~ msgid "Request presence updates" #~ msgstr "Online-Status erfragen" #~ msgid "Join on startup" #~ msgstr "Beim Start beitreten" #~ msgid "Add Chat" #~ msgstr "Chat hinzufügen" #~ msgid "Name" #~ msgstr "Name" #~ msgid "Description" #~ msgstr "Beschreibung" dino-0.5.0/main/po/dino.pot0000664000000000000000000010260114776241610014174 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "" dino-0.5.0/main/po/el.po0000664000000000000000000013157414776241610013472 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-05-19 07:48+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.18-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Ακύρωση" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Σύνδεση" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Επιλογή avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Επιλογή" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Εικόνες" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Όλα τα αρχεία" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Αφαίρεση λογαριασμού %s;" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Σύνδεση…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Συνδεδεμένο" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Ασύνδετο" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Λάθος κωδικός" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Μη έγκυρο πιστοποιητικό TLS" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Λάθος" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Λογαριασμοί" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Προσθήκη Λογαριασμού" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Δεν υπάρχουν ενεργοί λογαριασμοί" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Σύνδεση" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Ο διακομιστής δεν μπόρεσε να αποδείξει ότι είναι %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Το πιστοποιητικό ασφαλείας του δεν είναι αξιόπιστο από το λειτουργικό σας " "σύστημα." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Το πιστοποιητικό ασφαλείας του έχει εκδοθεί για διαφορετικό domain." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Το πιστοποιητικό ασφαλείας του θα είναι έγκυρο μόνο στο μέλλον." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Το πιστοποιητικό ασφαλείας του έχει λήξει." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Δημιουργία λογαριασμού" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Τώρα μπορείτε να χρησιμοποιήσετε τον λογαριασμό %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Λάθος όνομα χρήστη ή κωδικός" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Κάτι πήγε στραβά" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Δεν ήταν δυνατή η σύνδεση στο %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Μη έγκυρη διεύθυνση" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Καμία απάντηση από τον διακομιστή" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Εγγραφή στο %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Ο διακομιστής απαιτεί την εγγραφή μέσω ενός ιστότοπου" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Άνοιγμα ιστότοπου" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Εγγραφή" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ελέγξτε %s για πληροφορίες σχετικά με τον τρόπο εγγραφής" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Αποκλεισμός" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Ρυθμίσεις" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Διαμόρφωση δωματίου συνομιλίας" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Μέλη" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Ιδιοκτήτης" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Διαχειριστής" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Μέλος" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Το αρχείο υπερβαίνει το μέγιστο μέγεθος μεταφόρτωσης του διακομιστή." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Το μήνυμα είναι πολύ μεγάλο" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "διόρθωση" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "εκκρεμεί…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "αποτυχία παράδοσης" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Μη κρυπτογραφημένο" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Δεν είναι δυνατή η αποστολή μηνύματος" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i λεπτό πριν" msgstr[1] "%i λεπτά πριν" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Μόλις τώρα" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Εγώ" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Η εικόνα εστάλη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Το αρχείο εστάλη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Η εικόνα ελήφθη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Το αρχείο ελήφθη" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Εξερχόμενη κλήση" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Εισερχόμενη κλήση" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Εχθές" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Έναρξη Συνομιλίας" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Χρήστης" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Πρόσκληση" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Ξεκινήστε ιδιωτική συνομιλία" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Αποβολή" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Εκχώρηση άδειας εγγραφής" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Ανάκληση άδειας εγγραφής" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Πρόσκληση σε Συνεδρία" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Επιλέξτε αρχείο" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s από %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Όνομα του δωματίου συνομιλίας" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Περιγραφή του δωματίου συνομιλίας" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Διηνεκές" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Το δωμάτιο θα παραμείνει μετά την αποχώρηση του τελευταίου χρήστη" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Δημόσια αναζητήσιμο" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Οι χρήστες έχουν τη δυνατότητα να αλλάξουν θέμα" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Άδεια θέασης JIDs" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Ποιος επιτρέπεται να δει τα JIDs των χρηστών;" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Κωδικός πρόσβασης" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Εποπτευόμενο" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Μόνο οι χρήστες με \"φωνή\" μπορούν να στέλνουν μηνύματα" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Μόνο για μέλη" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Μόνο μέλη μπορούν να εισέλθουν στην δωμάτιο συνομιλίας" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Αυτή η συνεδρία δεν σας επιτρέπει να στέλνετε μηνύματα." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Ζητήστε άδεια" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Αποστολή αρχείου" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Έναρξη κλήσης" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Ηχητική κλήση" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Βίντεο κλήση" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Αναζήτηση μηνυμάτων" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Πληροφορίες εντοπισμού σφαλμάτων" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Κλήση…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Κουδούνισμα…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s τερμάτισε την κλήση" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s απέρριψε την κλήση" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Κάμερες" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Δεν βρέθηκε κάμερα." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Μικρόφωνα" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Δε βρέθηκε μικρόφωνο." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Ηχεία" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Δε βρέθηκε ηχείο." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Πρόσκληση σε Κλήση" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Έναρξη" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Συμμετοχή σε Ομαδική συνομιλία" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Επόμενο" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Συμμετοχή" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Είσοδος…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Απαιτείται κωδικός πρόσβασης για την είσοδο στο δωμάτιο συνομιλίας" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Απαγορεύεται η συμμετοχή ή η δημιουργία συνεδρίου" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Το δωμάτιο συζήτησης δεν υπάρχει" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Δεν επιτρέπεται η δημιουργία δωματίου συνομιλίας" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Είσοδος μόνο για Μέλη" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Επιλέξτε ένα διαφορετικό ψευδώνυμο" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Πάρα πολλοί χρήστες στο δωμάτιο συνομιλίας" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Προσθήκη" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Σχετικά με το Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Σήμερα" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i αποτέλεσμα αναζήτησης" msgstr[1] "%i αποτελέσματα αναζήτησης" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Στο %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Με %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Εισερχόμενη βιντεοκλήση" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Εισερχόμενη ομαδική βιντεοκλήση" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Εισερχόμενη ομαδική κλήση" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Απόρριψη" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Αποδοχή" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Αίτημα συνδρομής" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Απόρριψη" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Πρόσκληση σε %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s σας προσκάλεσε στο %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Αίτημα άδειας" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ζητά την άδεια να γράψει σε %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Αποστολή ειδοποιήσεων πληκτρολόγησης" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Αποστολή αποδείξεων ανάγνωσης" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Ενεργό" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Ανενεργό" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Προεπιλογή: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Αίτηση" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Ζητήστε άδεια για αποστολή μηνυμάτων" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Καλώς ήρθατε στο Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Συνδεθείτε ή δημιουργήστε έναν λογαριασμό για να ξεκινήσετε." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Ρύθμιση λογαριασμού" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Διαχείριση λογαριασμών" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Επεξεργασία μηνύματος" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Εσύ" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Προσθήκη αντίδρασης" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Άνοιγμα" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Αποθήκευση ως…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s και %i άλλοι πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s και %s πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s και %s πληκτρολογούν…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s πληκτρολογεί…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Η κλήση ξεκίνησε" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Ξεκίνησε πριν από %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Διαχειριστήκατε αυτήν την κλήση σε άλλη συσκευή" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Τέλος κλήσης" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Τερματισμός στις %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Διήρκεσε %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Αναπάντητη κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Χάσατε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s έχασε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Η κλήση απορρίφθηκε" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Απορρίψατε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s απέρριψε αυτήν την κλήση" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Αποτυχία κλήσης" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ώρα" msgstr[1] "%i ώρες" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i λεπτό" msgstr[1] "%i λεπτά" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "μερικά δευτερόλεπτα" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Παραδόθηκε" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Διαβάστηκε" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Αυτή η επαφή θα ήθελε να σας προσθέσει στη λίστα επαφών της" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Αυτή η συνομιλία δεν υποστηρίζει αντιδράσεις." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Απάντηση" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Αυτή η συνομιλία δεν υποστηρίζει απαντήσεις." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Λήψη %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s για μεταφόρτωση: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Αρχείο για μεταφόρτωση: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Αρχείο για μεταφόρτωση" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Η μεταφορά αρχείων απέτυχε" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Αρχείο" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Στοιχεία Επικοινωνίας" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Ενημέρωση μηνύματος" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Δεν ήταν δυνατή η δημιουργία ασφαλούς σύνδεσης" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Πίσω" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Επιλέξτε έναν δημόσιο διακομιστή" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ή καθορίστε μια διεύθυνση διακομιστή" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Αντ' αυτού, συνδεθείτε" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Όλα έτοιμα!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Ολοκλήρωση" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Ειδοποίηση όταν έρχεται νέο μήνυμα" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Τοπικό ψευδώνυμο" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Γενικά" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Δεν έχετε ανοιχτές συνομιλίες" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Κλικ + για έναρξη συνομιλίας ή εγγραφή σε ομαδική συνομιλία" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Συνομιλία" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Πλοήγηση" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Μετάβαση στην επόμενη συνομιλία" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Μετάβαση στην προηγούμενη συνομιλία" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Λογαριασμός" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Υποκοριστικό (nickname)" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Ψευδώνυμο" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Προσθήκη Επαφής" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Σύγχρονος XMPP Chat Client" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Το Dino είναι ένας σύγχρονος πελάτης συνομιλίας ανοιχτού κώδικα για desktop " "υπολογιστές. Επικεντρώνεται στην παροχή μιας καθαρής και αξιόπιστης " "εμπειρίας Jabber/XMPP έχοντας παράλληλα υπόψη την προστασία των προσωπικών " "δεδομένων σας." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Υποστηρίζει κρυπτογράφηση από άκρο σε άκρο με OMEMO και OpenPGP και " "επιτρέπει την ρύθμιση λειτουργιών που σχετίζονται με το απόρρητο, όπως " "αποδείξεις ανάγνωσης και ειδοποιήσεις πληκτρολόγησης." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Το Dino ανακτά το ιστορικό από τον διακομιστή και συγχρονίζει τα μηνύματα με " "άλλες συσκευές." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Δεν υπάρχει ενεργή αναζήτηση" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Πληκτρολογήστε για να ξεκινήσει μια αναζήτηση" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Δεν βρέθηκαν ανάλογα μηνύματα" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ελέγξτε την ορθογραφία ή προσπαθήστε να αφαιρέσετε φίλτρα" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Αποστολή" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Συντομεύσεις πληκτρολογίου" #~ msgid "Remove" #~ msgstr "Αφαίρεση" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Συνδεθείτε στο %s" #~ msgid "No accounts configured" #~ msgstr "Δεν έχουν διαμορφωθεί λογαριασμοί" #~ msgid "Add an account" #~ msgstr "Προσθήκη λογαριασμού" #~ msgid "Pick another server" #~ msgstr "Επιλέξτε άλλο διακομιστή" #~ msgid "Local Settings" #~ msgstr "Τοπικές Ρυθμίσεις" #~ msgid "Notifications" #~ msgstr "Ειδοποιήσεις" #~ msgid "Pin conversation" #~ msgstr "Καρφίτσωμα συνομιλίας" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Καρφίτσωμα συνομιλίας στην κορυφή της λίστας" #~ msgid "Only when mentioned" #~ msgstr "Μόνο όταν υπάρξει αναφορά σε εμένα" #~ msgid "Permissions" #~ msgstr "Άδειες" #~ msgid "Conference Details" #~ msgstr "Λεπτομέρειες Συνεδρίου" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Η επικοινωνία και οι ενημερώσεις κατάστασης προς οποιαδήποτε κατεύθυνση " #~ "είναι αποκλεισμένες" #~ msgid "A password to restrict access to the room" #~ msgstr "" #~ "Ένας κωδικός πρόσβασης για τον περιορισμό της πρόσβασης στο δωμάτιο " #~ "συνομιλίας" #~ msgid "Message history" #~ msgstr "Ιστορικό μηνυμάτων" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Μέγιστο ιστορικό που εκδίδεται από το δωμάτιο συνομιλίας" #~ msgid "Convert smileys to emojis" #~ msgstr "Μετατροπή smileys σε emojis" #~ msgid "Check spelling" #~ msgstr "Ορθογραφικός έλεγχος" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Κάντε κλικ εδώ για να ξεκινήσετε μια συνομιλία ή να εισέλθετε σε ένα " #~ "δωμάτιο ομαδικής συνομιλίας." dino-0.5.0/main/po/en.po0000664000000000000000000010177114776241610013470 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "" dino-0.5.0/main/po/eo.po0000664000000000000000000012152614776241610013471 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-18 10:09+0000\n" "Language-Team: Esperanto \n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.8-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Nuligi" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Konekti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Malaktivigi konton" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Aktivigi konton" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Elekti profilbildon" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Elekti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Bildoj" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Ĉiuj dosieroj" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Ĉu forigi konton %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Konektado…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Konektita" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Malkonektita" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Malĝusta pasvorto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Nevalida TLSa atesto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Eraro" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Kontoj" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Malaktivigitaj kontoj" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Aldoni Konton" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Neniuj aktivaj kontoj" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Ensaluti" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "La servilo ne povis pruvi, ke ĝi estas %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Ĝia atestilo ne estas fidata de via mastruma sistemo." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Ĝia atestilo estas eldonita por alia retejo." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Ĝia atestilo validos nur estonte." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Ĝia atestilo eksvalidiĝis." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Krei konton" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Vi povas nun uzi la konton %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Malĝusta uzantnomo aŭ pasvorto" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Io misis" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Ne povus konekti al %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Nevalida adreso" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Nenia respondo de servilo" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registriĝi sur %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "La servilo postulas registradon per retejo" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Malfermi retejon" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registriĝi" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Rigardi %s por informo pri kiel registriĝi" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloki uzanton" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloki tutan domajnon" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Malbloki" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Alpinglita" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Alpingli" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Blokita" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Blokita domajno" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Forbari" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Mutigi" #: main/src/windows/conversation_details.vala:130 #, fuzzy msgid "Notifications enabled" msgstr "Aktivigitaj sciigoj" #: main/src/windows/conversation_details.vala:133 #, fuzzy msgid "Notifications for mentions" msgstr "Sciigoj por mencioj" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Mutigita" #: main/src/windows/conversation_details.vala:138 #, fuzzy msgid "Notifications disabled" msgstr "Malaktivigitaj sciigoj" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Pri" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Agordoj" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Babilejaj Agordoj" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 #, fuzzy msgid "Encryption" msgstr "Kodo" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anoj" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Posedanto" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administranto" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "La dosiero superas la maksimuman grandon de alŝuto al la servilo." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mesaĝo tro longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redaktita" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "sendata…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "sendo malsukcesis" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Neĉifrita" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Ne povas sendi mesaĝon" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d-a %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d-a %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Antaŭ %i minuto" msgstr[1] "Antaŭ %i minutoj" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Ĵus" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Mi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bildon sendis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Dosieron sendis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bildon ricevis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Dosieron ricevis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Eliranta alvoko" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Envenanta alvoko" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Hieraŭ" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Komenci Konversacion" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Uzanto" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviti" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Komenci privatan interparolon" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Forpeli" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Doni skriban permeson" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Eksvalidigi skriban permeson" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviti en la Konferencon" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Elekti dosieron" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s el %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nomo de la babilejo" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Priskribo de la babilejo" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Daŭra" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "La babilejo daŭros post eliro de la lasta ĉeestinto" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Publike serĉebla" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Ĉeestantoj rajtas ŝanĝi temon" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permeso por vidi JID-ojn" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kiu rajtas vidi la JID-ojn de la ĉeestantoj?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Pasvorto" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Kontrolata" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Nur ĉeestantoj kun voĉo rajtas sendi mesaĝojn" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Nur anoj" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Nur anoj rajtas eniri la babilejon" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ĉi tiu konferenco ne permesas al vi sendi mesaĝojn." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Peti permeson" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Sendi dosieron" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Komenci alvokon" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Telefona alvoko" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videa alvoko" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Serĉi mesaĝojn" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detaloj de conversacio" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Fermi Conversacion" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informoj por sencimigo" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Alvokante…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Voksonante…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s finis la alvokon" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s malakceptis la alvokon" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Fotiloj" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kamerao ne troviĝis." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonoj" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofono ne troviĝis." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Laŭtparoliloj" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Laŭtparolilo ne troviĝis." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviti al alvoko" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Komenci" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Aliĝi al Kanalo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Sekva" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Aliĝi" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Aliĝante…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pasvorto estas bezonata por eniri en babilejon" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Forbarita je aliĝi aŭ krei konferencon" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Babilejo ne ekzistas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ne estas permesata, ke vi kreas babilejon" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Nurmembra babilejo" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Elektu malsaman kromnomon" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tro da ĉeestantoj en babilejo" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Aldoni" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Pri Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hodiaŭ" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d-a %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i serĉrezulto" msgstr[1] "%i serĉrezultoj" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Kun %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Envenanta videa alvoko" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Envenanta videa grupa alvoko" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Envenanta grupa alvoko" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rifuzi" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akcepti" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abona peto" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Rifuzi" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "invitilo por %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "invitilo de %s por %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Peto pri permeso" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s petas la permeson skribi en %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Sciigi pri tajpado" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Sendi kvitancojn de legiteco" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Ek" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "For" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Defaŭlto: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Peto" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Peti permeson por sendi mesaĝojn" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Bonvenon al Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Ensalutu aŭ kreu konton por komenci." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Agordi konton" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Administri kontojn" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adreso XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Temo" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Redakti mesaĝon" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Vi" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Aldoni reagon" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Malfermi" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Konservi kiel…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s, kaj %i ceteruloj tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, kaj %s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s kaj %s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s tajpas mesaĝon…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Alvoko komenciĝis" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Komencita antaŭ %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Vi akceptis ĉi tiun alvokon per alia aparato" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Alvoko finiĝis" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Finiĝis je %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Daŭris %sn" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Alvoko ne estis akceptita" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Vi ne akceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s ne akceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Alvoko estis malakceptita" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Vi malakceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s malakceptis ĉi tiun alvokon" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Alvoko malsukcesis" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i horo" msgstr[1] "%i horoj" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutoj" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "kelke da sekundoj" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Liverite" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Legi" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Ĉi tiu kontakto ŝatus aligi vin al sia kontaktlisto" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Ĉi konversaciejo ne subtenas reagojn." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Respondi" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Ĉi konversaciejo ne subtenas respondojn." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Elŝutado de %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofertis: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Dosiero ofertita: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Dosiero ofertita" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Dosiertransigo malsukcesis" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Dosiero" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nova" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktaj Detaloj" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Ĝisdatigi mesaĝon" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Ne povis sekure konekti" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Reen" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Elektu publikan servilon" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Aŭ specifu servilan adreson" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Ensaluti anstataŭe" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Ĉio pretas!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Fini" #: main/data/preferences_window/general_preferences_page.ui:9 #, fuzzy msgid "Send _Typing Notifications" msgstr "Sendi Sciigojn pri _Tajpojn" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 #, fuzzy msgid "_Notifications" msgstr "_Sciigoj" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Sciigi pri envenaj mesaĝoj" #: main/data/preferences_window/general_preferences_page.ui:55 #, fuzzy msgid "_Convert Smileys to Emoji" msgstr "_Konverti Mienetojn al Emoĝioj" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Loka kaŝnomo" #: main/data/preferences_window/account_preferences_subpage.ui:106 #, fuzzy msgid "Connection status" msgstr "Konektostatuso" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Forigi konton" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Ĝenerala" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vi ne havas malfermitajn babilojn" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Alklaku +(on) por komenci konversacion aŭ aligi al kanalo" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Konversacio" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigado" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Salti al sekva konversacio" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Salti al antaŭa konversacio" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Kaŝnomo" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Kaŝnomo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Aldoni kontakton" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moderna XMPP-Retebabililo" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino estas moderna malfermfonta retbabililo por la tabla komputilo. Ĝi celas " "provizi puran kaj fidindan sperton de Jabber/XMPP, protektante vian " "privatecon." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ĝi subtenas fin-al-finan ĉifradon per OMEMO kaj OpenPGP kaj permesas agordi " "funkciojn pri privateco kiel kvitancojn de legiteco kaj sciigojn pri tajpado." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino prenas historion el la servilo kaj sinkronigas mesaĝojn kun aliaj " "aparatoj." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Neniu aktiva serĉo" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Ektajpu por ekserĉi" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Neniuj kongruaj mesaĝoj" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Korektu la ortografion aŭ provu forigi filtrilojn" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Sendi" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferoj" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Fulmoklavoj" #~ msgid "Remove" #~ msgstr "Forigi" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Ensalutu al %s" #~ msgid "No accounts configured" #~ msgstr "Neniu aktiva konto" #~ msgid "Add an account" #~ msgstr "Aldoni konton" #~ msgid "Pick another server" #~ msgstr "Elekti alian servilon" #~ msgid "Local Settings" #~ msgstr "Lokaj Agordoj" #~ msgid "Notifications" #~ msgstr "Sciigoj" #~ msgid "Pin conversation" #~ msgstr "Fiksi konversacion" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fiksi konversacion supre de la konversacia listo" #~ msgid "Only when mentioned" #~ msgstr "Nur menciite" #~ msgid "Permissions" #~ msgstr "Permesoj" #~ msgid "Conference Details" #~ msgstr "Detaloj pri Konferenco" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Komunikado kaj statoĝisdatigoj ambaŭdirektaj estas forbaritaj" #~ msgid "A password to restrict access to the room" #~ msgstr "Pasvorto por limigi aliron al la babilejo" #~ msgid "Message history" #~ msgstr "Mesaĝa historio" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maksimuma nombro da historiaj mesaĝoj donotaj de la babilejo" #~ msgid "Convert smileys to emojis" #~ msgstr "Grafikigi tekstajn mienetojn" #~ msgid "Check spelling" #~ msgstr "Kontroli ortografion" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Alklaku ĉi tie por komenci konversacion aŭ aliĝi al kanalo." #~ msgid "No active conversations" #~ msgstr "Neniu aktiva interparolo" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s, kaj %i aliaj" #~ msgid "You can now start using %s" #~ msgstr "Vi nun povas komenci uzanta %s" #~ msgid "Open Registration" #~ msgstr "Malfermi registriĝon" #~ msgid "Save" #~ msgstr "Konservi" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s kaj %s" #~ msgid "%s and %s" #~ msgstr "%s kaj %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tajpas…" #~ msgstr[1] "tajpas…" #~ msgid "has stopped typing" #~ msgstr "ĉesis tajpi" #~ msgid "Discover real JIDs" #~ msgstr "Eltrovi verajn JID-ojn" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kiu rajtas eltrovi verajn JID-ojn?" #~ msgid "Failed connecting to %s" #~ msgstr "Malsukcesis konektanta al %s" #~ msgid "Join Conference" #~ msgstr "Aliĝi al Kunveno" #~ msgid "Quit" #~ msgstr "Ĉesi" #~ msgid "Search" #~ msgstr "Serĉi" #~ msgid "Send message marker" #~ msgstr "Sendi mesaĝmarkilon" #~ msgid "Start Chat" #~ msgstr "Ekbabili" #~ msgid "Request presence updates" #~ msgstr "Peti sciigojn pri ĉeesto" #~ msgid "Join on startup" #~ msgstr "Aliĝi je lanĉo" #~ msgid "Add Chat" #~ msgstr "Aldoni Babilon" dino-0.5.0/main/po/es.po0000664000000000000000000012520014776241610013466 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-23 19:09+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.1-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Conectar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desactivar cuenta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activar cuenta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Seleccionar imagen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Seleccionar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imágenes" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Todos los archivos" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "¿Quitar cuenta %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conectando…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Contraseña incorrecta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Error" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Cuentas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Cuentas deshabilitadas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Añadir Cuenta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "No hay cuentas activas" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Iniciar Sesión" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "El servidor no pudo probar que es %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "El certificado no es confiable para el sistema operativo." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "El certificado de seguridad está emitido para otro dominio." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "El certificado de seguridad solo será válido en el futuro." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "El certificado de seguridad está expirado." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crear cuenta" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Ya puedes usar la cuenta %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Usuario o contraseña incorrecta" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Algo ha ido mal" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "No se pudo conectar a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Dirección inválida" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "No hay respuesta del servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrarte en %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "El servidor requiere registrarse a través de un sitio web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Abrir sitio web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrarse" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Comprobar %s para información sobre como registrarse" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloquear usuario" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloquear todo el dominio" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Desbloquear" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fijado" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fijar" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqueado" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Dominio bloqueado" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloquear" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Silenciar" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificaciones habilitadas" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificaciones de menciones" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silenciado" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificaciones desactivadas" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Acerca de" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Configuraciones" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuración de la conversación en grupo" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Encriptación" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Miembros" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propietario" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Miembro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "El archivo excede el tamaño máximo de subida del servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mensaje demasiado largo" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "pendiente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "entrega fallida" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sin cifrado" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "No se pudo enviar el mensaje" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "hace %i min" msgstr[1] "hace %i mins" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Justo ahora" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Yo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagen enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Archivo enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagen recibida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Archivo recibido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Llamada saliente" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Llamada entrante" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ayer" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Iniciar Conversación" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuario" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invitar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversación privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permiso para escribir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar permiso para escribir" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitar a Conversación en grupo" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Seleccionar archivo" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s desde %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nombre de la conversación en grupo" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descripción de la conversación en grupo" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistente" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "La sala persistirá después de que el último ocupante se salga" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Búsqueda pública" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Los ocupantes pueden cambiar de asunto" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permiso para ver los JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "¿Quién puede ver los JID's de los ocupantes?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Contraseña" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderada" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Solo los participantes con voz pueden enviar mensajes" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Solo miembros" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Solo los miembros pueden entrar a la conversación" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Esta conversación en grupo no permite enviar mensajes." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicitar permiso" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Enviar un archivo" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar llamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audiollamada" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videollamada" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Buscar mensajes" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detalles de la conversación" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Cerrar la conversación" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Información de depuración" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Llamando…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Sonando…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s finalizó la llamada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s ha rechazado la llamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Cámaras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "No se encontró una cámara." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Micrófonos" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "No se encontró un micrófono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altavoces" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "No se encontraron altavoces." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invitar a la Llamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Unirse a Conversación en grupo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Siguiente" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Unirse" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Uniéndose…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Contraseña requerida para entrar a la conversación" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Prohibido unirse o crear una conversación en grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala no existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "No está permitido crear una conversación en grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "La conversación es solo para miembros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Elige un alias diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "La conversación tiene demasiados ocupantes" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Añadir" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Acerca de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoy" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado de búsqueda" msgstr[1] "%i resultado de búsquedas" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Videollamada entrante" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Llamada entrante de video en grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Llamada grupal entrante" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rechazar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Solicitud de suscripción" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Denegar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitación a %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s te ha invitado a %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Solicitud de permiso" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s solicita permiso para escribir en %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Enviar notificación de escritura" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Enviar notificación de lectura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Sí" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "No" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Por defecto: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Solicitar" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Solicitar permiso para enviar mensajes" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "¡Bienvenido a Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Inicia sesión o crea una cuenta para iniciar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Establecer cuenta" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gestionar cuentas" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Dirección XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nombre para mostrar" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Tema" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Editar el mensaje" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tú" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Añadir la reacción" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Guardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s y otros %i están tecleando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s y %s están escribiendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s y %s están escribiendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s está escribiendo…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Llamada iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Inició hace %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Ha gestionado esta llamada en otro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Llamada finalizada" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Finalizada a las %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Duración %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Llamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Hay una llamadas perdida" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s tiene una llamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Llamada rechazada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Has rechazado esta llamada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s ha rechazado esta llamada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Llamada fallida" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i horas" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "hace segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Entregado" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Leer" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "A este contacto le gustaría añadirte a la lista de contactos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Esta conversación no admite reacciones." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Respuesta" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Esta conversación no admite réplicas." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Bajando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofreció: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Archivo ofrecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Archivo ofrecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transferencia de archivo fallida" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Archivo" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nuevo" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalles de Contacto" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar mensaje" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "No se pudo establecer una conexión segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Volver" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Elegir un servidor público" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "O especificar la dirección de servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Iniciar sesión" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "¡Todo listo!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finalizado" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Enviar notificaciones de escritura" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Enviar _confirmación de lectura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificaciones" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notificar cada nuevo mensaje" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Convertir emoticones en emojis" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Estado de la conexión" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Eliminar cuenta" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "General" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "No tienes conversaciones abiertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Haz clic en + para iniciar un chat o unirte a un canal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Atajos de teclado" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversación" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegación" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Saltar a conversación siguiente" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Saltar a conversación anterior" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Cuenta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Alias" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Añadir Contacto" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Un cliente de XMPP moderno" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un cliente de mensajería moderno y libre para escritorio y móvil. " "Está enfocado en proveer una experiencia Jabber/XMPP limpia y confiable " "teniendo la privacidad en mente." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Soporta cifrado fin-a-fin a través de OMEMO y OpenPGP y permite configurar " "características relacionadas con la privacidad, como confirmaciones de " "lectura y notificaciones de escritura." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera el historial de mensajes desde el servidor y sincroniza los " "mensajes con otros dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "No hay búsquedas activas" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escribir para iniciar una búsqueda" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "No hay mensajes coincidentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comprueba la ortografía o intenta quitar los filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferencias" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Atajos de teclado" #~ msgid "Remove" #~ msgstr "Quitar" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Iniciar sesión a %s" #~ msgid "No accounts configured" #~ msgstr "No hay cuentas configuradas" #~ msgid "Add an account" #~ msgstr "Añadir una cuenta" #~ msgid "Pick another server" #~ msgstr "Elegir otro servidor" #~ msgid "Local Settings" #~ msgstr "Configuraciones locales" #~ msgid "Notifications" #~ msgstr "Notificaciones" #~ msgid "Pin conversation" #~ msgstr "fijar la conversación" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "" #~ "Fija la conversación en la parte superior de la lista de conversaciones" #~ msgid "Only when mentioned" #~ msgstr "Solo cuando te mencionan" #~ msgid "Permissions" #~ msgstr "Permisos" #~ msgid "Conference Details" #~ msgstr "Detalles de Conversación en grupo" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "La comunicación y las actualizaciones de estado en cualquier dirección " #~ "están bloqueadas" #~ msgid "A password to restrict access to the room" #~ msgstr "Una contraseña para restringir el acceso de la sala" #~ msgid "Message history" #~ msgstr "Historial de mensajes" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Cantidad máxima de mensajes previos emitido por la sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Convertir sonrisas en emoticonos" #~ msgid "Check spelling" #~ msgstr "Comprobar ortografía" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Pulsar aquí para iniciar una conversación o unirse a un conversación en " #~ "grupo." #~ msgid "No active conversations" #~ msgstr "No hay conversaciones activa" #~ msgid "Main window with conversations" #~ msgstr "Ventana principal con conversaciones" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s y %i otros" #~ msgid "You can now start using %s" #~ msgstr "Puedes empezar a usar %s" #~ msgid "Open Registration" #~ msgstr "Registro abierto" #~ msgid "Save" #~ msgstr "Guardar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s y %s" #~ msgid "%s and %s" #~ msgstr "%s y %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está escribiendo…" #~ msgstr[1] "están escribiendo…" #~ msgid "has stopped typing" #~ msgstr "ha dejado de escribir" #~ msgid "%i search results" #~ msgstr "%i resultados de la búsqueda" #~ msgid "Discover real JIDs" #~ msgstr "Descubrir JIDs reales" #~ msgid "Who may discover real JIDs?" #~ msgstr "¿Quién puede ver los JIDs reales?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Contraseña requerida para entrar en la conversación en grupo. Dejar vacío " #~ "para ninguna" #~ msgid "Failed connecting to %s" #~ msgstr "Fallo conectando con %s" #~ msgid "Join Conference" #~ msgstr "Unirse a Conversación en grupo" #~ msgid "Communicate happiness." #~ msgstr "Comunícate felizmente." #~ msgid "Quit" #~ msgstr "Salir" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "El JID debe tener la forma \"usuario@ejemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar dirección del enlace" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Seleccionar todo" #~ msgid "Search" #~ msgstr "Buscar" #~ msgid "Send message marker" #~ msgstr "Enviar confirmación al recibir y al leer los mensajes" #~ msgid "Start Chat" #~ msgstr "Iniciar Conversación" #~ msgid "Request presence updates" #~ msgstr "Solicitar actualizaciones de presencia" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "" #~ "Esto es una notificación de la aplicación. Haz click en el botón para " #~ "descartar" #~ msgid "Join on startup" #~ msgstr "Unirse al inicio" #~ msgid "Add Chat" #~ msgstr "Añadir conversación" dino-0.5.0/main/po/et.po0000664000000000000000000011616014776241610013474 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 23:17+0000\n" "Language-Team: none\n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Katkesta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Ühenda" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Lülita kasutajakonto välja" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Lülita kasutajakonto sisse" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Vali tunnuspilt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Vali" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Pildid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Kõik failid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Kas eemaldame kasutajakonto: %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Ühendame…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Ühendatud" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Ühendus on katkestatud" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Vale salasõna" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Vigane TLS-sertifikaat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Viga" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Kasutajakontod" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Mittekasutusel kontod" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Lisa kasutajakonto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Aktiivseid kasutajakontosid ei leidud" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Logi sisse" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Server ei suutnud tuvastada, et see on %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Tema turvasertifikaat pole sinu operatsioonisüsteemi poolt usaldatud." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Tema turvasertifikaat on välja antud muule domeenile." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Tema turvasertifikaadi kehtivus algab tulevikus." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Tema turvasertifikaat on aegunud." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Loo kasutajakonto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Sa võid nüüd kasutada kontot %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Vale kasutajanimi või salasõna" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Midagi läks valesti" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Ühenduse loomine serveriga %s ei õnnestunud" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Vigane aadress" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Server ei vasta" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registreeri serveris %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Serveri reeglid näevad ette, et registreerud veebisaidis" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Ava veebisait" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registreeru" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Registreerumise kohta leiad teavet: %s" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Blokeeri kasutaja" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blokeeri kogu domeen" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Eemalda blokeering" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Esiletõstetud" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Tõsta esile" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Blokeeritud" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domeen on blokeeritud" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokeeri" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Summuta" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Teavitused on kasutusel" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Teavitused mainimiste puhul" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Summutatud" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Teavitused pole kasutusel" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Teave" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Seadistused" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Jututoa seadistused" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Krüptimine" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Liikmed" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Omanik" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Peakasutaja" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Liige" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Faili suurus ületab serveris seadistatud üleslaadimise mahupiiri." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Sõnum on liiga pikk" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "muudetud" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "ootel…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "edasisaatmine ei õnnestunud" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Krüptimata" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Sõnumi saatmine ei õnnestu" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b. %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b. %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min eest" msgstr[1] "%i min eest" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Just nüüd" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Mina" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Pilt on saadetud" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fail on saadetud" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Pilt on vastu võetud" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fail on vastu võetud" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Väljuv kõne" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Saabuv kõne" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Eile" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Alusta vestlust" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Kasutaja" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Kutsu" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Alusta privaatset vestlust" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Müksa välja" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Luba kirjutusõigus" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Keela kirjutusõigus" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Kutsu vestlusrühma" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Vali fail" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s vestlusest %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Jututoa nimi" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Jututoa kirjeldus" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Püsiv" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Jututuba säilib ka peale viimase osaleja lahkumist" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Avalikult leitav" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Jututoas osalejad võivad teemat muuta" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Õigus näha osalejate JID-tunnuseid" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kellel on õigus näha jututoas osalejate JID-tunnuseid?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Salasõna" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Modereeritud" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Sõnumeid võivad saata vaid hääleõigusega osalejad" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Ainult liikmetele" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Jututuppa pääsevad vaid liikmed" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Selles vestlusrühmas sa ei saa saata sõnumeid." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Küsi luba" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Saada fail" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Helista" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Häälkõne" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videokõne" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Otsi sõnumeid" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Vestluse üksikasjad" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Sulge vestlus" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Silumisteave" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Helistame…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Heliseb…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s lõpetas kõne" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s ei võtnud kõnet vastu" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kaamerad" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ühtegi kaamerat ei leidunud." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonid" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ühtegi mikrofoni ei leidunud." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Kõlarid" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ühtegi kõlarit ei leidunud." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Helista" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Alusta" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Liitu kanaliga" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Edasi" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Liitu" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Liitume…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Jututuppa pääsemiseks on vaja salasõna" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Sul on keeld liituda vestlusrühmaga või seda luua" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Jututuba pole olemas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Jututoa loomine pole lubatud" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Ainult liikmetele mõeldud jututuba" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Vali mõni muu hüüdnimi" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Jututoas on liiga palju osalejaid" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Lisa" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Rakenduse teave: Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Täna" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d. %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i otsingutulemus" msgstr[1] "%i otsingutulemust" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Vestlusrühmas %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Kasutajaga %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Saabuv videokõne" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Saabuv videokõne rühmaga" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Saabub kõne rühmaga" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Keeldu" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Nõustu" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Tellimuspäring" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Keeldu" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Vestluskutse: %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s saatis sulle kutse vestlusrühma %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Loa küsimine" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s küsib õigusi kirjutada vestlusrühmas %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Saada kirjutamisteatisi" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Saada lugemisteatisi" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "kasutusel" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "pole kasutusel" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Vaikimisi: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Palu" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Palu õigust kirjutada siin vestlusrühmas" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Tere tulemast suhtlusrakendusse Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Alustamiseks logi sisse või loo kasutajakonto." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Seadista kasutajakontot" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Halda kasutajakontosid" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP aadress" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Kuvatav nimi" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Teema" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Muuda sõnumit" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Sina" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Reageeri" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ava" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salvesta kui…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ja %i muud kasutajat kirjutavad…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ja %s kirjutavad…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s ja %s kirjutavad…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s kirjutab…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Kõne algas" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Algusest möödas: %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Sa tegelesid selle kõnega muus seadmes" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Kõne lõppes" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Lõppes %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Kestus: %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Vastamata kõne" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Sa ei vastanud sellele kõnele" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s ei vastanud sellele kõnele" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Kõnest keeldumine" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Sina keeldusid vastamast sellele kõnele" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s keeldus vastamast sellele kõnele" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Kõne ei õnnestunud" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i tund" msgstr[1] "%i tundi" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minutit" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "paar sekundit" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Kohaletoimetatud" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Loetud" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Saada päring" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Sa veel ei saa selle kontakti olekuteateid." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "See kontakt soovib sind lisada oma kontaktiloendisse" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "See vestlus ei toeta reageerimise võimalust." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Vasta" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "See vestlus ei toeta vastamise võimalust." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Allalaadimisel %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Laadime alla %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Üleslaadimisel %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s, mahuga: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fail mahuga: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Pakutav fail" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Faili edastamine ei õnnestunud" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fail" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Uus" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktandmed" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Uuenda sõnumit" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Logi sisse" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Turvalise ühendus loomine ei õnnestunud" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tagasi" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Vali avalik server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Või sisesta serveri aadress" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Või pigem logi sisse" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Kõik on valmis!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Lõpeta" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Saada _kirjutamisteatisi" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Saada _lugemisteatisi" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Teavitused" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Teavita uue sõnumi saabumisel" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Muuda emotikonid emojideks" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Kohalik varjunimi" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Ühenduse olek" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Eemalda kasutajakonto" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Salasõna muutmine" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Muuda" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Praegune salasõna" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Uus salasõna" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Korda salasõna" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Üldised" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Sul pole vestlusi" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Vestluse loomiseks või kanaliga liitumiseks klõpsi + ikooni" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Näita eelistusi" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Klaviatuuri kiirklahvid" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Vestlus" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Liikumine rakenduses" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Suundu järgmise vestluse juurde" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Suundu eelmise vestluse juurde" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Kasutajakonto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Hüüdnimi" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Varjunimi" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Lisa kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moodne XMPP-põhine vestlusrakendus" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino on moodne avatud lähtekoodil põhinev vestlusrakendus töölauale. " "Rakenduse eesmärgiks on tagada selge ja usaldusväärne Jabber/XMPP võrgul " "põhinev suhtlusvõimalus, mis arvestab sinu privaatsusega." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Rakendus toetab nii OMEMO, kui OpenPGP standardil põhinevat läbivat " "krüptimist ja võimaldab seadistada privaatsusega seotud funktsionaalsusi, " "nagu lugemis- ja kirjutamisteatised." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino laadib vestluste ajaloo serverist ja sünkroniseerib sõnumeid kõikide " "sinu seadmete vahel." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ei leidu ühtegi otsingut" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Otsimiseks kirjuta midagi" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sellele otsingule pole vastuseid" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" "Proovi laiendada otsingut, kontrollida õigekirja või eemaldada filtreid" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Saada" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Eelistused" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Klaviatuuri kiirklahvid" #~ msgid "Remove" #~ msgstr "Eemalda" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Logi sisse kasutajakontoga %s" #~ msgid "No accounts configured" #~ msgstr "Sa pole seadistanud ühtegi kasutajakontot" #~ msgid "Add an account" #~ msgstr "Lisa kasutajakonto" #~ msgid "Pick another server" #~ msgstr "Vali mõni muu server" dino-0.5.0/main/po/eu.po0000664000000000000000000012211614776241610013473 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-12-09 11:48+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Utzi" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Konektatu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Irudia hautatu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Hautatu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Irudiak" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Fitxategi guztiak" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "%s kontua kendu?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Konektatzen…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Konektatuta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Ez konektatuta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Pasahitz okerra" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "TLS ziurtagiri ez balidouna" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Akatsa" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Kontuak" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Kontua gehitu" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Ez dago kontu aktiborik" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Saioa hasi" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Zerbitzariak ezin izan du frogatu %s dela." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Zure sistema eragilea ez da bere segurtasun ziurtagiriaz fido." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Bere segurtasun ziurtagiria beste domeinu baterako eman da." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Bere segurtasun ziurtagiria etorkizunean izango da baliozkoa soilik." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Bere segurtasun ziurtagiria iraungi da." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Kontua sortu" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Orain %s kontua erabili dezakezu." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Erabiltzaile izen edo pasahitz okerra" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Zerbait oker joan da" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Ezin izan da %s(e)ra konektatu" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Helbide ez balioduna" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Erantzunik ez zerbitzaritik" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Izena eman hemen: %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Zerbitzariak webgune baten bidez izena ematea eskatzen du" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Webgunea ireki" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Izena eman" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ikusi %s izena ematearen inguruko informazioa lortzeko" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokeatu" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Ezarpenak" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Gelaren konfigurazioa" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Kideak" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Jabea" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administratzailea" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Kidea" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" "Fitxategiak zerbitzariaren kargatzeko gehienezko tamaina gainditzen du." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mezu luzeegia" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editatuta" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "bidaltzeko zain…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "entregak huts egin du" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Enkriptatu gabe" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Ezin da mezua bidali" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "orain dela min %i" msgstr[1] "orain dela %i min" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Orain" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ni" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Irudia bidali da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fitxategia bidali da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Irudia jaso da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fitxategia jaso da" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Irteten ari den deia" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Deia jasotzen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Atzo" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Elkarrizketa hasi" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Erabiltzailea" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Gonbidatu" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Solasaldi pribatua hasi" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kanporatu" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Idazteko baimena eman" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Idazteko baimena ezeztatu" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Konferentziara gonbidatu" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Fitxategia hautatu" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s hemendik: %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Gelaren izena" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Gelaren deskribapena" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Iraunkorra" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Gelak iraun egingo du azken parte-hartzailea irten ondoren" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Publikoki bilagarria" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Parte-hartzaileek gaia alda dezakete" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JIDak ikusteko baimena" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Nor dago baimenduta parte-hartzaileen JIDak ikusteko?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Pasahitza" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderatua" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Ahotsa duten parte-hartzaileek soilik bidali dezakete mezuak" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Kideak soilik" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Kideak soilik sar daitezke gelara" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Konferentzia honek ez dizu mezuak bidaltzea baimentzen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Baimena eskatu" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Fitxategi bat bidali" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hasi deia" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audio-deia" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Bideo-deia" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Mezuak bilatu" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Arazte informazioa" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Deika…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Soinua jotzen…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s(e)k deia amaitu zuen" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s(e)k uko egin dio deiari" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamerak" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ez da kamerarik aurkitu." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonoak" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ez da mikrofonorik aurkitu." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Bozgorailuak" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ez da bozgorailurik aurkitu." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Dei batera gonbidatu" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Hasi" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Kanalera gehitu" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Hurrengoa" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Batu" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Batzen…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Pasahitza behar da gelara sartzeko" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Konferentzia sortu edo batzea debekatuta" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Gela ez da existitzen" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ez duzu gela sortzeko baimenik" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Gela kideentzat da soilik" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Ezizen ezberdin bat hautatu" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Gelak kide gehiegi ditu" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Gehitu" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dinori buruz" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Gaur" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "Bilaketa emaitza %i" msgstr[1] "%i bilaketa emaitza" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "hemen %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s(r)ekin" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Bideo-deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Taldeko bideo-deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Taldeko deia jasotzen" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Ukatu" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Onartu" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Harpidetzaren eskaera" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Ukatu" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s(e)ra gonbidapena" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s(e)k %s(e)ra gonbidatu zaitu" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Baimen eskaera" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s baimena eskatzen du %s(e)n idazteko" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Idazte jakinarazpenak bidali" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Bidali irakurtze baieztapena" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Piztuta" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Itzalita" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Lehenetsia: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Eskatu" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Mezuak bidaltzeko baimena eskatu" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Ongi etorri Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Saioa hasi edo kontu bat sortu hasteko." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Kontu bat ezarri" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Kontuak kudeatu" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ireki" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Gorde honela…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s eta beste %i idatzen ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s eta %s idazten ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s eta %s idazten ari dira…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s idazten ari da…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Deia hasi da" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Duela %s hasi zen" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Deia hau beste gailu batean kudeatu duzu" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Deia amaitu da" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%s-tan amaituta" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "%s iraun zuen" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Dei galdua" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Dei hau galdu duzu" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s(e)k dei hau galdu du" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Deia atzera bota da" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Dei horri uko egin diozu" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s(e)k uko egin dio dei horri" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Deia huts egin da" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "ordu %i" msgstr[1] "%i ordu" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "minutu %i" msgstr[1] "%i minutu" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "duela segundo batzuk" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Kontaktu honek bere kontaktuen zerrendara gehitu nahi zaitu" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%s deskargatzen…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s(e)k eskeini du: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fitxategia eskeini da: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fitxategia eskeinita" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Fitxategiaren transmisioak huts egin du" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fitxategia" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktuaren xehetasunak" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Mezua eguneratu" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Ezin izan da konexio seguru bat ezarri" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Atzera" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Zerbitzari publiko bat hautatu" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Edo zerbitzari baten helbidea zehaztu" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Saioa hasi" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Guztia ezarri da!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Amaitu" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Mezu berri bat heltzerakoan jakinarazi" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Bertako ezizena" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Orokorra" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Ez duzu irekitako txatik" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Elkarrizketa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Nabigazioa" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Jauzi hurrengo elkarrizketara" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Jauzi aurreko elkarrizketara" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Kontuak" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Goitizena" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Ezizena" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontaktua gehitu" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "XMPP txat bezero modernoa" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino mahaigainerako iturburu irekiko txat bezero moderno bat da. Jabber/XMPP " "esperientzia garbi eta fidagarri bat ematen du zure pribatutasuna kontuan " "hartzeaz gain." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Amaieratik amaierarako enkriptazioa onartzen du OMEMO eta OpenPGPrekin eta " "pribatutasun ezaugarriak konfiguratzea baimentzen du irakurtze markak eta " "idazketa jakinarazpenak bezala." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dinok zerbitzaritik hartzen du historia eta beste gailuekin mezuak " "sinkronizatzen ditu." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ez dago bilaketa aktiborik" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Idatzi bilatzen hasteko" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ez dago bat egiten duen mezurik" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ortografia egiaztatu edo saiatu iragazkiak kentzen" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Bidali" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Hobespenak" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Laster-teklak" #~ msgid "Remove" #~ msgstr "Kendu" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Hasi saioa %s(e)n" #~ msgid "No accounts configured" #~ msgstr "Ez dago konfiguratutako konturik" #~ msgid "Add an account" #~ msgstr "Kontu bat gehitu" #~ msgid "Pick another server" #~ msgstr "Beste zerbitzari bat hartu" #~ msgid "Local Settings" #~ msgstr "Bertako ezarpenak" #~ msgid "Notifications" #~ msgstr "Jakinarazpenak" #~ msgid "Only when mentioned" #~ msgstr "Soilik aipatua izaterakoan" #~ msgid "Permissions" #~ msgstr "Baimenak" #~ msgid "Conference Details" #~ msgstr "Konferentziaren xehetasunak" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Komunikazioa eta egoeraren eguneraketak edozein norabideetan blokeatuta " #~ "daude" #~ msgid "A password to restrict access to the room" #~ msgstr "Gelarako sarbidea murrizteko pasahitza" #~ msgid "Message history" #~ msgstr "Mezuen historia" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Gelak itzuliko dituen gehienezko historiako mezuak" #~ msgid "Convert smileys to emojis" #~ msgstr "Irrifartxoak emotikono bihurtu" #~ msgid "Check spelling" #~ msgstr "Ortografia egiaztatu" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klikatu hemen elkarrizketa berri bat hasi edo kanal batean sartzeko." #~ msgid "No active conversations" #~ msgstr "Ez dago solasaldi aktiborik" #~ msgid "Main window with conversations" #~ msgstr "Leiho nagusia elkarrizketekin" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s eta beste %i" #~ msgid "You can now start using %s" #~ msgstr "Orain %s erabiltzen hasi zaitezke" #~ msgid "Open Registration" #~ msgstr "Izen emate irekia" #~ msgid "Save" #~ msgstr "Gorde" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s eta %s" #~ msgid "%s and %s" #~ msgstr "%s eta %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "idazten ari da…" #~ msgstr[1] "idazten ari dira…" #~ msgid "has stopped typing" #~ msgstr "idazteari utzi dio" #~ msgid "%i search results" #~ msgstr "Bilaketaren emaitzak: %i" #~ msgid "Discover real JIDs" #~ msgstr "Egiazko JIDak ikusi" #~ msgid "Who may discover real JIDs?" #~ msgstr "Nork ikus ditzake egiazko JIDak?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Gelan sartzeko beharrezko pasahitza, utzi zuriz pasahitzik ez ezartzeko" #~ msgid "Failed connecting to %s" #~ msgstr "Huts %s(e)ra konektatzerakoan" #~ msgid "Join Conference" #~ msgstr "Konferentziara batu" #~ msgid "Communicate happiness." #~ msgstr "Poztasuna komunikatu." #~ msgid "Quit" #~ msgstr "Irten" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JIDak \"erabiltzailea@eredua.com\" itxura izan beharko luke" #~ msgid "Copy Link Address" #~ msgstr "Loturaren helbidea kopiatu" #~ msgid "Copy" #~ msgstr "Kopiatu" #~ msgid "Select All" #~ msgstr "Dena hautatu" #~ msgid "Search" #~ msgstr "Bilatu" #~ msgid "Send message marker" #~ msgstr "Mezu marka bidali" #~ msgid "Start Chat" #~ msgstr "Berriketa hasi" #~ msgid "Request presence updates" #~ msgstr "Presentzia eguneraketak eskatu" #~ msgid "Join on startup" #~ msgstr "Hasieran batu" #~ msgid "Add Chat" #~ msgstr "Berriketa gehitu" dino-0.5.0/main/po/fa.po0000664000000000000000000012122314776241610013446 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-03-12 11:00+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.12-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "لغو" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "اتصال" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "انتخاب آواتار" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "انتخاب" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "تصاویر" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "تمامی فایل‌ها" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "حساب %s حذف شود؟" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "در حال اتصال…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "متصل" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "قطع شده" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "رمز اشتباه است" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "گواهی تی‌ال‌اس نامعتبر است" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "خطا" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "حساب‌ها" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "اضافه کردن حساب" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "حساب فعالی وجود ندارد" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "ورود" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "سرور نمی تواند اثبات کند که %s است." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "گواهی امنیتی آن مورد اعنماد سیستم عامل شما نیست." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "گواهی امنیتی آن برای دامنه دیگری صادر شده است." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "گواهی امنیتی آن فقط در آینده معتبرخواهد شد." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "گواهی امنیتی آن منقضی شده است." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "ساخت کاربری" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "اکنون می توانید از این حساب استفاده کنید: %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "نام کاربری یا رمز اشتباه است" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "مشکلی پیش آمد" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "اتصال با %s برقرار نشد" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "آدرس نامعتبر" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "هیچ پاسخی از طرف سرور وجود ندارد" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "ثبت نام در %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "سرور نیاز به ثبت نام از طریق یک وب سایت دارد" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "بازکردن وب‌سایت" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "ثبت نام" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "بررسی %s برای کسب اطلاعات در مورد نحوه ثبت نام" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "مسدود کردن" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "تنظیمات" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "پیکربندی اتاق" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "اعضا" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "مالک" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "مدیر" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "عضو" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "حجم فایل از حداکثر حجم بارگذاری سرور بیشتر است." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "پیام خیلی طولانی است" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "ویرایش شده" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "رمزگذاری نشده" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "ارسال پیام ممکن نیست" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i دقیقه پیش" msgstr[1] "%i دقیقه پیش" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "همین الان" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "من" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "تصویر ارسال شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "فایل ارسال شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "تصویر دریافت شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "فایل دریافت شد" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "دیروز" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "آغاز گفتگو" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "کاربر" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "دعوت" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "گفتگوی خصوصی را شروع کنید" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "بیرون کردن" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "مجوز نوشتن بدهید" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "مجوز نوشتن را لغو کنید" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "دعوت کردن به کنفرانس" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "انتخاب فایل" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s از %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "نام اتاق" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "توضیحات اتاق" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "مداوم" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "بعد از رفتن آخرین فرد اتاق دوام خواهد داشت" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "قابلیت جستجوی عمومی" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "افراد ممکن است موضوع را تغییر دهند" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "اجازه مشاهده JIDها" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "چه کسی مجاز است JIDهای افراد را مشاهده کند؟" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "رمز" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "تعدیل شده" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "فقط افراد دارای صدا می توانند پیام ارسال کنند" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "فقط اعضا" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "فقط اعضا می توانند وارد اتاق شوند" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "این کنفرانس به شما اجازهٔ ارسال پیام نمی دهد." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "درخواست مجوز" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "ارسال فایل" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "جست‌جوی پیام‌ها" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "آغاز" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "پیوستن به کانال" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "بعدی" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "پیوستن" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "در حال پیوستن…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "گذرواژه برای ورود به اتاق لازم است" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "از پیوستن به کنفرانس یا ساختن آن منع شده‌اید" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "اتاق وجود ندارد" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "مجاز به ایجاد اتاق نیست" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "اتاق مخصوص اعضا" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "یک نام دیگر انتخاب کنید" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "تعداد زیادی از افراد در اتاق هستند" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "اضافه کردن" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "دربارهٔ Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "امروز" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a , %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i نتیجه جست وجو" msgstr[1] "%iنتایج جست وجو" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "در %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "با %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "رد کردن" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "پذیرش" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "درخواست اشتراک" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "رد کردن" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "دعوت به %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s شما را به %s دعوت کرد" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "درخواست مجوز" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s درخواست نوشتن در %s را دارد" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "ارسال اعلان تایپ کردن" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "فرستادن رسید خوانده‌شدن" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "روشن" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "خاموش" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "پیش‌فرض: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "درخواست" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "درخواست مجوز برای ارسال پیام" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "به دینو خوش آمدید!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "برای شروع یک حساب بسازید یا وارد شوید." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "حساب تنظیم کنید" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "مدیریت حساب‌ها" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s، %s و %i نفر دیگر در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s، %s و %s در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s و %s در حال نوشتن اند…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s در حال نوشتن است…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "این مخاطب می‌خواهد شما را به لیست مخاطبین خود اضافه کند" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "در حال دانلود %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ارائه شده: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "فایل ارائه شده: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "فایل ارائه شده است" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "انتقال فایل انجام نشد" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "اطلاعات مخاطب" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "به‌روز کردن پیام" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "اتصال ایمن برقرار نشد" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "بازگشت" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "یک سرور عمومی انتخاب کنید" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "یا آدرس سرور را مشخص کنید" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "در عوض وارد شوید" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "همه تنظیم شده!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "اتمام" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "اعلان در هنگام رسیدن پیام جدید" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "نام مستعار محلی" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "فراگیر" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "شما هیچ گپ بازی ندارید" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "گفتگو" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "جهت‌یابی" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "رفتن به گفت‌گوی بعدی" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "رفتن به گفت‌گوی قبلی" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "حساب" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "نام مستعار" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "نام مستعار" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "افزودن مخاطب" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "کلاینت نوین گپ XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "دینو یک کلاینت چت متن‌باز برای دسکتاپ است. تمرکز آن بر فراهم‌کردن تجربه‌ای " "شسته‌رفته و قابل‌اتکا از جَبِر/XMPP است درحالی که به حریم خصوصی‌تان اهمیت می‌دهد." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "از رمزگذاری سرتاسر با اُمیمو و اُپن‌پی‌جی‌پی پشتیبانی می‌کند و اجازه تنظیم " "قابلیت‌های مربوط به حریم خصوصی را می‌دهد، از جمله: رسید خوانده‌شدن پیام‌ها و " "اعلان در حال نوشتن بودن." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "دینو تاریخچه را از سرور دریافت می‌کند و پیام‌ها را با دیگر دستگاه‌ها همگام‌سازی " "می‌کند." #: main/data/global_search.ui:27 msgid "No active search" msgstr "جستجوی فعالی وجود ندارد" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "برای شروع جستجو تایپ کنید" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "هیچ پیام مطابقی وجود ندارد" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "املا را بررسی کنید یا فیلترها را حذف کنید" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "فرستادن" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "میانبر های صفحه کلید" #~ msgid "Remove" #~ msgstr "حذف" #, c-format #~ msgid "Sign in to %s" #~ msgstr "ورود به %s" #~ msgid "No accounts configured" #~ msgstr "هیچ حسابی پیکربندی نشده است" #~ msgid "Add an account" #~ msgstr "اضافه کردن حساب" #~ msgid "Pick another server" #~ msgstr "سرور دیگری انتخاب کنید" #~ msgid "Local Settings" #~ msgstr "تنظیمات محلی" #~ msgid "Notifications" #~ msgstr "اعلان‌ها" #~ msgid "Only when mentioned" #~ msgstr "فقط وقتی ذکر شده" #~ msgid "Permissions" #~ msgstr "مجوزها" #~ msgid "Conference Details" #~ msgstr "جزئیات کنفرانس" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "ارتباط و به‌روز رسانی وضعیت در هر دو جهت مسدود شده است" #~ msgid "A password to restrict access to the room" #~ msgstr "رمز برای محدود کردن دسترسی به اتاق" #~ msgid "Message history" #~ msgstr "تاریخچه پیام" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "حداکثر مقدار عقب مانده ارسال شده توسط اتاق" #~ msgid "Convert smileys to emojis" #~ msgstr "تبدیل صورتک‌ها به ایموجی" #~ msgid "Check spelling" #~ msgstr "بررسی املا" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "برای شروع گفتگو یا پیوستن به کانال اینجا کلیک کنید." #~ msgid "No active conversations" #~ msgstr "هیچ گفتگوی فعالی نیست" dino-0.5.0/main/po/fi.po0000664000000000000000000012250014776241610013455 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-12 18:09+0000\n" "Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Peru" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Yhdistä" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Poista tili käytöstä" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Ota tili käyttöön" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Valitse keskustelukuvake" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Valitse" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Kuvat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Kaikki tiedostot" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Poistetaanko tili %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Yhdistää…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Yhdistetty" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Ei yhdistetty" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Väärä salasana" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "TLS-varmenne ei kelpaa" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Virhe" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Tilit" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Käytöstä poistetut tilit" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Lisää tili" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Ei aktiivisia tilejä" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Kirjaudu sisään" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Palvelin ei voinut todistaa olevansa %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Käyttöjärjestelmäsi ei luota sen turvavarmenteeseen." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Sen turvavarmenne on myönnetty toiselle toimialueelle." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Sen varmenne tulee voimaan vasta tulevaisuudessa." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Sen turvavarmenne on vanhentunut." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Luo tili" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Voit nyt käyttää %s-tiliä." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Väärä käyttäjätunnus tai salasana" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Jokin meni pieleen" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Ei voitu yhdistää %s:een" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "VIrheellinen osoite" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Palvelin ei vastaa" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Rekisteröidy %s:lla" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Palvelin vaatii rekisteröitymään verkkosivuston kautta" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Avaa verkkosivusto" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Rekisteröity" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Tarkista %s ohjeet kirjautumiseen" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Estä käyttäjä" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Estä koko verkkotunnus" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Kumoa esto" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Kiinnitetty" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Kiinnitä" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Estetty" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Verkkotunnus estetty" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Estä" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Mykistä" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Ilmoitukset käytössä" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Ilmoitukset maininnoista" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Mykistetty" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Ilmoitukset pois käytöstä" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Tietoja" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Asetukset" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Huoneen asetukset" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Salaus" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Jäsenet" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Omistaja" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Ylläpitäjä" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Jäsen" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Tiedoston koko ylittää palvelimen määrittämän maksimikoon." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Viesti liian pitkä" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "muokattu" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "odottaa…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "toimitus epäonnistui" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Salaamaton" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Viestiä ei voi lähettää" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d. %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d. %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min sitten" msgstr[1] "%i min sitten" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Juuri nyt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Minä" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Kuva lähetetty" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Tiedosto lähetetty" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Kuva vastaanotettu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Tiedosto vastaanotettu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Lähtevä puhelu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Saapuva puhelu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d. %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Eilen" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Aloita keskustelu" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Käyttäjä" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Kutsu" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Aloita yksityiskeskustelu" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Potkaise pihalle" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Myönnä kirjoitusoikeus" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Kumoa kirjoitusoikeus" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Kutsu ryhmäkeskusteluun" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Valitse tiedosto" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s keskustelusta %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Huoneen nimi" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Huoneen kuvaus" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Pysyvä" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Huone säilyy viimeisen käyttäjän poistuttua" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Julkisesti löydettävissä" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Käyttäjät voivat vaihtaa aihetta" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Oikeus nähdä JID-tunnisteet" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kenen on sallittua nähdä osallistujien JID-tunnisteet?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Salasana" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderoitu" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Vain äänen saaneet käyttäjät voivat lähettää viestejä" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Vain jäsenille" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Vain jäsenet pääsevät huoneeseen" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Tämä konferenssi ei salli viestien lähettämistä." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Pyydä lupa" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Lähetä tiedosto" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Aloita puhelu" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Äänipuhelu" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videopuhelu" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Etsi viestejä" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Keskustelun tiedot" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Sulje keskustelu" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Vianjäljitystiedot" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Soitetaan…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Hälytetään…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s lopetti puhelun" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s kieltäytyi puhelusta" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamerat" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kameraa ei löytynyt." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonit" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofonia ei löytynyt." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Kaiuttimet" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Kaiuttimia ei löytynyt." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Kutsu puheluun" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Aloita" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Liity kanavalle" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Seuraava" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Liity" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Liitytään…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Huoneeseen vaaditaan salasana" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Estetty liittymästä tai luomasta ryhmäkeskustelua" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Huonetta ei ole olemassa" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Huoneen luomista ei sallita" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Huone vain jäsenille" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Valitse jokin muu nimimerkki" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Liikaa käyttäjiä huoneessa" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Lisää" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Tietoja Dinosta" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Tänään" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i hakutulos" msgstr[1] "%i hakutulosta" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Kanavalla %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s:n kanssa" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Saapuva videopuhelu" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Saapuva ryhmävideopuhelu" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Saapuva ryhmäpuhelu" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Hylkää" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Hyväksy" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Yhteyspyyntö" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Hylkää" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Kutsu %s:lle" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s kutsui sinut %s:lle" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Lupapyyntö" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s pyytää lupaa kirjoittaa %s:ssa" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Lähetä kirjoittamisilmoitukset" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Lähetä lukukuittaukset" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Käytössä" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Pois" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Oletus: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Pyyntö" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Pyydä lupaa lähettää viestejä" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Tervetuloa, tämä on Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Kirjaudu sisään tai luo tili päästäksesi alkuun." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Määritä tili" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Hallitse tilejä" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP-osoite" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Näyttönimi" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Aihe" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Muokkaa viestiä" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Sinä" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Lisää reaktio" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Avaa" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Tallenna nimellä…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ja %i muuta kirjoittaa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ja %s kirjoittavat…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s ja %s kirjoittavat…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s kirjoittaa…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Puhelu aloitettu" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Aloitettu %s sitten" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Käsittelit tämän puhelun toisella laitteella" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Puhelu päättyi" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Päättyi %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Kesto %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Puhelu vastaamatta" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Et vastannut tähän puheluun" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s ei vastannut tähän puheluun" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Kieltäydytty puhelusta" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Kieltäydyit tästä puhelusta" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s kieltäytyi tästä puhelusta" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Puhelu epäonnistui" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i tunti" msgstr[1] "%i tuntia" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuutti" msgstr[1] "%i minuuttia" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "muutama sekunti" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Toimitettu" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Luettu" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Lähetä pyyntö" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Et vastaanota tilapäivityksiä tältä henkilöltä vielä." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Tämä käyttäjä haluaisi lisätä sinut yhteystietoihinsa" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Tämä keskustelu ei tue reaktioita." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Vastaa" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Tämä keskustelu ei tue vastauksia." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Ladataan %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Ladataan %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Lähetetään %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "Tarjottu %s: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Tarjottu tiedosto: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Tarjottu tiedosto" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Tiedostonsiirto epäonnistui" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Tiedosto" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Uusi" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Yhteystiedot" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Päivitä viesti" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Kirjaudu" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Salattua yhteyttä ei voitu muodostaa" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Takaisin" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Valitse julkinen palvelin" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Tai määritä palvelimen osoite" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Kirjaudu sisään sen sijaan" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Kaikki valmista!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Valmis" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Lähetä _kirjoitusilmoitukset" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Lähetä _lukukuittaukset" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Ilmoitukset" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Ilmoita, kun uusi viesti saapuu" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Muunna hymiöt emojeiksi" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Paikallinen alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Yhteyden tila" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Poista tili" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Vaihda salasana" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Vaihda" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Nykyinen salasana" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Uusi salasana" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Vahvista salasana" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Yleinen" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Sinulla ei ole avoimia pikakeskusteluja" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Napsauta + aloittaaksesi keskustelun tai liittyäksesi kanavalle" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Näytä asetukset" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Pikanäppäimet" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Keskustelu" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Liikkuminen" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Siirry seuraavaan keskusteluun" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Siirry edelliseen keskusteluun" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Tili" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nimimerkki" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Lisää yhteystieto" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Nykyaikainen XMPP-asiakasohjelma" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino on nykyaikainen avoimen lähdekoodin keskusteluohjelma työpöydälle. Se " "tarjoaa selkeän ja luotettavan Jabber/XMPP-kokemuksen huomioiden " "yksityisyytesi." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Se tukee päästä päähän -salausta OMEMO:n ja OpenPGP:n avulla ja mahdollistaa " "yksityisyyteen liittyvien ominaisuuksien, kuten lukukuittausten ja " "kirjoitusilmoitusten asetusten määrittämisen." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino hakee historian palvelimelta ja synkronoi viestit muiden laitteiden " "kanssa." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ei aktiivista hakua" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Kirjoita haun aloittamiseksi" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ei vastaavia viestejä" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Tarkista oikeinkirjoitus tai yritä poistaa suodattimia" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Lähetä" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Asetukset" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Pikanäppäimet" #~ msgid "Remove" #~ msgstr "Poista" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Kirjaudu tilille %s" #~ msgid "No accounts configured" #~ msgstr "Ei määritettyjä tilejä" #~ msgid "Add an account" #~ msgstr "Lisää tili" #~ msgid "Pick another server" #~ msgstr "Valitse toinen palvelin" #~ msgid "Local Settings" #~ msgstr "Paikalliset asetukset" #~ msgid "Notifications" #~ msgstr "Ilmoitukset" #~ msgid "Pin conversation" #~ msgstr "Kiinnitä keskustelu" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Kiinnittää keskustelun keskusteluluettelon yläosaan" #~ msgid "Only when mentioned" #~ msgstr "Vain mainittaessa" #~ msgid "Permissions" #~ msgstr "Oikeudet" #~ msgid "Conference Details" #~ msgstr "Ryhmäkeskustelun yksityiskohdat" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Viestit ja tilapäivitykset estetään molempiin suuntiin" #~ msgid "A password to restrict access to the room" #~ msgstr "Salasana pääsyn huoneeseen rajoittamiseksi" #~ msgid "Message history" #~ msgstr "Viestihistoria" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Säilytettyjen viestien enimmäismäärä huoneessa" #~ msgid "Convert smileys to emojis" #~ msgstr "Muuta hymiöt emojeiksi" #~ msgid "Check spelling" #~ msgstr "Tarkista oikeinkirjoitus" #~ msgid "No active conversations" #~ msgstr "Ei aktiivisia keskusteluja" #~ msgid "Main window with conversations" #~ msgstr "Keskustelut pääikkunassa" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s and %i muuta" #~ msgid "Save" #~ msgstr "Tallenna" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ja %s" #~ msgid "%s and %s" #~ msgstr "%s ja %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "kirjoittaa…" #~ msgstr[1] "kirjoittavat…" #~ msgid "has stopped typing" #~ msgstr "on lakannut kirjoittamasta" #~ msgid "Discover real JIDs" #~ msgstr "Näytä oikeat JID:t" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kuka voi nähdä oikeat JID:t?" #~ msgid "Password required for room entry, if any" #~ msgstr "Huoneeseen vaadittava salasana, jos asetettu" #~ msgid "Join Conference" #~ msgstr "Liity ryhmäkeskusteluun" #~ msgid "Quit" #~ msgstr "Lopeta" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Anna JID muodossa \"user@example.com\"" #~ msgid "Copy Link Address" #~ msgstr "Kopioi linkin osoite" #~ msgid "Copy" #~ msgstr "Kopioi" #~ msgid "Select All" #~ msgstr "Valitse kaikki" #~ msgid "Search" #~ msgstr "Etsi" #~ msgid "Send message marker" #~ msgstr "Lähetä lukukuittaus" #~ msgid "Start Chat" #~ msgstr "Aloita keskustelu" dino-0.5.0/main/po/fr.po0000664000000000000000000012624714776241610013502 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-25 14:09+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annuler" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Se connecter" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Désactiver ce compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activer ce compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Sélectionner une image de profil" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Sélectionner" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Images" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Tous les fichiers" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Supprimer le compte %s ?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Connexion…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Connecté" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Déconnecté" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Mauvais mot de passe" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificat TLS invalide" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Erreur" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Comptes" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Comptes désactivés" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Ajouter un compte" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Aucun compte actif" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Connexion" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Le serveur n’a pas su prouver qu’il est %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Votre système d’exploitation ne fait pas confiance au certificat proposé." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Son certificat a été généré pour un autre domaine." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Son certificat ne deviendra valide que dans le futur." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Son certificat a expiré." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Créer un compte" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Vous pouvez maintenant utiliser le compte %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Mauvais nom d’utilisateur·ice ou mot de passe" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Quelque chose s’est mal passé" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Impossible de se connecter à %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Adresse invalide" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Pas de réponse du serveur" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "S’inscrire sur %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Ce serveur nécessite de s’inscrire depuis un site Web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Ouvrir le site web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "S’inscrire" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Consultez %s pour plus d’informations sur comment s’inscrire" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloquer cet utilisateur·ice" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloquer le domaine entier" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Débloquer" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Épinglé" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Épingler" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqué·e" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domaine bloqué" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloquer" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Rendre muet" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notifications activées" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notifications pour les mentions" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Muet" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notifications désactivées" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "À propos" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Paramètres" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuration du salon" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Chiffrement" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Propriétaire" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrateur·ice" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Le fichier dépasse la taille maximale autorisée par le serveur." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Message trop long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "modifié" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "en cours d’envoi…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "échec de l’envoi" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Non chiffré" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Impossible d’envoyer le message" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "le %x à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "le %x à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "le %d %b à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "le %d %b à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a à %Hh %M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a à %lh %M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Il y a %i minute" msgstr[1] "Il y a %i minutes" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "À l’instant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Moi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Image envoyée" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fichier envoyé" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Image reçue" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fichier reçu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Appel sortant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Appel entrant" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Hier" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Commencer une discussion" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilisateur·ice" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviter" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Commencer une discussion privée" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Éjecter du salon" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Accorder la permission d’écrire" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Révoquer la permission d’écrire" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviter dans ce salon" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Sélectionner un fichier" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nom du salon" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Description du salon" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistant" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Ce salon persistera après le départ du dernier participant" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Visible publiquement" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Les participant·es peuvent changer le sujet" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Autorisation de voir les adresses XMPP" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui peut voir les adresses XMPP des participant·es ?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Mot de passe" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Modéré" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" "Seuls les participant·es avec la permission d’écrire peuvent envoyer des " "messages" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Membres uniquement" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Seuls les membres peuvent accéder au salon" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Vous n’êtes pas autorisé·e à envoyer de messages sur ce salon." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demander la permission" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Envoyer un fichier" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Démarrer un appel" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Appel audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Appel vidéo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Rechercher dans les messages" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Détails de cette conversation" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Fermer cette conversation" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informations de débogage" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Appel en cours…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Ça sonne…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s a arrêté l’appel" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s a rejeté l’appel" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Caméras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Aucune caméra trouvée." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microphones" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Aucun microphone trouvé." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Haut-parleurs" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Aucun haut-parleur trouvé." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviter à un appel" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Démarrer" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Rejoindre un salon" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Suivant" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Rejoindre" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Connexion au salon…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Un mot de passe est nécessaire pour rejoindre ce salon" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Vous n’avez pas le droit de rejoindre ou de créer ce salon" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Ce salon n’existe pas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Création de salon interdite" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Salon réservé aux membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Choisissez un autre pseudo" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Trop de participant·es dans ce salon" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ajouter" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "À propos de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Aujourd’hui" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i résultat" msgstr[1] "%i résultats" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Sur %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Avec %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Appel vidéo entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Appel vidéo de groupe entrant" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Appel de groupe entrant" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejeter" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accepter" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Demande d’ajout de contact" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Refuser" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitation à %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s vous a invité·e à rejoindre le salon %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Demande de permission" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demande la permission d’écrire dans %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Envoyer les notifications d’écriture" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Envoyer les accusés de réception" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Activé" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Désactivé" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Par défaut : %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Demander" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Demander la permission d’envoyer des messages" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Bienvenue dans Dino !" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Connectez-vous ou créez un compte pour commencer." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurer un compte" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gérer les comptes" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adresse XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nom d’affichage" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Sujet" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Éditer le message" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Vous" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Ajouter une réaction" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ouvrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Enregistrer sous…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s et %i autres sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s et %s sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s et %s sont en train d’écrire…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s est en train d’écrire…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Appel démarré" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "A commencé il y a %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Vous avez réagi à cet appel depuis un autre client" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Appel terminé" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "S’est terminé à %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "A duré %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Appel manqué" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Vous avez manqué cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s a manqué cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Appel rejeté" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Vous avez rejeté cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s a rejeté cet appel" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Appel échoué" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i heure" msgstr[1] "%i heures" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minute" msgstr[1] "%i minutes" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "quelques secondes" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Reçu" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Lu" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Envoyer une requête" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Vous ne recevez pas encore les changements de statuts de ce contact." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Cette personne souhaiterait vous ajouter à sa liste de contacts" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Cette conversation ne gère pas les réactions." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Répondre" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Cette conversation ne gère pas les réponses." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Téléchargement de %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Téléchargement de %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "%s envoyés… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s proposé au téléchargement : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fichier proposé au téléchargement : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fichier proposé au téléchargement" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Échec du transfert de fichier" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fichier" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nouveau" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Informations du contact" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Modifier ce message" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Se connecter" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Impossible d’établir une connexion sécurisée" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Retour" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Choisissez un serveur public" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ou indiquez l’adresse d’un serveur" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Annuler et se connecter" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tout est prêt !" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Terminer" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Envoyer les notifications d’écriture" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Envoyer les _accusés de réception" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notifications" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notifier de l’arrivée de nouveaux messages" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Convertir les smileys en emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Statut de connexion" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Supprimer ce compte" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Changement de mot de passe" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Changer" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Mot de passe actuel" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Nouveau mot de passe" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Confirmation du mot de passe" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Général" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vous n’avez aucune discussion ouverte" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Cliquez sur + pour commencer une conversation ou rejoindre un salon" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Afficher les préférences" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Raccourcis clavier" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Discussion" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigation" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Aller à la discussion suivante" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Revenir à la discussion précédente" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Pseudo" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Ajouter un contact" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Client de discussion XMPP moderne" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino est un client de discussion libre et moderne pour ordinateur. Il se " "concentre sur le fait d’offrir une expérience XMPP simple et fiable tout en " "ayant toujours à l’esprit votre vie privée." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Il prend en charge le chiffrement de bout en bout avec OMEMO et OpenPGP et " "permet de configurer les fonctions liées à la confidentialité telles que les " "accusés de réception et les notifications d’écriture." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino récupère l’historique du serveur et synchronise les messages avec les " "autres clients." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Pas de recherche en cours" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Écrivez pour lancer une recherche" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Aucun message correspondant à la recherche" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Vérifiez l’orthographe ou essayez de supprimer des filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Envoyer" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Préférences" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Raccourcis clavier" #~ msgid "Remove" #~ msgstr "Supprimer" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Se connecter en tant que %s" #~ msgid "No accounts configured" #~ msgstr "Aucun compte configuré" #~ msgid "Add an account" #~ msgstr "Ajouter un compte" #~ msgid "Pick another server" #~ msgstr "Choisir un autre serveur" #~ msgid "Local Settings" #~ msgstr "Paramètres locaux" #~ msgid "Notifications" #~ msgstr "Notifications" #~ msgid "Pin conversation" #~ msgstr "Épingler la conversation" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Épingle la conversation en haut de la liste des conversations" #~ msgid "Only when mentioned" #~ msgstr "Seulement quand mentionné" #~ msgid "Permissions" #~ msgstr "Permissions" #~ msgid "Conference Details" #~ msgstr "Informations du salon" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Tous les échanges de messages et de statuts sont bloqués" #~ msgid "A password to restrict access to the room" #~ msgstr "Un mot de passe pour restreindre l’accès au salon" #~ msgid "Message history" #~ msgstr "Historique des messages" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Nombre maximum de messages d’historique renvoyé par le salon" #~ msgid "Convert smileys to emojis" #~ msgstr "Convertir les émoticônes typographiques en emojis" #~ msgid "Check spelling" #~ msgstr "Vérifier l'orthographe" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Cliquez ici pour commencer une discussion ou rejoindre un salon." #~ msgid "No active conversations" #~ msgstr "Aucune discussion en cours" #~ msgid "Main window with conversations" #~ msgstr "Fenêtre principale avec des conversations" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s et %i autres" #~ msgid "You can now start using %s" #~ msgstr "Vous pouvez maintenant utiliser %s" #~ msgid "Open Registration" #~ msgstr "Inscription ouverte" #~ msgid "Save" #~ msgstr "Sauver" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s et %s" #~ msgid "%s and %s" #~ msgstr "%s et %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "est en train d’écrire…" #~ msgstr[1] "sont en train d’écrire…" #~ msgid "has stopped typing" #~ msgstr "a arrêté d’écrire" #~ msgid "%i search results" #~ msgstr "%i résultats de recherche" #~ msgid "Discover real JIDs" #~ msgstr "Découverte des vrais JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui peut découvrir les vrais JID ?" #~ msgid "Password required for room entry, if any" #~ msgstr "Mot de passe pour rejoindre le salon, vide pour aucun" #~ msgid "Failed connecting to %s" #~ msgstr "Échec de la connexion à %s" #~ msgid "Join Conference" #~ msgstr "Rejoindre une conférence" #~ msgid "Communicate happiness." #~ msgstr "Communiquer avec bonheur." #~ msgid "Quit" #~ msgstr "Quitter" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Le JID doit être de la forme « utilisateur@example.com »" #~ msgid "Copy Link Address" #~ msgstr "Copier l’adresse du lien" #~ msgid "Copy" #~ msgstr "Copier" #~ msgid "Select All" #~ msgstr "Tout sélectionner" #~ msgid "Search" #~ msgstr "Rechercher" #~ msgid "Send message marker" #~ msgstr "Envoyer les marqueurs de message" #~ msgid "Start Chat" #~ msgstr "Commencer une discussion" #~ msgid "Request presence updates" #~ msgstr "Demander les mises à jour de la présence" #~ msgid "Join on startup" #~ msgstr "Rejoindre au démarrage" #~ msgid "Add Chat" #~ msgstr "Ajouter discussion" dino-0.5.0/main/po/gl.po0000664000000000000000000012431214776241610013464 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 23:17+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Acceder" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desactivar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Escoller imaxe do perfil" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Escoller" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imaxes" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Tódolos ficheiros" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Eliminar a conta %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conectando…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Contrasinal incorrecto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificado TLS non válido" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Erro" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Contas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Contas desactivadas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Engadir conta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Sen contas activas" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Accede" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor non puido probar que é %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O teu sistema non estableceu a confianza no seu certificado de seguridade." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "O seu certificado de seguridade foi proporcionado para outro dominio." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "O seu certificado de seguridade só terá validez no futuro." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "O certificado de seguridade caducou." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crear unha conta" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Agora é posible usar a conta %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Usuario ou contrasinal incorrectos" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Algo fallou" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Non se conectou a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Enderezo non válido" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Sen resposta do servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Rexistrarse en %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "O servidor precisa que te rexistres a través dun sitio web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Abrir sitio web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Rexistrar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Olla en %s para máis información sobre o rexistro" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloquear usuaria" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloquear todo o dominio" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Desbloquear" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fixada" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fixar" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqueada" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Dominio bloqueado" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloquear" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Acalar" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificacións activadas" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificación de mencións" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Acalada" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificacións desactivadas" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Sobre" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Axustes" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Axustes da sala" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Cifraxe" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membresía" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "O ficheiro excede o tamaño máximo de subida permitido." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mensaxe demasiado longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "pendente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "fallou a entrega" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sen cifrar" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Imposible enviar a mensaxe" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fai %i min" msgstr[1] "fai %i minutos" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Xusto agora" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imaxe enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Ficheiro enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imaxe recibida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Ficheiro recibido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Chamada saínte" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Chamada entrante" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Onte" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Comezar conversa" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuario" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Comezar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Botar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permiso para escribir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar o permiso de escritura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar ó grupo" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Escolle ficheiro" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrición da sala" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistente" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "A sala permanecerá despois de que saia o derradeiro participante" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Pódese atopar de xeito público" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Os participantes poden muda-lo asunto" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permiso para ollar JIDs" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quen pode ollar os JIDs dos participantes?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Contrasinal" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderado" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Só os participantes con voz poden enviar mensaxes" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Só membros" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Só os membros poden entrar na sala" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Non tes permiso para escribir en esta sala de conferencia." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicita permiso" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Enviar ficheiro" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Buscar mensaxes" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detalles da conversa" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Fechar conversa" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Información de depuración" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Chamando…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Soando…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s rematou a chamada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s rexeitou a chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Cámaras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Non se atopa unha cámara." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Micrófonos" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Non se atopa o micrófono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altofalantes" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Non se atopa o altofalante." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar a Chamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Comezar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Unirse a unha canle" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Seguinte" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Unirse" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Uníndose…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Precisas dun contrasinal para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Non ten permiso para unirse ou crea-lo grupo" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "A sala non existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Non pode crea-la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala só para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolle un alcume diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Demasiada xente na sala" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Engadir" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Sobre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoxe" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d de %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da procura" msgstr[1] "%i resultados da procura" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Chamada de vídeo entrante" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Chamada de vídeo en grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Chamada en grupo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rexeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Solicitude de subscrición" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Negar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Convite a %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s convidoute a %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Solicitude de permiso" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s pide permiso para escribir en %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Mostrar que estás escribindo" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Enviar confirmacións de lectura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Activado" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Desactivado" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Por defecto: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Solicitar" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Solicita permiso para enviar mensaxes" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Benvida a Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Acceder ou crear unha conta para comezar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurar conta" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Xestionar contas" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Enderezo XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nome mostrado" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Asunto" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Editar mensaxe" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ti" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Engadir reacción" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Gardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e outros %i están escribindo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s están a escribir…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s están a escribir…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s está a escribir…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Comezou a chamada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Iniciada hai %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Atendeches a chamada noutro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Chamada finalizada" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Rematou ás %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durou %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Chamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Perdeches esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Chamada rexeitada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Rexeitaches esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s rexeitou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Fallou a chamada" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i horas" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "uns poucos segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Entregado" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Ler" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Enviar solicitude" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Aínda non recibes actualizacións de estado deste contacto." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Esta persoa gostaríalle engadirte á súa listaxe dos contactos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Esta conversa non admite reaccións." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Responder" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Esta conversa non admite respostas." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Descargando %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Descargando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Subindo %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofreceuche: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Ficheiro ofrecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Ficheiro ofrecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Fallou a transferencia do ficheiro" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Ficheiro" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Novidade" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalles do contacto" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar mensaxe" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Acceder" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Non se estableceu unha conexión segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Atrás" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Escoller un servidor público" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ou indicar un enderezo de servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Utiliza conta existente" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Todo feito!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Rematar" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Permitir ver que _Escribes" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Enviar Comprobante de _Lectura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificacións" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notificar cando chega unha nova mensaxe" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Converter Sorrisos a Emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alcume local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Estado da conexión" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Retirar conta" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Cambiar contrasinal" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Cambiar" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Contrasinal actual" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Novo contrasinal" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Confirmar contrasinal" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Xeral" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Non tes conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Preme no + para comezar unha conversa ou unirte a unha canle" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Mostrar os axustes" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Atallos do teclado" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegación" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ir a seguinte conversa" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ir a conversa anterior" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Sobrenome" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alcume" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Engadir contacto" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Cliente moderno para conversas XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é un cliente moderno e de código aberto para o escritorio. Orientado a " "fornecer unha experiencia Jabber/XMPP limpa e fiábel tendo a privacidade e " "seguranza presentes." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suporta a cifraxe de punto-a-punto con OMEMO e OpenPGP e permite configurar " "funcións orientadas á privacidade tales coma confirmación de lectura e " "notificacións de escritura." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtén o histórico dende o servidor e sincroniza as mensaxes con outros " "dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sen procura activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escriba para comezar unha procura" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sen mensaxes coincidentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Comprobe a escrita ou tente eliminar filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferencias" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Atallos de teclado" #~ msgid "Remove" #~ msgstr "Eliminar" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Acceder con %s" #~ msgid "No accounts configured" #~ msgstr "Sen contas configuradas" #~ msgid "Add an account" #~ msgstr "Engadir unha conta" #~ msgid "Pick another server" #~ msgstr "Escoller outro servidor" #~ msgid "Local Settings" #~ msgstr "Axustes locais" #~ msgid "Notifications" #~ msgstr "Notificacións" #~ msgid "Pin conversation" #~ msgstr "Fixar a conversa" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fixa a conversa na parte superior da lista de conversas" #~ msgid "Only when mentioned" #~ msgstr "Só cando te mencionan" #~ msgid "Permissions" #~ msgstr "Permisos" #~ msgid "Conference Details" #~ msgstr "Detalles do grupo" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "As actualizacións do estado e comunicación están bloqueadas en calquera " #~ "dirección" #~ msgid "A password to restrict access to the room" #~ msgstr "Un contrasinal para restrinxir o acceso á sala" #~ msgid "Message history" #~ msgstr "Histórico das mensaxes" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Morea máxima de rexistros fornecidos pola sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Converter risoños a emojis" #~ msgid "Check spelling" #~ msgstr "Comprobar ortografía" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Preme aquí para iniciar unha conversa ou unirte a unha canle." #~ msgid "Video call incoming" #~ msgstr "Chamada de vídeo entrante" #~ msgid "Call incoming" #~ msgstr "Chamada entrante" #~ msgid "Establishing call" #~ msgstr "Establecendo chamada" #~ msgid "Video call establishing" #~ msgstr "Establecendo chamada de vídeo" #~ msgid "Call establishing" #~ msgstr "Establecendo chamada" #~ msgid "Call in progress…" #~ msgstr "Chamada en curso…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Durou %s" #~ msgid "seconds" #~ msgstr "segundos" #~ msgid "No active conversations" #~ msgstr "Sen conversas activas" #~ msgid "Main window with conversations" #~ msgstr "Lapela principal con conversas" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i outros" #~ msgid "You can now start using %s" #~ msgstr "Agora xa podes comezar a utilizar %s" #~ msgid "Open Registration" #~ msgstr "Rexistro aberto" #~ msgid "Save" #~ msgstr "Gardar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está a escribir…" #~ msgstr[1] "están a escribir…" #~ msgid "has stopped typing" #~ msgstr "deixou de escribir" #~ msgid "%i search results" #~ msgstr "%i resultados da procura" #~ msgid "Discover real JIDs" #~ msgstr "Descobri-los JIDs reais" #~ msgid "Who may discover real JIDs?" #~ msgstr "Quen pode descobri-los JIDs reais?" #~ msgid "Password required for room entry, if any" #~ msgstr "Contrasinal precisada para entrar na sala, se houbese" #~ msgid "Failed connecting to %s" #~ msgstr "Ocorreu un erro ó conectar cara %s" #~ msgid "Join Conference" #~ msgstr "Unirse a un grupo" #~ msgid "Communicate happiness." #~ msgstr "Espalla a felicidade." #~ msgid "Quit" #~ msgstr "Saír" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "O JID debe te-lo formato \"usuario@exemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar enderezo da ligazón" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Escoller todo" dino-0.5.0/main/po/hi.po0000664000000000000000000013243014776241610013462 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-12 18:09+0000\n" "Language-Team: none\n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "रद्द करें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "जुड़ें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "खाता अक्षम करें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "खाता सक्षम करें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "अवतार चुनें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "चुनें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "छवियां" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "सभी फाइलें" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "खाता %s हटाएं?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "कनेक्ट हो रहा है…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "कनेक्टेड" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "डिस्कनेक्टेड" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "गलत पासवर्ड" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "अमान्य TLS प्रमाणपत्र" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "त्रुटि" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "खाते" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "अक्षम खाते" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "खाता जोड़ें" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "कोई सक्रिय खाता नहीं" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "साइन इन" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "सर्वर यह साबित नहीं कर सका कि यह %s है।" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "इसका सुरक्षा प्रमाणपत्र आपके ऑपरेटिंग सिस्टम द्वारा विश्वसनीय नहीं है।" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "इसका सुरक्षा प्रमाणपत्र दूसरे डोमेन को जारी किया जाता है।" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "इसका सुरक्षा प्रमाणपत्र भविष्य में ही मान्य होगा।" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "इसका सुरक्षा प्रमाणपत्र समाप्त हो गया है।" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "खाता बनाएं" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "अब आप %s खाते का उपयोग कर सकते हैं।" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "गलत उपयोक्ता नाम या पासवर्ड" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "कुछ गलत हुआ" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "%s से जुड़ नहीं सके" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "अमान्य पता" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "सर्वर से कोई प्रतिक्रिया नहीं" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "%s पर पंजीकरण करें" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "सर्वर को एक वेबसाइट के माध्यम से साइन अप करने की आवश्यकता होती है" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "वेबसाइट खोलें" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "पंजीकृत करें" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "साइन अप करने के तरीके के बारे में जानकारी के लिए %s देखें" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "पिन किए गए" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "पिन" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "अवरुद्ध" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "अवरुद्ध करें" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "मूक" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "अधिसूचनाएं सक्षम" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "उल्लेखों के लिए अधिसूचनाएं" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "मूक किया गया" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "अधिसूचनाएं अक्षम" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "परिचय" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "सेटिंग" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "कक्ष विन्यास" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "कूटलेखन" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "सदस्य" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "संचालक" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "प्रबंधक" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "सदस्य" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "फाइल सर्वर के अधिकतम अपलोड आकार से अधिक है।" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "संदेश बहुत लंबा है" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "संपादित" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "लंबित…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "डिलीवरी विफल" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "अकूटलेखित" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "संदेश भेजने में असमर्थ" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i मिनट पहले" msgstr[1] "%i मिनट पहले" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "बस अभी" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "मैं" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "छवि भेजी गई" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "फाइल भेजी गई" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "छवि प्राप्त हुई" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "फाइल प्राप्त हुई" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "जावक कॉल" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "आवक कॉल" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "बिता कल" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "बातचीत प्रारंभ करें" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "उपयोक्ता" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "आमंत्रित करें" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "निजी बातचीत शुरू करें" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "बाहर निकालें" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "लेखन अनुमति दें" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "लेखन अनुमति रद्द करें" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "सम्मेलन में आमंत्रित करें" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "फाइल चुनें" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s द्वारा %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "कक्ष का नाम" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "कक्ष का विवरण" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "बरकरार" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "अंतिम सदस्य के जाने के बाद भी कक्ष बना रहेगा" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "सार्वजनिक रूप से खोज योग्य" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "सदस्य विषय बदल सकते हैं" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JID देखने की अनुमति" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "सदस्यों की JID देखने की अनुमति किसे है?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "पासवर्ड" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "नियंत्रित" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "केवल वॉयस वाले लोग ही संदेश भेज सकते हैं" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "केवल सदस्य" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "केवल सदस्य ही कक्ष में प्रवेश कर सकते हैं" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "यह कॉन्फ़्रेंस आपको संदेश भेजने की अनुमति नहीं देती है।" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "अनुमति मांगें" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "फाइल भेजें" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "कॉल शुरू करें" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "ऑडियो कॉल" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "वीडियो कॉल" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "संदेश खोजें" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "बातचीत का विवरण" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "बातचीत बंद करें" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "डिबग जानकारी" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "कॉलिंग…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "घंटी बज रही है…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s ने कॉल समाप्त कर दी" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s ने कॉल को काट दिया" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "कैमरा" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "कैमरा नहीं मिला।" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "माइक्रोफोन" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "कोई माइक्रोफोन नहीं मिला।" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "स्पीकर" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "कोई स्पीकर नहीं मिला।" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "कॉल हेतु आमंत्रित करें" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "शुरु" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "चैनल से जुड़ें" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "अगला" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "जुड़ें" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "जुड़ रहे हैं…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "कक्ष में प्रवेश के लिए पासवर्ड आवश्यक है" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "कॉन्फ़्रेंस में शामिल होने या बनाने पर प्रतिबंध लगा दिया गया है" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "कक्ष मौजूद नहीं है" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "कक्ष बनाने की अनुमति नहीं है" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "केवल सदस्यों के लिए कक्ष" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "कोई अलग उपनाम चुनें" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "कक्ष में बहुत अधिक लोग हैं" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "जोड़ें" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "डिनो के बारे में" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "आज" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i खोज परिणाम" msgstr[1] "%i खोज परिणाम" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s में" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s के साथ" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "आवक वीडियो कॉल" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "आवक वीडियो समूह कॉल" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "आवक समूह कॉल" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "अस्वीकारें" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "स्वीकारें" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "सदस्यता अनुरोध" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "अस्वीकारें" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s को आमंत्रण" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s ने आपको %s पर आमंत्रित किया" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "अनुमति अनुरोध" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, fuzzy, c-format msgid "%s requests the permission to write in %s" msgstr "%1$s, %2$s में लिखने की अनुमति का अनुरोध करता है" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "टाइपिंग सूचनाएं भेजें" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "पठन रसीदें भेजें" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "चालू" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "बंद" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "तयशुदा: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "अनुरोध" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "संदेश भेजने की अनुमति का अनुरोध करें" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "डिनो में स्वागत है!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "आरंभ करने के लिए साइन इन करें या खाता बनाएं।" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "खाता स्थापित करें" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "खाते प्रबंधित करें" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP पता" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "प्रदर्शित नाम" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "विषय" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "संदेश संपादित करें" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "आप" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "प्रतिक्रिया जोड़ें" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "खोलें" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "ऐसे सहेजें…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s और %i अन्य लोग टाइप कर रहे हैं…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s और %s टाइप कर रहे हैं…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s और %s टाइप कर रहे हैं…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s टाइप कर रहा है…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "कॉल शुरू हुई" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s पहले शुरू हुआ" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "आपने यह कॉल किसी अन्य डिवाइस पर संभाली" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "कॉल समाप्त" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%s पर समाप्त हुआ" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "%s तक चला" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "कॉल छूट गई" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "आपसे यह कॉल छूट गई" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s से यह कॉल छूट गई" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "कॉल अस्वीकृत" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "आपने यह कॉल अस्वीकार कर दिया" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s ने इस कॉल को अस्वीकार कर दिया" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "कॉल विफल" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i घंटा" msgstr[1] "%i घंटे" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i मिनट" msgstr[1] "%i मिनट" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "कुछ सेकंड" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "वितरित" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "पठित" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "यह संपर्क आपको अपनी संपर्क सूची में जोड़ना चाहता है" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "यह बातचीत प्रतिक्रियाओं का समर्थन नहीं करती।" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "जवाब" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "यह बातचीत जवाब का समर्थन नहीं करती।" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%s डाउनलोड हो रहा है…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s पेश: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "फाइल पेश: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "फाइल पेश" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "फाइल स्थानांतरण विफल" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "फाइल" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "नया" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "संपर्क विवरण" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "संदेश अपडेट करें" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "सुरक्षित कनेक्शन स्थापित नहीं किया जा सका" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "पीछे" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "सार्वजनिक सर्वर चुनें" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "या सर्वर पता निर्दिष्ट करें" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "इसके बजाय साइन इन करें" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "सब तैयार!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "समापन" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "टाइपिंग अधिसूचना भेजें (_T)" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "पठन रसीद भेजें (_R)" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "अधिसूचनाएं (_N)" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "नया संदेश आने पर सूचित करें" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "स्माइलीज को इमोजी में बदलें (_C)" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "स्थानीय उपनाम" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "कनेक्शन स्थिति" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "खाता हटाएं" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "सामान्य" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "आपके पास कोई खुली चैट नहीं है" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "चैट शुरू करने या चैनल से जुड़ने के लिए + पर क्लिक करें" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "कीबोर्ड शॉर्टकट" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "वार्तालाप" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "नेविगेशन" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "अगली बातचीत पर जाएं" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "पिछली बातचीत पर जाएं" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "खाता" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "उपनाम" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "उपनाम" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "संपर्क जोड़ें" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "आधुनिक XMPP चैट क्लाइंट" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "डिनो डेस्कटॉप के लिए एक आधुनिक ओपन-सोर्स चैट क्लाइंट है। यह आपकी गोपनीयता को ध्यान में " "रखते हुए एक स्वच्छ और विश्वसनीय जैबर/एक्सएमपीपी अनुभव प्रदान करने पर केंद्रित है।" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "यह OMEMO और OpenPGP के साथ शुरू-से-अंत कूटलेखन का समर्थन करता है और गोपनीयता-संबंधी " "सुविधाओं जैसे कि पठन रसीद और टाइपिंग अधिसूचनाओं को विन्यस्त करने की अनुमति देता है।" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "डिनो सर्वर से इतिहास प्राप्त करता है और संदेशों को अन्य डिवाइसों के साथ समन्वयित करता है।" #: main/data/global_search.ui:27 msgid "No active search" msgstr "कोई सक्रिय खोज नहीं" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "खोज शुरू करने के लिए टाइप करें" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "कोई मेल खाता संदेश नहीं" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "वर्तनी जांचें या फिल्टर हटाने का प्रयास करें" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "भेजें" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "प्राथमिकताएं" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "कीबोर्ड शॉर्टकट" #~ msgid "Remove" #~ msgstr "हटाएं" #, c-format #~ msgid "Sign in to %s" #~ msgstr "%s में साइन इन करें" #~ msgid "No accounts configured" #~ msgstr "कोई खाता विन्यस्त नहीं" #~ msgid "Add an account" #~ msgstr "खाता जोड़ें" #~ msgid "Pick another server" #~ msgstr "कोई अन्य सर्वर चुनें" dino-0.5.0/main/po/hu.po0000664000000000000000000012521414776241610013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-14 13:43+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Mégse" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Kapcsolódás" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Fiók letiltása" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Fiók engedélyezése" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Profilkép kiválasztása" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Kiválasztás" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Képek" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Összes fájl" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Eltávolítja a(z) %s fiókot?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Kapcsolódás…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Kapcsolódva" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Leválasztva" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Hibás jelszó" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Érvénytelen TLS-tanúsítvány" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Hiba" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Fiókok" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Letiltott fiókok" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Fiók hozzáadása" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Nincsenek aktív fiókok" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Bejelentkezés" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "A kiszolgáló nem tudta bizonyítani, hogy ez %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Az operációs rendszer nem bízik a biztonsági tanúsítványában." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "A biztonsági tanúsítványát egy másik tartományra állították ki." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "A biztonsági tanúsítványa csak a jövőben válik érvényessé." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "A biztonsági tanúsítványa lejárt." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Fiók létrehozása" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Most már használhatja ezt a fiókot: %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Hibás felhasználónév vagy jelszó" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Valami tönkre ment" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Nem sikerült kapcsolódni ehhez: %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Érvénytelen cím" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Nincs válasz a kiszolgálótól" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Regisztráció ezen: %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "A kiszolgáló azt követeli, hogy regisztráljon egy weboldalon keresztül" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Weboldal megnyitása" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Regisztráció" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" "Nézze meg a %s oldalt azon információkért, hogy hogyan kell regisztrálni" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Felhasználó tiltása" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Teljes tartomány tiltása" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Tiltás feloldása" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Kitűzve" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Kitűzés" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Tiltva" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Tartomány tiltva" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Tiltás" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Némítás" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Értesítések engedélyezve" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Értesítések az említésekhez" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Némítva" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Értesítések letiltva" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Névjegy" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Beállítások" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Szoba beállítása" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Titkosítás" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Tagok" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Tulajdonos" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Adminisztrátor" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Tag" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "A fájl meghaladja a kiszolgáló legnagyobb feltöltési méretét." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Az üzenet túl hosszú" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "szerkesztve" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "függőben…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "sikertelen kézbesítés" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Titkosítatlan" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Nem lehet elküldeni az üzenetet" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b. %e., %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b. %e., %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%p %I∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i perccel ezelőtt" msgstr[1] "%i perccel ezelőtt" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Épp most" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Én" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Kép elküldve" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fájl elküldve" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Kép érkezett" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fájl érkezett" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Kimenő hívás" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Bejövő hívás" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b. %e." #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Tegnap" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Beszélgetés indítása" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Felhasználó" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Meghívás" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Magánbeszélgetés indítása" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kirúgás" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Írási jogosultság megadása" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Írási jogosultság visszavonása" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Meghívás a konferenciába" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Fájl kiválasztása" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s tőle: %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "A szoba neve" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "A szoba leírása" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Állandó" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "A szoba megmarad azután is, hogy az utolsó résztvevő elhagyta" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Nyilvánosan kereshető" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "A résztvevők megváltoztathatják a témát" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Jogosultság a Jabber-azonosítók megtekintéséhez" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kinek engedélyezett a résztvevők Jabber-azonosítóinak megtekintése?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Jelszó" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderált" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Csak a hanggal rendelkező résztvevők küldhetnek üzeneteket" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Csak tagok" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Csak tagok léphetnek be a szobába" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ez a konferencia nem engedélyezi az üzenetek küldését." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Jogosultság kérése" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Fájl küldése" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hívás indítása" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hanghívás" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videohívás" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Üzenetek keresése" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Beszélgetés részletei" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Beszélgetés bezárása" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Hibakeresési információk" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Hívás…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Csörgetés…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s befejezte a hívást" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s elutasította a hívást" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamerák" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nem található kamera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonok" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nem található mikrofon." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hangszórók" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nem található hangszóró." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Meghívás hívásra" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Indítás" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Csatlakozás csatornához" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Következő" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Csatlakozás" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Csatlakozás…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Jelszó szükséges a szobába való belépéshez" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" "Eltiltva a konferenciához való csatlakozástól vagy a konferencia " "létrehozásától" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "A szoba nem létezik" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Nem engedélyezett a szoba létrehozása" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Csak tagoknak engedélyezett szoba" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Válasszon másik becenevet" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Túl sok résztvevő van a szobában" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Hozzáadás" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "A Dino névjegye" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Ma" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%b. %e., %a" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i keresési eredmény" msgstr[1] "%i keresési eredmény" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Ebben: %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Ezzel: %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Bejövő videohívás" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Bejövő csoportos videohívás" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Bejövő csoportos hívás" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Elutasítás" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Elfogadás" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Feliratkozási kérés" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Elutasítás" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Meghívás ide: %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s meghívta Önt ide: %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Jogosultsági kérés" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s jogosultságot kér, hogy ide írjon: %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Gépelési értesítések küldése" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Olvasási visszaigazolások küldése" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Be" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Ki" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Alapértelmezett: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Kérés" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Jogosultság kérése az üzenetek küldéséhez" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Üdvözli a Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Jelentkezzen be, vagy hozzon létre egy fiókot az induláshoz." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Fiók beállítása" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Fiókok kezelése" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP-cím" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Megjelenített név" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Téma" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Üzenet szerkesztése" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ön" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Reakció hozzáadása" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Megnyitás" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Mentés másként…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s és %i partner éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s és %s éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s és %s éppen ír…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s éppen ír…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Hívás elindítva" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Elindítva ennyi ideje: %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Ön ezt a hívást egy másik eszközön kezelte" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Hívás befejezve" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Befejezve ekkor: %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Eddig tartott: %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Nem fogadott hívás" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Ön nem fogadta ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s nem fogadta ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Hívás elutasítva" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Ön elutasította ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s elutasította ezt a hívást" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Hívás sikertelen" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i óra" msgstr[1] "%i óra" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i perc" msgstr[1] "%i perc" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "néhány másodperc" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Kézbesítve" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Elolvasva" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Kérés küldése" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Még nem kap állapotfrissítéseket ettől a partnertől." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Ez a partner hozzá szeretné adni Önt a partnerlistájához" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Ez a beszélgetés nem támogatja a reakciókat." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Válasz" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Ez a beszélgetés nem támogatja a válaszokat." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "%s letöltése… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%s letöltése…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "%s feltöltése… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ezt ajánlotta: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Felajánlott fájl: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Felajánlott fájl" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "A fájlátvitel sikertelen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fájl" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Új" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Partner részletei" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Üzenet frissítése" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Bejelentkezés" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Nem sikerült kiépíteni biztonságos kapcsolatot" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Vissza" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Nyilvános kiszolgáló kiválasztása" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Vagy egy kiszolgáló címének megadása" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Bejelentkezés inkább" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Minden készen áll!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Befejezés" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Gépelési értesítések küldése" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "_Olvasási visszaigazolások küldése" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "É_rtesítések" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Értesítés új üzenet érkezésekor" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "Mosolyjelek át_alakítása emodzsivá" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Helyi álnév" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Kapcsolat állapota" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Fiók eltávolítása" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Jelszó megváltoztatása" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Változtatás" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Jelenlegi jelszó" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Új jelszó" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Jelszó megerősítése" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Általános" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Önnek nincsenek nyitott csevegései" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" "Kattintson a + gombra egy csevegés indításához vagy egy csatornához való " "csatlakozáshoz" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Beállítások megjelenítése" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Gyorsbillentyűk" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Beszélgetés" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigáció" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ugrás a következő beszélgetésre" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ugrás az előző beszélgetésre" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Fiók" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Becenév" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Álnév" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Partner hozzáadása" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Modern XMPP-csevegési ügyfélprogram" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "A Dino egy modern, nyílt forráskódú csevegőprogram az asztali gépekre. Arra " "összpontosít, hogy tiszta és megbízható Jabber/XMPP-élményt nyújtson, " "miközben a magánszféra megőrzését is fontosnak tartja." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Támogatja az OMEMO és az OpenPGP használatával történő végpontok közötti " "titkosítást, és lehetővé teszi a magánszférához kapcsolódó funkciókat, mint " "például az olvasási visszaigazolást és a gépelési értesítéseket." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "A Dino lekéri az előzményeket a kiszolgálóról, és szinkronizálja az " "üzeneteket a többi eszközzel." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nincs aktív keresés" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Gépeljen a keresés elkezdéséhez" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nincsenek illeszkedő üzenetek" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Ellenőrizze a helyesírást vagy próbálja meg eltávolítani a szűrőket" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Küldés" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Beállítások" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Gyorsbillentyűk" #~ msgid "Remove" #~ msgstr "Eltávolítás" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Bejelentkezés ide: %s" #~ msgid "No accounts configured" #~ msgstr "Nincsenek beállított fiókok" #~ msgid "Add an account" #~ msgstr "Fiók hozzáadása" #~ msgid "Pick another server" #~ msgstr "Másik kiszolgáló választása" #~ msgid "Local Settings" #~ msgstr "Helyi beállítások" #~ msgid "Notifications" #~ msgstr "Értesítések" #~ msgid "Pin conversation" #~ msgstr "Beszélgetés kitűzése" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Kitűzi a beszélgetést a beszélgetéslista tetejére" #~ msgid "Only when mentioned" #~ msgstr "Csak ha említenek" #~ msgid "Permissions" #~ msgstr "Jogosultságok" #~ msgid "Conference Details" #~ msgstr "Konferencia részletei" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "A kommunikáció és az állapotfrissítések mindkét irányból le vannak tiltva" #~ msgid "A password to restrict access to the room" #~ msgstr "Jelszó a szobához való hozzáférés korlátozásához" #~ msgid "Message history" #~ msgstr "Üzenetelőzmények" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "A szoba üzenetelőzményeinek legnagyobb mennyisége" #~ msgid "Convert smileys to emojis" #~ msgstr "Hangulatjelek átalakítása emodzsikká" #~ msgid "Check spelling" #~ msgstr "Helyesírás-ellenőrzés" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Kattintson ide egy beszélgetés indításához vagy egy csatornához való " #~ "csatlakozáshoz." #~ msgid "No active conversations" #~ msgstr "Nincs aktív beszélgetés" #~ msgid "Main window with conversations" #~ msgstr "A fő ablak a beszélgetésekkel" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s és %i további felhasználó" #~ msgid "Save" #~ msgstr "Mentés" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s, és %s" #~ msgid "%s and %s" #~ msgstr "%s és %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "gépel…" #~ msgstr[1] "gépel…" #~ msgid "has stopped typing" #~ msgstr "abbahagyta a gépelést" #~ msgid "Discover real JIDs" #~ msgstr "Valódi JID-k megtekintése" #~ msgid "Who may discover real JIDs?" #~ msgstr "Ki láthatja a valódi JID-ket?" #~ msgid "Password required for room entry, if any" #~ msgstr "A szobába lépéshez szükséges jelszó, ha van" #~ msgid "Join Conference" #~ msgstr "Csatlakozás konferenciához" #~ msgid "Quit" #~ msgstr "Kilépés" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "" #~ "A JID-nek ebben a formátumban kell lennie: \"felhasználó@szolgáltató.com\"" #~ msgid "Copy Link Address" #~ msgstr "Link címének másolása" #~ msgid "Copy" #~ msgstr "Másolás" #~ msgid "Select All" #~ msgstr "Összes kijelölése" #~ msgid "Search" #~ msgstr "Keresés" dino-0.5.0/main/po/hy.po0000664000000000000000000012526414776241610013511 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-11 12:05+0000\n" "Language-Team: none\n" "Language: hy\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Չեղարկել" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Կպնել" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Անջատել հաշիւը" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Միացնել հաշիւը" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Ընտրել աւատար" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Ընտրել" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Նկարներ" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Բոլոր նիշքերը" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Հեռացնե՞լ %s հաշիւը" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Միացում…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Միացուած է" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Անջատուած է" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Սխալ ծածկագիր" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Անուաւեր TLS վկայական" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Սխալ" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Հաշիւներ" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Անջատուած հաշիւներ" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Աւելացնել հաշիւ" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Ակտիւ հաշիւներ չկան" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Մուտք" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Սպասարկիչը չի ապացուցել որ այն %s֊ն է։" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Ձեր օպերացիոն համակարգը չի վստահում սպասարկչի անվտանգ միացման վկայականին։" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Անվտանգութեան վկայականը թողարկուած է այլ տիրոյթի համար։" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Անվտանգութեան վկայականը կը դառնայ վաւեր ապագայում։" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Անվտանգութեան վկայականի ժամկէտն աւարտուել է։" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Ստեղծել հաշիւ" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "%s հաշիւն արդէն օգտագործելի է։" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Սխալ մուտքանուն կամ ծածկագիր" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Ինչ֊որ բան սխան ընթացաւ" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Հնարաւոր չէ միանալ %s֊ին" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Անուաւեր հասցէ" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Սպասարկիչը չի պատասխանում" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Գրանցուել %s֊ում" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Սպասարկիչը պահանջում է գրանցուել կայքում" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Բացել կայքը" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Գրանցուել" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Նշել %s՝ գրանցուելու մասին տեղեկանալու համար" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Արգելափակել օգտատիրոջը" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Արգելափակել ամբողջ տիրոյթը" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Ապաարգելափակել" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Ամրացուած է" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Ամրացնել" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Արգելափակ է" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Տիրոյթն արգելափակուած է" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Արգելափակել" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Լռեցնել" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Ծանուցումները միացուած են" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Ծանուցել երբ նշուած ես" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Լռեցուած է" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Ծանուցումներն անջատուած են" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Ծրագրի մասին" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Կարգաւորումներ" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Սենեակի կարգաւորումներ" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Կրիպտաւորում" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Անդամներ" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Տէր" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Վարչապետ" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Անդամ" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Նիշքի չափսը սպասարկչում թոյլատրելիից մեծ է։" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Հաղորդագրութիւնը չափից շատ երկար է" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "խմբագրուած է" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "ուղարկւում է…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "առաքումը խափանուեց" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ոչ կրիպտաւորուած" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Հնարաւոր չէ ուղարկել գրութիւնը" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i րոպէ առջ" msgstr[1] "%i րոպէ առաջ" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Հէնց նոր" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ես" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Նկարն ուղարկուած է" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Նիշքն ուղարկուած է" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Նկարը ստացուած է" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Նիշքը ստացուած է" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Զանգ է գնում" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Զանգ է գալիս" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Երէկ" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Սկսել զրոյց" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Օգտատէր" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Հրաւիրել" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Սկսել անձնական զրոյց" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Տշել" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Տալ գրելու թոյլտւութիւն" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Հետ վերցնել գրելու թոյլտւութիւն" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Հրաւիրել սենեակ" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Ընտել նիշք" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s֊ը %s֊ից" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Սենեակի անուանում" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Սենեակի նկարագրութիւն" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Մշտական" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Սենեակը մնալու է նոյնիսկ եթէ վերջին մասնակիցը լքի այն" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Հանրային եւ փնտրելի" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Մասնակիցները կարող են փոխել սենեակի թեման" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Թոյլատրել տեսնել մասնակիցների JID֊երը" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Ո՞վ կարող է տեսնել մասնակիցների JID֊երը։" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Ծածկագիր" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Խօսքի կարգաւորումներ են առկայ" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" "Միայն խօսքի թոյլտւութիւն ունեցող մասնակիցներն են ունակ յղել գրութիւններ" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Միայն անդամների համար" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Միայն անդամներն են կարող մուտք գործել սենեակ" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Այս սենեակում գրելու թոյլտւութիւն չունես։" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Դիմել խօսքի թոյլտւութիւն ստանալու համար" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Ուղարկել նիշք" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Սկսել զանգ" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Աւդիօ զանգ" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Վիդեօ զանգ" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Փնտրել գրութիւններ" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Զրոյցի մանրամասներ" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Փակել զրոյցը" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Տեղեկութիւններ վրիպազերծման համար" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Զանգ է գնում…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Զանգ…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s֊ն աւարտեց զանգը" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s֊ը չընդունեց զանգը" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Խցիկներ" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Խցիկ չի գտնուել" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Խօսափողներ" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Խօսափող չի գտնուել։" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Բարձրախօսներ" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Բարձրախօս չի գտնուել։" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Հրաւիրել զանգի" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Սկսել" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Միանալ սենեակին" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Յաջորդ" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Միանալ" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Մուտք…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Սենեակին միանալու համար անհրաժէշտ է ծածկագիր" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Քեզ արգելուած է ստեղծել կամ միանալ սենեակների" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Սենեակը գոյութիւն չունի" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Սենեակ ստեղծելն արգելուած է" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Փակ սենեակ՝ միայն անդամների համար" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Ընտրել այլ մականուն" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Չափից շատ մասնակիցներ՝ սենեակում" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Աւելացնել" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dino֊ի մասին" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Այսօր" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i արդիւնք" msgstr[1] "%i արդիւնք" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s֊ում" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s֊ի հետ" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Վիդեօ զանգ է գալիս" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Խմբային վիդեօ զանգ է գալիս" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Խմբային զանգ է գալիս" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Մերժել" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Ընդունել" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Հետեւելու դիմում" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Մերժել" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Հրաւէր՝ %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s֊ը հրաւիրեց քեզ %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Դիմել թոյլտւութեան համար" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s դիմում է %s֊ում գրելու թոյլտւութեան համար" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Յայտնել որ գրում ես" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Յայտնել որ կարդացել ես" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Այո" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Ոչ" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Լռելեայն՝ %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Դիմել" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Դիմել գրութիւններ ուղարկելու թոյլտւութեան համար" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Բարի գալո՛ւստ Dino" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Մուտք գործիր կամ ստեղծիր հաշիւ։" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Կարգաւորել հաշիւ" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Ակտիւ հաշիւներ չկան" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP հասցէ" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Անուն" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Թեմա" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Խմբագրել գրութիւնը" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Դու" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Աւելացնել արձագանք" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Բացել" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Պահել որպէս…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s֊ը, %s֊ը եւ %i մնացածը տպում են…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s֊ը, %s֊ը եւ %s֊ը տպում են…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s֊ը եւ %s֊ը տպում են…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s ֊ը տպում է…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Զանգը սկսուել է" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Սկսուել է %s առաջ" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Փոխանցել ես զանգը այլ սարքին" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Զանգն աւարտուեց" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Աւարտուեց %s֊ին" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Տեւեց՝ %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Բաց թողնուած զանգ" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Բաց թողեցիր այս զանգը" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s֊ը բաց թողեց այս զանգը" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Զանգը մերժուած է" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Դու մերժեցիր այս զանգը" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s֊ը մերժեց այս զանգը" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Զանգը ձախողուեց" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ժամ" msgstr[1] "%i ժամ" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i րոպէ" msgstr[1] "%i րոպէ" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "մի քանի վայրկեան" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Առաքուած է" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Ընթերցուած է" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Ուղարկել դիմում" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Դու չես ստանում կարգավիճակի թարմացումներ այս անձից։" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Այս անձը ցանկանում է աւելացնել քեզ իր կոնտակտների մէջ" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Այս զրոյցը չի սատարում արձագանքների։" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Պատասխանել" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Այս զրոյցը չի սատարում պատասխանների։" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Ներբեռնւում է %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Բեռնւում է %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Վերբեռնւում է %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "առաջարկւում է ընդունել %s, չափսը՝ %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Առաջարկւում է ընդունել նիշք՝ %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Առաջարկւում է ընդունել նիշք" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Նիշքի փոխանցումը խափանուեց" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Նիշք" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Նոր" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Կոնտակտի մանրամասներ" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Թարմացնել գրութիւնը" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Մուտք գործել" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Հնարաւոր չէ հաստատել գաղտնի միացում" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Հետ" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Ընտրել հանրային սպասարկիչ" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Կամ նշել սպասարկչի հասցէ" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Մուտքգործել փոխարէնը" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Եղա՛ւ։" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Վերջ" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Յայտնել _Տպելուդ Մասին" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Յայտնել _Ընթերցելուդ Մասին" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Ծանուցումներ" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Ծանուցել երբ նոր գրութիւն է եկել" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Փոխարկել զմայլիկներն՝ էմոջիների" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Տեղական այլանուն" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Կապի վիճակ" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Հեռացնել հաշիւը" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Փոխել ծածկագիրը" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Փոխել" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Ընթացիկ ծածկագիր" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Նոր ծածկագիր" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Հաստատել ծածկագիրը" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Ընդհանուր" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Չունես ոչ մի բաց զրոյց" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Սեղմիր + զրոյց սկսելու կամ սենեակ մտնելու համար" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Ցուցադրել կարգաւորումներ" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Ստեղնաշարի կարճատներ" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Զրոյց" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Նաւարկում" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Անցնել յաջորդ զրոյցին" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Անցնել նախորդ զրոյցին" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Հաշիւ" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Մականուն" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Այլանուն" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Աւելացնել կոնտակտ" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Ժամանակակից XMPP կլիենտ" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino֊ն՝ ժամանակակից, բաց կոդով կլիենտ է։ Այն միտուած է պարզ եւ վստահելի " "Jabber/XMPP փորձառութիւն ապահովելուն, միաժամանակ մտքում ունենալով քո " "գաղտնիութիւնը։" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Սատարում է ծայրից ծայրի կրիպտաւորմանը OMEMO֊ի եւ OpenPGP֊ի միջոցով եւ թոյլ է " "տալիս կարգաւորել գաղտնիութեանը վերաբերող հնարաւորութիւններ, ինչպիսին են՝ " "ստացուած գրութիւնը ընթերնելու կամ տպելու մասին ծանուցելը։" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino֊ն ունակ է ստանալ սպասարկչից գրութիւնների պատմութիւն եւ սինքրոնացնել այն " "այլ սարքերի հետ։" #: main/data/global_search.ui:27 msgid "No active search" msgstr "Փնտրելն ընթացքում չէ" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Տպէք՝ փնտրելու համար" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Համապատասխան գրութիւններ չկան" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Համոզուիր որ սխալներ չկան կամ փորձիր վերացնել զտիչները" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Ուղարկել" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Կարգաւորումներ" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Ստեղնաշարի կարճատներ" dino-0.5.0/main/po/ia.po0000664000000000000000000010246114776241610013454 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Language-Team: none\n" "Language: ia\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "" dino-0.5.0/main/po/id.po0000664000000000000000000011610614776241610013460 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-10-27 10:00+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.8.2-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Batal" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Hubungi" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Nonaktifkan akun" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Aktifkan akun" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Pilih gambar profil" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Pilih" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Gambar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Semua file" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Hapus akun %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Menghubungi…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Terhubung" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Terputus" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Password salah" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Sertifikat TLS tidak valid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Rusak" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Akun" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Akun dinonaktifkan" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Tambah akun" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Tidak ada akun aktif" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Masuk" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Server tidak dapat membuktikan bahwa ini adalah %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Sertifikat keamanannya tidak dipercaya oleh OS Anda." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Sertifikat keamanan dikeluarkan untuk domain lain." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Sertifikat keamanan hanya akan berlaku di masa mendatang." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Sertifikat keamanan tidak berlaku." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Buat akun" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Akun dapat digunakan %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nama pengguna atau kata sandi salah" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Ada yang tidak beres" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Tidak terhubung ke %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Alamat tidak valid" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Server tidak merespon" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Terdaftar pada %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Server ini mengharuskan pendaftaran melalui website" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Buka website" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Daftar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Periksa %s untuk informasi pendaftaran" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Blokir pengguna" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blokir seluruh domain" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Buka blokir" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Disematkan" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Sematkan" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Diblokir" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domain diblokir" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokir" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Senyapkan" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notifikasi dinyalakan" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notifikasi penyebutan" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Disenyapkan" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notifikasi disenyapkan" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Tentang" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Pengaturan" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Konfigurasi kamar" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Enkripsi" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anggota" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Pemilik" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Moderator" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Anggota" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Ukuran file melebihi ukuran unggahan maksimum." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Pesan terlalu panjang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "Diedit" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "tertunda…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "pengiriman gagal" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Tidak terenkripsi" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Tidak bisa mengirimkan pesan" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i menit yang lalu" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Sesaat lalu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Saya" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Gambar terkirim" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File terkirim" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Gambar diterima" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "File diterima" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Panggilan keluar" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Panggilan masuk" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Kemarin" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Mulai percakapan" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Pengguna" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Undang" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Mulai percakapan pribadi" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Depak" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Beri ijin menulis" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Batalkan ijin menulis" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Undang ke Grup" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Pilih file" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s dari %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nama kamar" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Deskripsi kamar" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Abadi" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Kamar tetap ada setelah penghuni terakhir pergi" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Dapat ditemukan" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Penghuni dapat mengubah subyek pembicaraan" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Ijin untuk melihat JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Siapa yang diijinkan untuk melihat JID penghuni?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Kata sandi" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Dimoderasi" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Hanya penghuni dengan akses suara yang boleh mengirim pesan" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Khusus anggota" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Hanya anggota yang boleh memasuki kamar" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Grup ini tidak mengijinkan anda mengirim pesan." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Meminta ijin" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Kirim file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Mulai panggilan" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Panggilan audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Panggilan video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Cari pesan" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detail Obrolan" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Tutup Obrolan" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informasi Debug" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Memanggil…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Berdering…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s mengakhiri panggilan" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s menolak panggilan" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kamera tidak ditemukan." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofon" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofon tidak ditemukan." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Sepiker" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Sepiker tidak ditemukan." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Mulai" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Bergabung dengan Channel" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Selanjutnya" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Bergabung" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bergabung…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Password diperlukan untuk memasuki kamar" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Dilarang bergabung atau membuat grup" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Kamar tidak ada" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Tidak diperbolehkan membuat kamar" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Kamar khusus anggota" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Pilih nama panggilan lain" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Terlalu banyak penghuni di kamar" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Tambah" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Tentang Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hari ini" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i hasil pencarian" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "di %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Pada %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Panggilan video masuk" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Panggilan video grup masuk" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Panggilan grup masuk" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Tolak" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Terima" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Meminta ijin" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Tolak" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Undangan untuk %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s mengundang anda ke %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Permintaan ijin" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%smeminta ijin untuk menulis ke dalam %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Kirim pemberitahuan pengetikan" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Kirim tanda telah dibaca" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "On" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Mati" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Pengaturan awal: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Permintaan" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Meminta ijin untuk mengirim pesan" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Selamat datang di Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Masuk atau buat akun untuk memulai." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Buat akun" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Atur akun" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Alamat XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Ubah pesan" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Anda" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Tambahkan reaksi" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Buka" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Simpan sebagai…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s, dan %i lainnya sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, dan %s sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s dan%s sedang mengetik…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s sedang mengetik…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Panggilan dimulai" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Dimulai %s yang lalu" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Anda menangani panggilan ini di perangkat lain" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Panggilan berakhir" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Berakhir pada %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Berlangsung %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Panggilan tak terjawab" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Anda melewatkan panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s melewatkan panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Panggilan ditolak" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Anda menolak panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s menolak panggilan ini" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Panggilan gagal" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i jam" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i menit" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "beberapa detik" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Terkirim" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Baca" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Kontak ini ingin menambahkan anda kedalam daftar kontaknya" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Percakapan ini tidak mendukung reaksi." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Balas" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Percakapan ini tidak mendukung balasan." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Mengunduh %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ditawarkan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "File ditawarkan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "File ditawarkan" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transfer file gagal" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Berkas" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Baru" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detail kontak" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Update pesan" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Tidak dapat membuat sambungan aman" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Kembali" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Pilih server publik" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Atau tentukan alamat server" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Masuk saja" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Selesai!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Selesai" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Beri tahu saat ada pesan" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias lokal" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Status koneksi" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Hapus akun" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Umum" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Anda tidak memiliki percakapan terbuka" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klik + untuk memulai obrolan atau bergabung dengan saluran" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Pintasan keyboard" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Percakapan" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigasi" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Pesan selanjutnya" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Pesan sebelumnya" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Akun" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Panggilan" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Tambah kontak" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Aplikasi chat XMPP modern" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino adalah aplikasi chat open source modern untuk PC. Menyediakan " "pengalaman Jabber / XMPP yang handal dengan tetap menjunjung privasi Anda." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Mendukung enkripsi end-to-end dengan OMEMO dan OpenPGP, dan memungkinkan " "pengaturan fitur terkait privasi seperti tanda pesan dibaca dan " "pemberitahuan pengetikan." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino mengambil riwayat pesan dari server dan menyinkronkan pesan dengan " "perangkat lain." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Tidak ada pencarian aktif" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Ketik untuk memulai pencarian" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Tidak ada pesan yang cocok" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Periksa ejaan atau coba hapus filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Kirim" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferensi" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Kombinasi tombol" #~ msgid "Remove" #~ msgstr "Hapus" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Masuk ke %s" #~ msgid "No accounts configured" #~ msgstr "Tidak ada akun yang dikonfigurasi" #~ msgid "Add an account" #~ msgstr "Tambah akun" #~ msgid "Pick another server" #~ msgstr "Pilih server lain" #~ msgid "Local Settings" #~ msgstr "Pengaturan lokal" #~ msgid "Notifications" #~ msgstr "Notifikasi" #~ msgid "Pin conversation" #~ msgstr "Sematkan percakapan" #~ msgid "Only when mentioned" #~ msgstr "Hanya jika dipanggil" #~ msgid "Permissions" #~ msgstr "Ijin" #~ msgid "Conference Details" #~ msgstr "Detail Grup" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Komunikasi dan pembaruan status diblokir pada kedua arah" #~ msgid "A password to restrict access to the room" #~ msgstr "Kata sandi untuk membatasi akses ke kamar" #~ msgid "Message history" #~ msgstr "Riwayat pesan" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Jumlah maksimum backlog yang diterbitkan oleh kamar" #~ msgid "Convert smileys to emojis" #~ msgstr "Ubah smiley menjadi emoji" #~ msgid "Check spelling" #~ msgstr "Periksa ejaan" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klik untuk memulai percakapan atau bergabung dengan channel." dino-0.5.0/main/po/ie.po0000664000000000000000000011612414776241610013461 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-03-01 10:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anullar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Connexer" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Selecte un avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Selecter" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Images" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Omni files" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Remover li conto %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conexion…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conexet" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Disconexet" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Contrasigne es ínvalid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificate TLS es ínvalid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Errore" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Contos" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Adjunter un conto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Null activ contos" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Inregistrar se" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Li servitor ne posset provar que it es %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Su certificat ne have fide de vor sistema operativ." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Su certificat es emisset por un altri dominia." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Su certificat va esser valid solmen in li futur." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Su certificat ha expirat." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crear un conto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Nu vu posse usar li conto %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Contrasigne o nómine ínvalid" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Un errore evenit" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Ne successat conexer a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Adresse es ínvalid" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Servitore ne responde" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrar sur %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Li servitor besona r" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Aperter li website" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Visita %s por li information pri registration" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blocar" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Parametres" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuration del chambre" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietario" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Li grandore de file excede li maximum del servitor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Li missage es tre long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redactet" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ínciffrat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Ne successat inviar li missage" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%e %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%e %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "ante %i min" msgstr[1] "ante %i mins" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Strax" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Yo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Image sta inviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File sta inviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Image sta recivet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Image es recivet" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%e %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Yer" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Iniciar un conversation" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usator" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invitar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar un privat conversation" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Remover" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Dar li permission scrir" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar li permission scrir" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitar a un conferentie" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Selecter un file" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nómine del chambre" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrition del chambre" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistent" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Li chambre va persister pos que li ultim occupant surti" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Serchabil publicmen" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Occupantes posse cambiar li tema" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permission vider JIDs" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qui es permisset vider li JIDs del membres?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Contrasigne" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderat" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Solmen occupantes con voce posse inviar missages" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Solmen membres" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Solmen li membres posse intrar li chambre" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ti-ci conferentie ne permisse a vos inviar missages." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demandar li permission" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Inviar un file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Serchar missages" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Adherer al chanele" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Avan" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Adherer" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Adherente…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Un contrasigne es besonat por intrar li chambre" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Prohibit de adhesion o creation de conferenties" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Chambre ne existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ne es permisset crear chambres" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Solmen por membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Selecte un different nómine" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tro mult occupantes in li chambre" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adjunter" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Pri Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hodie" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultate de sercha" msgstr[1] "%i resultates de sercha" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Refusar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Petition de abonnament" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Refusar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitation a(l) %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s invitat vos a(l) %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Demande de permission" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demanda li permission scrir in %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Inviar notificationes pri li tippada" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Inviar confirmationes de letion" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Yes" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "No" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Predefinit: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Demandar" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Demandar li permission inviar missages" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Benevenit a Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Inregistra vos o crea un conto por iniciar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Etablisser un conto" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gerer contos" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i altri tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s tippa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s tippa…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Ti-ci contacte desira adjunter vos al su contact-liste" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Descargante %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ha ofertat: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "File sta ofertat: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Un file sta ofertat" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transferte de un file ne successat" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detallies del contacte" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualisar li missage" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Ne successat etablisser un secur conexion" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Retro" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Selecte un public servitore" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "O provide un adresse de servitore" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "O inregistrar se" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Omni es pret!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finir" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notificar quande un missage ariva" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Local pseudonim" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "General" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Vu ne have apertet conversationes" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversation" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigation" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ear al sequent conversation" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ear al precedent conversation" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nómine" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonim" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adjunter li contacte" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Un modern client de conversationes XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un modern cliente de conversationes con fonte apert. It foca se sur " "provider un nett e fidibil experientie de Jabber/XMPP con un attention a " "confidentialitá." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "It supporta ciffration terminal per OMEMO e OpenPGP e permisse configurar " "sensitiv functiones quam confirmation de lectada e notificationes de tippada." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtene li diarium del servitore e sincronisa missages inter altri " "apparates." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Null activ sercha" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tippa por iniciar un sercha" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Null correspondent missages" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controla li ortografie o remove filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Inviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Rapid-tastes" #~ msgid "Remove" #~ msgstr "Remover" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Inregistrar se in %s" #~ msgid "No accounts configured" #~ msgstr "Null contos etablisset" #~ msgid "Add an account" #~ msgstr "Adjunter un conto" #~ msgid "Pick another server" #~ msgstr "Selecte un altri servitore" #~ msgid "Local Settings" #~ msgstr "Local parametres" #~ msgid "Notifications" #~ msgstr "Notificationes" #~ msgid "Only when mentioned" #~ msgstr "Solmen quande es mentionat" #~ msgid "Permissions" #~ msgstr "Permissiones" #~ msgid "Conference Details" #~ msgstr "Detallies del conferentie" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Actualisation del communication e statu es blocat in ambi directiones" #~ msgid "A password to restrict access to the room" #~ msgstr "Un contrasigne por restricter li accesse al chambre" #~ msgid "Message history" #~ msgstr "Diarium de missages" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Max númere de old missages gardat per li chambre" #~ msgid "Convert smileys to emojis" #~ msgstr "Converter smileys a emojis" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Fa un clic ti-ci por iniciar un conversation o adherer a un channel." #~ msgid "No active conversations" #~ msgstr "Null activ conversationes" #~ msgid "Main window with conversations" #~ msgstr "Li primari fenestre con conversationes" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i altri" #~ msgid "You can now start using %s" #~ msgstr "Nu vu posse usar %s" #~ msgid "Open Registration" #~ msgstr "Apert registration" #~ msgid "Save" #~ msgstr "Gardar quam" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippa…" #~ msgstr[1] "tippa…" #~ msgid "has stopped typing" #~ msgstr "ne tippa plu" #~ msgid "%i search results" #~ msgstr "resultates: %i" #~ msgid "Discover real JIDs" #~ msgstr "Decovrir real JIDs" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qui posse decovrir real JIDs?" #~ msgid "Password required for room entry, if any" #~ msgstr "Un contrasigne por intrar li chambre" dino-0.5.0/main/po/is.po0000664000000000000000000011614314776241610013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-08-23 18:21+0000\n" "Language-Team: none\n" "Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n" "X-Generator: Weblate 4.14-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Hætta við" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Tengjast" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Velja notandamynd" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Velja" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Myndir" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Allar skrár" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Fjarlægja reikning %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Tengist…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Tengdur" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Ótengdur" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Rangt lykilorð" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ógilt TLS vottorð" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Villa" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Reikningar" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Bæta við reikningi" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Engir virkir reikningar" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Skrá inn" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Þjónninn gat ekki sannað eign léns %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Öryggisvottorð hans er ekki treyst af stýrikerfinu þínu." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Öryggisvottorð hans er gefið út á annað lén." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Öryggisvottorð hans mun aðeins gilda í framtíðinni." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Öryggisvottorð hans er útrunnið." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Búa til reikning" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Þú getur nú notað reikninginn %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Rangt notandanafn eða lykilorð" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Eitthvað fór úrskeiðis" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Gat ekki tengst %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ógilt netfang" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Ekkert svar frá þjóni" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Nýskrá á %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Þjónninn þarfnast að þú skráir þig í gegnum vefsíðu" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Opna vefsíðu" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Nýskrá" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Athugaðu %s til að fá upplýsingar um hvernig á að nýskrá" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Loka á" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Stillingar" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Herbergisstilling" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Meðlimar" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigandi" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Stjórnandi" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Meðlimur" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Skráin fer yfir hámarksupphleðslustærð þjónsins." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Skilaboð of langt" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "breytt" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "að senda…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "afhending mistókst" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ódulkóðað" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Ekki er hægt að senda skilaboð" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Fyrir %i mín" msgstr[1] "Fyrir %i mín" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Núna" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ég" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Mynd send" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Skrá send" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Mynd móttekin" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Skrá móttekin" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Símtal í útleið" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Símtal í innleið" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Í gær" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Byrja samtal" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Notandi" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Bjóða" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Byrja einkasamtal" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Reka út" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Veita skrifleyfi" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Afturkalla skrifleyfi" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Bjóða í spjall" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Veldu skrá" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s frá %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Heiti herbergis" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Lýsing á herbergi" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Bjarga tómu herbergi" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" "Herbergið verður áfram til jafnvel eftir að síðasti notandinn er farinn" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Hægt er að leita eftir fyrir almenning" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Gestir geta breytt umræðuefni spjallsins" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Leyfi til að skoða JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Hverjum er heimilt að skoða JID gesta?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Lykilorð" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Stýrt" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Aðeins gestir með rödd (heimild) geta sent skilaboð" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Aðeins meðlimar" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Aðeins meðlimir mega fara inn í herbergi" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Þér hefur ekki verið leyft að senda skilaboð í þessu samtali." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Óska eftir leyfi" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Senda skrá" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Hefja símtal" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Hljóðsímtal" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Myndsímtal" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Leita að skilaboðum" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Villuleitarupplýsingar" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Hringir…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Hringir…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s lauk símtali" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s afþakkaði símtalið" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Myndavélar" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Myndavél fannst ekki." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Hljóðnemar" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Hljóðnemi fannst ekki." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hátalarar" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Hátalari fannst ekki." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Bjóða í símtal" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Byrja" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Tengjast rás" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Áfram" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Tengjast" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Tengist…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lykilorð þarf til að komast inn í herbergi" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Bannað að taka þátt í eða skapa ráðstefnu" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Herbergið er ekki til" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Óheimilt að búa til herbergi" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Herbergi er eingöngu fyrir meðlima" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Veldu annað gælunafn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Það eru of margir í herberginu" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Bæta við" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Um Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Í dag" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i leitarniðurstaða" msgstr[1] "%i leitarniðurstöður" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Í %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Með %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Óskast eftir myndsímtali" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Óskast eftir hópmyndsímtali" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Óskast eftir hópsímtali" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Hafna" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Samþykkja" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Áskriftarbeiðni" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Hafna" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Boð í %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s bauð þér í %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Beiðni um leyfi" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s biður um leyfi til að skrifa í %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Senda innsláttartilkynningar" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Senda lestrarkvittanir" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Virkt" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Óvirkt" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Sjálfgefið: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Biðja um" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Biðja um leyfi til að senda skilaboð" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Velkomin(n) í Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Skráðu þig inn eða búðu til reikning til að byrja." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Stofna reikning" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Stjórna reikningum" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Opna" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Vista sem…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s og %i aðrir eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s og %s eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s og %s eru að skrifa…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s er að skrifa…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Símtal hafið" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Byrjaði fyrir %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Þú svaraðir þessu símtali í öðru tæki" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Símtali lokið" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Laukst %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Entist %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Ósvarað símtal" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Þú misstir af þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s missti af þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Símtali hafnað" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Þú hafnaðir þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s afþakkaði þessu símtali" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Símtal mistókst" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i klst" msgstr[1] "%i klst" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i mín" msgstr[1] "%i mín" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "nokk(rum/rar) sekúndu(m/r)" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Þessi tengiliður vill bæta þér við tengiliðalistann sinn" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Að sækja %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s. Stærð: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Skráarstærð: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Skrá í boði" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Skráaflutningur mistókst" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Tengiliðaupplýsingar" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Breyta skilaboði" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Mistókst að skapa örugga tengingu" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Til baka" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Velja opinberan netþjón" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Eða tilgreina lén netþjóns" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Skrá inn í staðinn" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Allt klárt!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Loka" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Láta vita af nýjum skilaboðum" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Staðbundið samnefni" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Almennt" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Þú hefur engin opin spjöll" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Samtal" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Hreyfing" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Fara í næsta spjall" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Fara í fyrra spjall" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Reikningur" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Gælunafn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Samnefni" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Bæta við" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Nútímalegt XMPP-spjallforrit" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino er nútímalegt spjallforrit og frjáls hugbúnaður fyrir skjáborðið. Það " "leggur áherslu á að veita hreina og áreiðanlega Jabber/XMPP upplifun með " "friðhelgi þína í huga." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Það styður dulkóðun frá enda til enda með OMEMO og OpenPGP og gerir kleift " "að stilla persónuverndartengda eiginleika eins og lestrarkvittanir og " "innsláttartilkynningar." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino sækir feril af netþjóni og samstillir skilaboð með öðrum tækjum." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Engin virk leit" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skrifaðu til að hefja leit" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Engin samsvarandi skilaboð" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Athugaðu stafsetninguna eða reyndu að fjarlægja síur" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Senda" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Flýtilyklar" #~ msgid "Remove" #~ msgstr "Fjarlægja" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Skrá inn á %s" #~ msgid "No accounts configured" #~ msgstr "Engir reikningar stilltir" #~ msgid "Add an account" #~ msgstr "Bæta við reikningi" #~ msgid "Pick another server" #~ msgstr "Velja annan netþjón" #~ msgid "Local Settings" #~ msgstr "Staðbundnar stillingar" #~ msgid "Notifications" #~ msgstr "Tilkynningar" #~ msgid "Only when mentioned" #~ msgstr "Aðeins þegar minnst er á mig" #~ msgid "Permissions" #~ msgstr "Heimildir" #~ msgid "Conference Details" #~ msgstr "Upplýsingar um ráðstefnu" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Lokað er fyrir samskipta- og stöðuuppfærslur í báðar áttir" #~ msgid "A password to restrict access to the room" #~ msgstr "Lykilorð til að takmarka aðgang að herbergi" #~ msgid "Message history" #~ msgstr "Skilaboðaferill" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Hámarksfjöldi sögu sem herbergið gefur út" #~ msgid "Convert smileys to emojis" #~ msgstr "Breytta :) í 🙂 sjálfvirkt" #~ msgid "Check spelling" #~ msgstr "Kanna stafsetningu" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Smelltu hér til að hefja samtal eða taka þátt í rás." dino-0.5.0/main/po/it.po0000664000000000000000000012522314776241610013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 08:50+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annulla" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Connetti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Disabilita l'account" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Abilita l'account" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Scegli una foto profilo" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Seleziona" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Immagini" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Tutti i documenti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Rimuovere l'account %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Connessione…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Connesso" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Disconnesso" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Password sbagliata" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Il certificato TLS non è valido" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Errore" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Account" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Account disabilitato" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Aggiungi un account" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Nessun account attivo" #: main/src/windows/preferences_window/add_account_dialog.vala:126 #, fuzzy msgid "Sign in" msgstr "Registrati" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Il server non può dimostrare di essere %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Il certificato di sicurezza non è riconosciuto dal tuo sistema." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Il certificato di sicurezza è rilasciato per un altro dominio." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Il certificato di sicurezza diventerà valido solo in futuro." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Il certificato di sicurezza è scaduto." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crea account" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Ora è possibile iniziare a usare l’account %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nome utente o password sbagliati" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Qualcosa è andato storto" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Impossibile connettersi a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Indirizzo non valido" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Nessuna risposta dal server" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrati su %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Il server richiede di registrarsi tramite un sito web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Apri il sito web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrati" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Controlla %s per informazioni su come registrarsi" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Blocca l'utente" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blocca l'intero dominio" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Sblocca" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fissato" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fissa" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloccato" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Dominio bloccato" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blocca" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Silenza" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notifiche abilitate" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notifiche per menzioni" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silenziato" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notifiche disabilitate" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Informazioni" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Impostazioni" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configurazione della stanza" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Crittografia" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Partecipanti" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietario" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Amministratore" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Il file eccede la dimensione massima di upload." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Messaggio troppo lungo" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "modificato" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "in attesa…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "invio fallito" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Non cifrato" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Impossibile inviare il messaggio" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuto fa" msgstr[1] "%i minuti fa" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Adesso" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Io" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Immagine inviata" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "File inviato" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Immagine ricevuta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "File ricevuto" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Chiamata in uscita" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Chiamata in arrivo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ieri" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Inizia una conversazione" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utente" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invita" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Inizia una conversazione privata" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Espelli" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Concedi il permesso di scrivere" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca il permesso di scrivere" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invita alla conferenza" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Seleziona un file" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s da %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nome della stanza" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrizione della stanza" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistente" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "La stanza continuerà ad esistere dopo l'uscita dell'ultimo occupante" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Ricercabile pubblicamente" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "I partecipanti possono cambiare l'argomento" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Autorizzazione per visualizzare i JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Chi è autorizzato a vedere i JID degli occupanti?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Password" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderata" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Solo i partecipanti autorizzati possono mandare messaggi" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Solo per membri" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Solo i membri possono entrare nella stanza" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Questa conferenza non ti permette di inviare messaggi." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Richiesta di autorizzazione" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Invia un file" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Chiama" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chiamata audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videochiamata" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Cerca tra i messaggi" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Dettagli sulla conversazione" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Chiudi conversazione" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informazioni di debug" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Chiamando…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Sta squillando…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s ha terminato la chiamata" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s ha rifiutato la chiamata" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Telecamere" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nessuna telecamera trovata." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoni" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nessun microfono trovato." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altoparlanti" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nessun altoparlante trovato." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invita alla chiamata" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Inizia" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Entra nel canale" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Avanti" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Partecipa" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Accesso in corso…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Una password è richiesta per entrare nella stanza" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Ti è proibito entrare o creare una conferenza" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La stanza non esiste" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Non sei abilitato a creare la stanza" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "La stanza è solo per membri" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Scegli un soprannome differente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "La stanza ha troppi partecipanti" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Aggiungi" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Informazioni su Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Oggi" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i risultato per la ricerca" msgstr[1] "%i risultati per la ricerca" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "In %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Con %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Videochiamata in arrivo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Videochiamata di gruppo in arrivo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Chiamata di gruppo in arrivo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rifiuta" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Accetta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Richiesta di iscrizione" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Rifiuta" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invito per %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s ti ha invitato a %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Richiesta di autorizzazione" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ha chiesto il permesso di scrivere in %s" #: main/src/ui/contact_details/settings_provider.vala:29 #, fuzzy msgid "Send typing notifications" msgstr "Invia notifiche di digitazione" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Invia ricevute di lettura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Attivo" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Disattivo" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Predefinito: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Richiedi" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Richiedi il permesso di inviare messaggi" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Benvenuto in Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Accedi o crea un account per iniziare." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configura account" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gestisci account" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Indirizzo XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nome visualizzato" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Argomento" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Modifica messaggio" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tu" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Aggiungi reazione" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Apri" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salva come…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i altri stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s stanno scrivendo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s sta scrivendo…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Chiamata iniziata" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Iniziata %s fa" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Hai gestito questa chiamata su un altro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Chiamata terminata" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Terminata alle %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durata %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Chiamata persa" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Hai perso questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s ha perso questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Chiamata rifiutata" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Hai rifiutato questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s ha rifiutato questa chiamata" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Chiamata non riuscita" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ora" msgstr[1] "%i ore" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minuti" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "qualche secondo" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Inviato" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Letto" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Invia richiesta" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Non ricevi ancora aggiornamenti sullo stato da questo contatto." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Questo contatto vorrebbe aggiungerti alla sua lista contatti" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Questa conversazione non supporta le reazioni." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Rispondi" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Questa conversazione non supporta le risposte." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Scaricamento %s... (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Scaricamento di %s in corso…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Caricamento %s... (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ha offerto: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "File offerto: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "File offerto" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Trasferimento del file non riuscito" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "File" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nuovo" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Dettagli del contatto" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aggiorna il messaggio" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Accedi" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Impossibile stabilire una connessione sicura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Indietro" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Scegli un server pubblico" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Oppure inserisci l'indirizzo di un server" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Altrimenti accedi" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tutto pronto!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Fine" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Invia notifiche di _digitazione" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Invia ricevute di _lettura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notifiche" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notifica quando arriva un nuovo messaggio" #: main/data/preferences_window/general_preferences_page.ui:55 #, fuzzy msgid "_Convert Smileys to Emoji" msgstr "_Trasforma gli smile in emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias locale" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Stato della connessione" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Rimuovi l'account" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Cambia password" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Cambia" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Password corrente" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Nuova password" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Conferma password" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Generali" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Non hai chat aperte" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Clicca + per iniziare una conversazione o unirti ad un canale" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Mostra le preferenze" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Scorciatoie da tastiera" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversazione" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigazione" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Passa alla conversazione successiva" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Passa alla conversazione precedente" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Account" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Soprannome" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Aggiungi contatto" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Client di chat moderno per XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino è un client di chat per il desktop, moderno e open-source. Si concentra " "nel fornire un'esperienza Jabber/XMPP pulita e affidabile tenendo presente " "la tua privacy." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Support la crittografia end-to-end tramite OMEMO e OpenPGP e permette di " "configurare le funzioni relative alla privacy come le ricevute di lettura e " "le notifiche di digitazione." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupera la cronologia dal server e sincronizza i messaggi con gli " "altri dispositivi." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nessuna ricerca attiva" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Digita per iniziare una ricerca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nessun messaggio corrispondente" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controlla l'ortografia o prova a rimuovere dei filtri" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Invia" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferenze" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Scorciatoie da tastiera" #~ msgid "Remove" #~ msgstr "Rimuovi" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Accedi a %s" #~ msgid "No accounts configured" #~ msgstr "Nessun account configurato" #~ msgid "Add an account" #~ msgstr "Aggiungi un account" #~ msgid "Pick another server" #~ msgstr "Scegli un altro server" #~ msgid "Local Settings" #~ msgstr "Impostazioni locali" #~ msgid "Notifications" #~ msgstr "Notifiche" #~ msgid "Pin conversation" #~ msgstr "Fissa conversazione" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fissa la conversazione in cima all'elenco delle conversazioni" #~ msgid "Only when mentioned" #~ msgstr "Solo se menzionato" #~ msgid "Permissions" #~ msgstr "Permessi" #~ msgid "Conference Details" #~ msgstr "Dettagli della conferenza" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "La comunicazione e gli aggiornamenti di stato sono bloccati in entrambe " #~ "le direzioni" #~ msgid "A password to restrict access to the room" #~ msgstr "Una password per limitare l’accesso al gruppo" #~ msgid "Message history" #~ msgstr "Cronologia dei messaggi" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Numero massimo di messaggi in cronologia restituiti dalla stanza" #~ msgid "Convert smileys to emojis" #~ msgstr "Trasforma le faccine in emoji" #~ msgid "Check spelling" #~ msgstr "Controlla l'ortografia" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Fai clic qui per iniziare una conversazione o per entrare in un canale." #~ msgid "No active conversations" #~ msgstr "Nessuna conversazione attiva" #~ msgid "Main window with conversations" #~ msgstr "La finestra principale con le conversazioni" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i altri" #~ msgid "You can now start using %s" #~ msgstr "Adesso puoi iniziare ad usare %s" #~ msgid "Open Registration" #~ msgstr "Registrazioni aperte" #~ msgid "Save" #~ msgstr "Salva" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s, e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "sta scrivendo…" #~ msgstr[1] "stanno scrivendo…" #~ msgid "has stopped typing" #~ msgstr "ha smesso di scrivere" #~ msgid "%i search results" #~ msgstr "%i risultati per la ricerca" #~ msgid "Discover real JIDs" #~ msgstr "Scopri i veri JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Chi può scoprire i veri JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "Password richiesta per entrare nella stanza, se impostata" #~ msgid "Failed connecting to %s" #~ msgstr "Connessione a %s fallita" #~ msgid "Join Conference" #~ msgstr "Partecipa alla conferenza" #~ msgid "Communicate happiness." #~ msgstr "Comunica felicità." #~ msgid "Quit" #~ msgstr "Esci" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "Il JID dovrebbe essere nella forma \"utente@example.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copia l'indirizzo del collegamento" #~ msgid "Copy" #~ msgstr "Copia" #~ msgid "Select All" #~ msgstr "Seleziona Tutto" #~ msgid "Search" #~ msgstr "Cerca" #~ msgid "Send message marker" #~ msgstr "Invia lo stato del messaggio" #~ msgid "Start Chat" #~ msgstr "Inizia una conversazione" #~ msgid "Request presence updates" #~ msgstr "Richiedi gli aggiornamenti di stato" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Questa è una notifica dell'app. Clicca il bottone per chiudere" #~ msgid "Join on startup" #~ msgstr "Entra all'avvio" #~ msgid "Add Chat" #~ msgstr "Aggiungi conversazione" dino-0.5.0/main/po/ja.po0000664000000000000000000012723514776241610013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-12 18:09+0000\n" "Language-Team: Japanese \n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "キャンセル" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "接続" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "アカウントを無効にする" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "アカウントを有効にする" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "アバターを選択" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "選択" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "画像" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "すべてのファイル" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "アカウント %s を削除しますか?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "接続試行中…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "接続済み" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "切断" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "パスワードが違います" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "TLS 証明書が不正です" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "エラー" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "アカウント" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "無効とされたアカウント" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "アカウントを追加" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "アクティブなアカウントがありません" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "サインイン" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "サーバーはこれが %s だと証明できませんでした。" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "このセキュリティー証明書はオペレーティングシステムに信頼されていません。" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "このセキュリティー証明書はほかのドメインのために発行されています。" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "このセキュリティー証明書が有効になる日時になっていません。" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "このセキュリティー証明書は有効期限切れです。" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "アカウントを作成" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "アカウント %s を使用できるようになりました。" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "ユーザー名またはパスワードが正しくありません" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "問題が発生しました" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "%s に接続できませんでした" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "アドレスが不正です" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "サーバーから応答がありません" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "%s に登録" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "サーバーが Web サイトでのサインアップを要求しています" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Web サイトを開く" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "登録" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "サインアップの方法に関する情報は、%s をご確認ください" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "ユーザーをブロック" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "ドメイン全体をブロック" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "ブロックを解除" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "固定済" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "固定" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "ブロック済" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "ドメインはブロックされています" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "ブロック" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "ミュート" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "通知が有効になっています" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "メンションの通知" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "ミュート済" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "通知が無効になっています" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "概要" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "設定" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "ルームの設定" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "暗号化" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "メンバー" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "オーナー" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理人" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "メンバー" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" "ファイルのサイズがサーバーの最大アップロード可能サイズを超過しています。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "メッセージが長すぎます" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "編集済" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "待機中…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "配信に失敗しました" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "非暗号化" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "メッセージを送信できません" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b%d日、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b%d日、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a曜日、%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a曜日、%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "たった今" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "自分" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "画像を送信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "ファイルを送信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "画像を受信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "ファイルを受信しました" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "発信呼び出し" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "着信" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b%d日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨日" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "会話を開始" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "ユーザー" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "招待" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "個人チャットを開始" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "退出させる" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "送信権限を許可" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "送信権限を取り消す" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "トークルームへ招待" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "ファイルを選択" #: main/src/ui/util/helper.vala:65 #, fuzzy, c-format msgid "%s from %s" msgstr "%2$s の %1$s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "ルームの名前" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "ルームの説明" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "ルームを自動削除しない" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "最後の参加者が退会してもルームを残す" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "外部から検索可能にする" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "参加者にタイトルの変更を許可する" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JID を表示できるメンバー" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "参加者の JID を表示できるメンバーを選択します" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "パスワード" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "発言制限" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "発言権のある参加者のみがメッセージを送れます" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "メンバー制" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "メンバーのみが入室できます" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "このトークルームでは、メッセージの送信が許可されていません。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "送信権限を要求" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "ファイルを送信" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "通話開始" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "音声通話" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "ビデオ通話" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "メッセージを検索" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "会話の詳細" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "会話を終了" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "デバッグ情報" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "呼び出し中…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "鳴っている…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%sは通話を終了しました" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s は通話を拒否しました" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "カメラ" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "カメラが見つかりません。" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "マイク" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "マイクが見つかりません。" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "スピーカー" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "スピーカーが見つかりません。" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "通話に招待" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "開始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "チャンネルに参加" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "次へ" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "参加" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "参加試行中…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "ルームに参加するにはパスワードが必要です" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "参加中または作成中のトークルームから退会させられました" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "ルームは存在しません" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "ルームを作成する権限がありません" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "メンバー制ルーム" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "別のニックネームを選んでください" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "ルームの参加者が多すぎます" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "追加" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dino について" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今日" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%b%d日 (%a)" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 件の検索結果" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s での検索結果" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s との会話での検索結果" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "ビデオ通話の着信中" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "ビデオグループ通話の着信中" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "グループ通話の着信中" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "拒否" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "承諾" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "在席通知の申込" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "拒否" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s への招待" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s さんがあなたを %s に招待しました" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "送信権限の要求" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s さんが %s での送信権限を要求しています" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "入力中であることを通知" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "既読状態を送信" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "オン" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "オフ" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "デフォルト: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "要求" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "メッセージの送信権限を要求" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Dino へようこそ!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "サインインまたはアカウント登録をしてください。" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "アカウントをセットアップ" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "アカウントを管理" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPPのアドレス" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "表示名" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "トピック" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "メッセージを編集" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "あなた" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "リアクションを追加" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "開く" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "名前を付けて保存" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s さん、%s さんに加えて、%i 人のメンバーが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s さん、%s さん、%s さんが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s さんと %s さんが入力しています…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s さんが入力しています…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "通話開始" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s前に開始" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "別のデバイスでこの呼び出しを処理しました" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "通話終了" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%sに終了" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "%s 継続していた" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "不在着信" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "この電話に出られなかった" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s はこの呼び出しに出られなかった" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "通話が拒否されました" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "通話を拒否しました" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s はこの呼び出しを拒否しました" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "呼び出しに失敗しました" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i 時間" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i 分" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "数秒" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "送信済" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "既読" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "この相手があなたを連絡先に追加しようとしています" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "この会話はリアクションをサポートしていません。" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "返信" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "この会話は返信をサポートしていません。" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%sダウンロードしています…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s を受信しました: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "ファイルを受信しました: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "ファイルが提供されています" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "ファイルの転送に失敗しました" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "ファイル" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "新規" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "連絡先の詳細" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "メッセージを更新" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "安全な接続を確立できませんでした" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "戻る" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "公開サーバーを選択" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "または、サーバーのアドレスを手入力" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "代わりにサインイン" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "すべてのセットアップが完了しました!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "完了" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "入力中の通知を送信" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "既読通知を送信" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "通知" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "新しいメッセージが届いたときに通知" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "ASCIIの顔文字を絵文字に変換" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "ローカルでの別名" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "接続ステータス" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "アカウントを削除" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "一般" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "表示するトークはまだありません" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "+をクリックしてチャットまたはチャンネルに参加" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "キーボードショートカット" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "トーク" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "ナビゲーション" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "次のトークへ移動" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "前のトークへ移動" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "アカウント" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "ニックネーム" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "別名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "連絡先を追加" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "現代的な XMPP チャット クライアント" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino はオープンソースの現代的なデスクトップ向けチャットクライアントです。プラ" "イバシーを考慮しつつ、シンプルで信頼できる Jabber/XMPP エクスペリエンスの提供" "を第一に考えて開発されています。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO と OpenPGP を利用したエンドツーエンド暗号化に対応しており、既読状態の送" "信や入力通知などのプライバシー関連の設定も可能です。" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino はサーバーから履歴を取得し、ほかのデバイスとメッセージを同期します。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "まだ検索していません" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "入力して検索を開始してください" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "一致するメッセージはありません" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "スペルを確認するか、フィルターを消去してみてください" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "送信" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "設定" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "キーボードショートカット" #~ msgid "Remove" #~ msgstr "削除" #, c-format #~ msgid "Sign in to %s" #~ msgstr "%s にサインイン" #~ msgid "No accounts configured" #~ msgstr "アカウントが設定されていません" #~ msgid "Add an account" #~ msgstr "アカウントを追加" #~ msgid "Pick another server" #~ msgstr "別のサーバーを選択" #~ msgid "Local Settings" #~ msgstr "ローカル設定" #~ msgid "Notifications" #~ msgstr "通知" #~ msgid "Pin conversation" #~ msgstr "会話を固定" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "会話をリストの最上部に固定" #~ msgid "Only when mentioned" #~ msgstr "メンションされた場合のみ" #~ msgid "Permissions" #~ msgstr "送信権限" #~ msgid "Conference Details" #~ msgstr "トークルームの詳細" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "双方のコミュニケーションと状態の更新がブロックされます" #~ msgid "A password to restrict access to the room" #~ msgstr "ルームへのアクセスを制限するためのパスワード" #~ msgid "Message history" #~ msgstr "メッセージの履歴" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "保存するトーク履歴の数" #~ msgid "Convert smileys to emojis" #~ msgstr "スマイリーを絵文字に変換" #~ msgid "Check spelling" #~ msgstr "スペルチェック" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "トークを始めたりトークルームに参加したりするには、ここをクリックしてくださ" #~ "い。" #~ msgid "No active conversations" #~ msgstr "アクティブなトークがありません" #~ msgid "Main window with conversations" #~ msgstr "トーク中のメインウィンドウ" #~ msgid "%s, %s and %i others" #~ msgstr "%s さん、%s さんとその他 %i 人" #~ msgid "You can now start using %s" #~ msgstr "%s をお使いいただけます" #~ msgid "Open Registration" #~ msgstr "登録を開く" #~ msgid "Save" #~ msgstr "保存" #~ msgid "%s, %s and %s" #~ msgstr "%s さん、%s さんと %s さん" #~ msgid "%s and %s" #~ msgstr "%s さんと %s さん" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "が入力しています…" #~ msgid "has stopped typing" #~ msgstr "が入力を中断しました" #~ msgid "%i search results" #~ msgstr "%i 件の検索結果" #~ msgid "Discover real JIDs" #~ msgstr "JID の検索" #~ msgid "Who may discover real JIDs?" #~ msgstr "JID の検索を許可する対象" #~ msgid "Password required for room entry, if any" #~ msgstr "トークルームへの参加に必要なパスワード" #~ msgid "Failed connecting to %s" #~ msgstr "%s への接続に失敗しました" #~ msgid "Join Conference" #~ msgstr "トークルームに参加" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID は “user@example.com” の形式にしてください" #~ msgid "Copy Link Address" #~ msgstr "リンクアドレスをコピー" #~ msgid "Copy" #~ msgstr "コピー" #~ msgid "Select All" #~ msgstr "すべて選択" #~ msgid "Search" #~ msgstr "検索" #~ msgid "Start Chat" #~ msgstr "会話を開始" dino-0.5.0/main/po/kab.po0000664000000000000000000010426714776241610013626 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-10-03 12:15+0000\n" "Language-Team: none\n" "Language: kab\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.8-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Sefsex" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Tuqqna" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Fren" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Tugniwin" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Akk ifuyla" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Tebɣiḍ ad tekkseḍ amiḍan %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Yetteqqen…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Yeqqen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Isenser" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Awal n uɛeddi d armeɣtu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Tuccḍa" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Imiḍanen" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Rnu amiḍan" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Qqen" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Snulfu-d amiḍan" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Awal n uɛeddi neɣ isem n useqdac d arameɣtu" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Yir tansa" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Jerred deg %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Ldi asmel Web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Snulfu-d amiḍan" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Sewḥel" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Iɣewwaṛen" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Imttekkiyen" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Bab" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Anedbal" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Amedraw" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "yettwaẓreg" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ur yettwawgelhen ara" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Tura kan" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Nekki" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Iḍelli" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Aseqdac" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Snubeg" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Fren afaylu" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s sɣur %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Awal n uɛeddi" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Suter tasiregt" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Nadi deg iznan" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Tikamiṛatin" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Iṣawaḍen" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Imesmeɣren n yimesli" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Bdu" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Uḍfir" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Zeddi" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Rnu" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Ɣef Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Ass-a" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Agi" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Qbel" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Aggi" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Irmed" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Yexsi" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Suter" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Ansuf ɣer Dino !" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Ẓreg izen" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Ldi" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Sekles am…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Tiririt" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Afaylu" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tuɣalin" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Fakk" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Amatu" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Adiwenni" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Amiḍan" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Mefferisem" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Rnu anermis" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Azen" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Inegzumen n unasiw" #~ msgid "Remove" #~ msgstr "Kkes" #~ msgid "Notifications" #~ msgstr "Ilɣa" #~ msgid "Permissions" #~ msgstr "Tisirag" #~ msgid "Check spelling" #~ msgstr "Sefqed taɣdira" dino-0.5.0/main/po/ko.po0000664000000000000000000011651214776241610013476 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-10-17 00:56+0000\n" "Language-Team: none\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.15-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "취소" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "연결" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "아바타 선택" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "선택" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "이미지" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "모든 파일" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "%s 계정을 삭제하시겠습니까?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "연결 중…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "연결 됨" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "연결 해제 됨" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "잘못된 비밀번호" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "올바르지 않은 TLS 인증서" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "오류" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "계정" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "계정 추가" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "활성화 된 계정이 없습니다" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "가입" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "보안 인증서가 운영체제로부터 신뢰되지 않았습니다" #: main/src/windows/preferences_window/add_account_dialog.vala:147 #, fuzzy msgid "Its security certificate is issued to another domain." msgstr "보안 인증서가 다른 도메인으로부터 발급되었습니다." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "보안 인증서가 미래에 유효합니다." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "보안 인증서가 만료되었습니다." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "계정 생성" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "이제 %s 계정을 쓸 수 있습니다." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "잘못된 유저명이거나 비밀번호" #: main/src/windows/preferences_window/add_account_dialog.vala:227 #, fuzzy msgid "Something went wrong" msgstr "무엇인가 잘 못 되었습니다." #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "%s에 연결하지 못했습니다" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "잘못된 주소" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "서버로부터 응답이 없습니다" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "%s에 등록" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "이 서버는 웹사이트를 통한 가입만이 가능합니다" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "웹사이트 열기" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "등록" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "가입하는 법에 관한 정보는 %s를 확인하세요" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "차단" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "설정" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "방 설정" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "구성원들" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "소유자" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "관리자" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "구성원" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "파일이 서버 허용 최대 용량을 초과하였습니다." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "메세지가 너무 김" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "수정됨" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "대기중…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "전송 실패" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "비암호화" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "메세지를 보낼 수 없음" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 분 이전에" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "방금 전" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "나" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "이미지 전송 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "파일 전송 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "이미지 수신 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "파일 수신 됨" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "전화 송신 중" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "전화 수신 중" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "어제" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "대화 시작" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "사용자" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "초대" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "사적 대화 시작하기" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "추방" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "쓰기 권한 부여" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "쓰기 권한 회수" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "대화 그룹에 초대" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "파일 선택" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "방의 이름" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "방의 설명" #: main/src/ui/util/data_forms.vala:37 #, fuzzy msgid "Persistent" msgstr "유지성" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "방이 마지막 구성원이 나가도 유지됩니다" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "공개적으로 검색 가능" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "참여자가 주제를 바꿈" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JID를 볼 권한" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "누가 참여자들의 JID를 볼 수 있게 허용했습니까?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "비밀번호" #: main/src/ui/util/data_forms.vala:55 #, fuzzy msgid "Moderated" msgstr "조정됨" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "발언 가능한 참여자만이 메세지를 보낼 수 있습니다" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "회원 전용" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "회원만이 방에 들어올 수 있습니다" #: main/src/ui/chat_input/chat_input_controller.vala:217 #, fuzzy msgid "This conference does not allow you to send messages." msgstr "이 대화 그룹에서의 메세지 전송이 허가되지 않았습니다." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "권한 요청" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "파일 전송" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "전화 걸기" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "음성 통화" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "영상 통화" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "메세지 검색" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 #, fuzzy msgid "Debug information" msgstr "정보 디버그" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "전화 거는 중…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "전화 수신 중…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s 가 통화를 종료함" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s 가 통화를 거부함" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "카메라" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "카메라를 찾지 못했습니다." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "마이크" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "마이크를 찾지 못했습니다." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "스피커" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "스피커를 찾지 못했습니다." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "통화에 초대" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "시작" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "채널 참가" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "다음" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "참가" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "참가 중…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "방에 참가하기 위해 비밀번호가 필요합니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "참여하거나 만든 그룹 대화에서 추방됐습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "방이 존재하지 않습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "방 생성이 허용되지 않았습니다" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "회원 전용 방" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "다른 닉네임을 고르세요" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "방에 사용자가 너무 많습니다" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "추가" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dino에 대하여" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "오늘" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 개의 검색 결과" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s와 함께" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "영상 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "그룹 영상 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "그룹 통화 수신중" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "거부" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "허용" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "친구 추가 요청" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "거부" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s로 초대됨" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s가 %s로 초대함" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "권한 요구" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s가 %s에 쓰기 권한을 요청함" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "입력 중 알림 전송" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "읽음 알림 전송" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "켜기" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "끄기" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "기본값: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "요청" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "메세지 전송을 위한 권한 요청" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Dino에 오신 것을 환영합니다!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "로그인 또는 계정을 생성 하십시오." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "계정 설정" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "계정 관리" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "열기" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "로 저장…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s 그리고 %i 사람들이 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s와(과) %s, %s이(가) 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s와(과) %s이(가) 입력 중…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s 이(가) 입력 중…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "통화 시작 됨" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s 전에 시작 됨" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "다른 장치로 통화를 제어하고 있습니다" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "통화 종료 됨" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%s에 종료 됨" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, fuzzy, c-format msgid "Lasted %s" msgstr "마지막 내역 %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 #, fuzzy msgid "Call missed" msgstr "부재된 통화" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "통화를 받지 않았습니다" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s이(가) 통화를 받지 않음" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "통화 거부됨" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "통화를 거부했습니다" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s이(가) 통화를 거부했습니다" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "통화 실패" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i 시간" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i 분" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "몇 초 전" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "이 연락처가 당신을 연락처에 추가하길 원합니다" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%s로 다운로드 중…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, fuzzy, c-format msgid "%s offered: %s" msgstr "%s 전송 됨: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, fuzzy, c-format msgid "File offered: %s" msgstr "파일 전송됨: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "파일 전송 됨" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "전송 실패" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "연락처 세부 사항" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "메세지 수정" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "보안 연결을 설정 할 수 없음" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "이전" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "공용 서버 선택" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "또는 구체적인 서버 주소 입력" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "로그인 하기" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "모든 설정이 끝났습니다!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "완료" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "새로운 메세지 수신시에 알림" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 #, fuzzy msgid "Local alias" msgstr "내부 가칭" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "전역" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "열린 채팅이 없습니다" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "대화" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "창 제어" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "다음 대화로 넘어가기" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "이전 대화로 넘어가기" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "계정" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "닉네임" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "가칭" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "연락처 추가" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "현대 XMPP 채팅 클라이언트" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino는 데스크탑을 위한 현대 오픈소스 채팅 클라이언트입니다. 깔끔하고 신뢰 할 " "수 있는 Jabber/XMPP 경험을 개인정보 보호 중시와 함께 제공할 수 있도록 주력하" "고 있습니다." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO와 OpenPGP를 통한 종단간 암호화를 지원하며 프라이버시와 관련된 읽음 확인" "이나 입력 알림기능을 설정할 수 있습니다." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino는 서버와 타 장치와의 메세지 동기화로부터 기록을 불러옵니다." #: main/data/global_search.ui:27 #, fuzzy msgid "No active search" msgstr "검색 결과가 없음" #: main/data/global_search.ui:28 #, fuzzy msgid "Type to start a search" msgstr "검색하려면 입력하세요" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "해당하는 메세지가 없음" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "오타를 확인해보거나 필터를 제거해보세요" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "보내기" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "키보드 단축키" #~ msgid "Remove" #~ msgstr "삭제" #, c-format #~ msgid "Sign in to %s" #~ msgstr "%s에 로그인" #~ msgid "No accounts configured" #~ msgstr "어떤 계정도 설정되지 않음" #~ msgid "Add an account" #~ msgstr "계정 추가" #~ msgid "Pick another server" #~ msgstr "다른 서버 선택" #, fuzzy #~ msgid "Local Settings" #~ msgstr "일부 설정" #~ msgid "Notifications" #~ msgstr "알림" #~ msgid "Only when mentioned" #~ msgstr "언급 되었을 때만" #~ msgid "Permissions" #~ msgstr "권한" #~ msgid "Conference Details" #~ msgstr "대화 그룹 세부 사항" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "양방향에서의 소통과 상태 갱신이 차단됩니다" #~ msgid "A password to restrict access to the room" #~ msgstr "방에 접근을 제한하기 위한 비밀번호" #~ msgid "Message history" #~ msgstr "메세지 기록" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "최대 로그 수가 방에 의해 제한됩니다" #~ msgid "Convert smileys to emojis" #~ msgstr "웃음 모양을 이모티콘으로 변환" #~ msgid "Check spelling" #~ msgstr "맞춤법 확인" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "대화를 시작하거나 채널에 들어가려면 여기를 누르세요." dino-0.5.0/main/po/lb.po0000664000000000000000000011721014776241610013456 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: Luxembourgish (Dino)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-04-15 00:11+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Ofbriechen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Connectéieren" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Avatar auswielen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Auswielen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Biller" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "All Dateien" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Konto %s läschen?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Verbannen…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Verbonnen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Getrennt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Falsch Passwuert" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Invaliden TLS Zertifikat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Feeler" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Kontoen" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Account bäisetzen" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Keng Kontoen aktiv" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Aloggen" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Account kreéieren" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Du kanns elo den Account %s benotzen." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Falsche Benotzernumm oder Passwuert" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Eppes ass schif gaangen" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Konnt net op %s connectéieren" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ongülteg Adress" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Keng Äntwert vum Server" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Ob %s registréieren" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "De Server setzt eng Registréierung duerch eng Websäit viraus" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Websäit opmaachen" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registréieren" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Kuck %s fir Informatioune iwwert Registréierung" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blockéieren" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Astellungen" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Raum Astellungen" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Memberen" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Besëtzer" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrateur" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Member" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Noriicht ze laang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "beaarbecht" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Onverschlësselt" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Konnt de Message net schécken" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l:%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i Minutt hier" msgstr[1] "%i Minutten hier" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Just elo" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ech" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild geschéckt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Datei geschéckt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild kritt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Datei kritt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gëschter" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Konversatioun starten" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Benotzer" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Alueden" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Privat Conversatioun starten" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Erausgeheien" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invitéieren an Konferenz" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s vum %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Numm vum Raum" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Beschreiwung vum Raum" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Dauerhaft" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "De Raum bleift bestoen, nodeems de leschte Benotzer erausgaangen ass" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Ëffentlech siichtbar" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Benotzer däerfe Sujet änneren" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Erlabnis JIDs ze gesinn" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wien ass erlaabt den Gäscht hier JIDs ze gesinn?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Passwuert" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderéiert" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Nëmme Benotzer mat Stemm däerfe Messagë schécken" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Nëmme fir Memberen" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Nëmme Memberen däerfen de Raum betrieden" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Messagen duerchsichen" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Start" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Kanal bäitrieden" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Nächst" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Bäitrieden" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bäitrieden…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Passwuert gëtt gebraucht fir de Raum ze betrieden" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Gebannt vum bäitrieden an erstelle vu Konferenzen" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Raum existéiert net" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Net erlaabt ee Raum ze erstellen" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Raum ass nëmme fir Memberen" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wiel een anere Spëtznumm" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "De Raum huet ze vill Benotzer" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Bäisetzen" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Iwwert Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Haut" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i Sichresultat" msgstr[1] "%i Sichresultater" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "An %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Mat %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akzeptéieren" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abonnement Ufro" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Verweigeren" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Aluedung an %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s huet dech zu %s agelueden" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Tipp-Notifikatioune schécken" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Scheck Lies Bestätegungen" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Un" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Aus" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Wëllkomm bei Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Logg dech an oder kreéier een Account fir lass ze leen." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Konto opsetzen" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Kontoe managen" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, fuzzy, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s an %i anerer sinn um schreiwen" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s an %s sinn um schreiwen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s an %s sinn um schreiwen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s ass um schreiwen…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Dëse Kontakt wéilt dech gären an seng Kontaktlecht ophuelen" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Lueden %s erof…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s offréiert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Datei offréiert: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Datei offréiert" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Dateitransfert ass feelgeschloen" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Datei" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetailer" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "De Message updaten" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Konnt keng geséchert Verbindung opbauen" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Zeréck" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Ëffentleche Server auswielen" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Oder spezifizéier eng Server Adress" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Aloggen a Platz" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Alles ageriicht!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Ofschléissen" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Benoriichtege wann een neie Message ukënnt" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokalen Pseudonym" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Allgemeng" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du hues keng oppen Chats" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigatioun" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Zu nächster Konversatioun sprangen" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Zu viregter Konversatioun sprangen" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Spëtznumm" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonym" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kontakt bäisetzen" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Modernen XMPP Chat Client" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino ass e modernen, quell-offene Chat Client fir den Desktop. Hien biet eng " "opgeraumt a robust Jabber/XMPP Erfarung a leet ee Schwéierpunkt op " "Privatsphär." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Hien ënnerstëtz Enn-zu-Enn Verschlësselung mat OMEMO an OpenPGP an enthält " "Privatsphäre-Astellungen zu Liesbestätegungen an Tipp-Benoriichtegungen." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino rifft  Gespréichverläf vum Server of a synchroniséiert Noriichte mat " "anere Geräter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Keng aktiv Sich" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tipp fir eng Sich ze starten" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Keng Message fonnt" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Iwwerpréif Schreifweis oder läsch Filteren" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Astellungen" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Tastaturofkierzungen" #~ msgid "Remove" #~ msgstr "Läschen" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Bei %s umellen" #~ msgid "No accounts configured" #~ msgstr "Keng Kontoe konfiguréiert" #~ msgid "Add an account" #~ msgstr "Ee Konto bäisetzen" #~ msgid "Pick another server" #~ msgstr "Anere Server auswielen" #~ msgid "Local Settings" #~ msgstr "Lokal Astellungen" #~ msgid "Notifications" #~ msgstr "Notifikatiounen" #~ msgid "Only when mentioned" #~ msgstr "Nëmme wann erwäänt" #~ msgid "Conference Details" #~ msgstr "Konferenz Detailer" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Kommunikatioun a Status Updates sinn an all Richtung blockéiert" #~ msgid "A password to restrict access to the room" #~ msgstr "Ee Passwuert fir den Accès zu dësem Raum anzeschränken" #~ msgid "Message history" #~ msgstr "Messagen Historique" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maximal Unzuel vum ale Messagen, déi vum Raum geschéckt ginn" #~ msgid "Convert smileys to emojis" #~ msgstr "Smileyen an Emojien konvertéieren" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klick hei fir eng Konversatioun ze starten oder engem Channel " #~ "bäizetrieden." #~ msgid "No active conversations" #~ msgstr "Keng Conversatiounen aktiv" #~ msgid "Main window with conversations" #~ msgstr "Haaptfënster mat den Conversatiounen" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s an %i anerer" #~ msgid "You can now start using %s" #~ msgstr "Du kanns elo %s benotzen" #~ msgid "Open Registration" #~ msgstr "Oppe Registréierung" #~ msgid "Save" #~ msgstr "Späicheren" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s an %s" #~ msgid "%s and %s" #~ msgstr "%s an %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tippt grad…" #~ msgstr[1] "tippe grad…" #~ msgid "has stopped typing" #~ msgstr "huet opgehalen ze tippen" #~ msgid "%i search results" #~ msgstr "%i Sichresultater" #~ msgid "Discover real JIDs" #~ msgstr "Echt JIDs gesinn" #~ msgid "Who may discover real JIDs?" #~ msgstr "Ween däerf echt JIDs gesinn?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passwuert fir de Raum ze betrieden, wann een gebraucht gëtt" #~ msgid "Failed connecting to %s" #~ msgstr "Feeler beim connectéieren op %s" #~ msgid "Join Conference" #~ msgstr "Konferenz bäitrieden" #~ msgid "Communicate happiness." #~ msgstr "Gléck kommunizéieren." #~ msgid "Quit" #~ msgstr "Zoumaachen" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID sollt folgend Form hunn „user@example.com“" #~ msgid "Copy Link Address" #~ msgstr "Kopéier Link Adress" #~ msgid "Copy" #~ msgstr "Kopéieren" #~ msgid "Select All" #~ msgstr "Alles auswielen" #~ msgid "Search" #~ msgstr "Sichen" #~ msgid "Send message marker" #~ msgstr "Message Marker verschécken" #~ msgid "Start Chat" #~ msgstr "Chat starten" #~ msgid "Request presence updates" #~ msgstr "Presenz Updaten ufroen" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "" #~ "Dat heiten ass eng App-Notifikatioun. Klick de Knäppche fir ze ewech ze " #~ "maachen" dino-0.5.0/main/po/lt.po0000664000000000000000000012374214776241610013507 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-10 14:09+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.8-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Atsisakyti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Prisijungti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Išjungti paskyrą" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Įjungti paskyrą" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Pasirinkti avatarą" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Pasirinkti" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Paveikslai" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Visi failai" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Šalinti paskyrą %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Jungiamasi…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Prisijungta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Atsijungta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Neteisingas slaptažodis" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Negaliojantis TLS liudijimas" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Klaida" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Paskyros" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Išjungtos paskyros" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Pridėti paskyrą" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Aktyvių paskyrų nėra" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Prisijungti" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Serveriui nepavyko įrodyti, kad jis yra %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Jūsų operacinė sistema nepasitiki jo saugumo liudijimu." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Jo saugumo liudijimas yra išduotas kitam domenui." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Jo saugumo liudijimas pradės galioti tik ateityje." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Jo saugumo liudijimas nebegalioja." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Sukurti paskyrą" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Dabar galite naudoti paskyrą %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Neteisingas naudotojo vardas ar slaptažodis" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Kažkas nutiko" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Nepavyko prisijungti prie %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Neteisingas adresas" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Nėra atsakymo iš serverio" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registruotis %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Serveris reikalauja registruotis per internetinę svetainę" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Atverti internetinę svetainę" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registruotis" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Išsamesnei informacijai apie registraciją, žiūrėkite %s" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Užblokuoti naudotoją" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Užblokuoti visą domeną" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Atblokuoti" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Prisegta" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Prisegti" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Užblokuotas" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domenas užblokuotas" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokuoti" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Nutildyti pranešimus" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Pranešimai įjungti" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Pranešimai apie paminėjimus" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Pranešimai nutildyti" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Pranešimai išjungti" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Apie" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Nustatymai" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Kambario konfigūracija" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Šifravimas" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Nariai" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Savininkas" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administratorius" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Narys" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Failas viršija didžiausią įkėlimo į serverį dydį." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Žinutė per ilga" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redaguota" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "laukiama…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "pristatymas nepavyko" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifruota" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Nepavyko išsiųsti žinutės" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "prieš %i minutę" msgstr[1] "prieš %i minutes" msgstr[2] "prieš %i minučių" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Ką tik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Aš" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Paveikslas išsiųstas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Failas išsiųstas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Paveikslas gautas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Failas gautas" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Išeinantis skambutis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Įeinantis skambutis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Vakar" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Pradėti pokalbį" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Naudotojas" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Pakviesti" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Pradėti privatų pokalbį" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Išvaryti" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Suteikti leidimą rašyti" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Panaikinti leidimą rašyti" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Pakviesti į konferenciją" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Pasirinkti failą" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s iš %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Kambario pavadinimas" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Kambario aprašas" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Ilgalaikis" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Kambarys išliks netgi po to, kai išeis paskutinis lankytojas" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Viešai randamas" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Lankytojai gali keisti temą" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Leidimas žiūrėti JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kam leidžiama matyti lankytojų JID?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Slaptažodis" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderuojamas" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Tik lankytojai su balso teise gali siųsti žinutes" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Tik nariai" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Tik nariai gali užeiti į kambarį" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ši konferencija jums neleidžia siųsti žinučių." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Užklausti leidimo" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Siųsti failą" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Pradėti skambutį" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Garso skambutis" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Vaizdo skambutis" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Ieškoti žinučių" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Išsamiau apie pokalbį" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Užverti pokalbį" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Derinimo informacija" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Skambinama…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Skamba…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s baigė pokalbį" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s atmetė skambutį" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameros" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nerasta jokios kameros." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonai" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nerasta jokio mikrofono." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Garsiakalbiai" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nerasta jokių garsiakalbių." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Pakviesti į skambutį" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Pradėti" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Prisijungti prie kanalo" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Kitas" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Prisijungti" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Prisijungiama…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Norint užeiti į kambarį, reikalingas slaptažodis" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Uždrausta prisijungti ar sukurti konferenciją" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Kambario nėra" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Neleidžiama kurti kambarių" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Kambarys tik nariams" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Pasirinkti kitą slapyvardį" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Kambaryje per daug žmonių" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Pridėti" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Apie Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Šiandien" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%b %d, %a" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i paieškos rezultatas" msgstr[1] "%i paieškos rezultatai" msgstr[2] "%i paieškos rezultatų" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Ties %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Su %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Įeinantis vaizdo skambutis" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Įeinantis grupės vaizdo skambutis" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Įeinantis grupės skambutis" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Atmesti" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Priimti" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Prenumeratos užklausa" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Atmesti" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Pakvietimas į %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s pakvietė jus į %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Leidimo užklausa" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s prašo leidimo rašyti ties %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Siųsti pranešimus apie žinutės rašymą" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Siųsti pranešimus apie žinučių skaitymą" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Įjungta" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Išjungta" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Pagal numatymą: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Prašyti" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Prašyti leidimo siųsti žinutes" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Sveiki atvykę į Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Norėdami pradėti, prisijunkite arba susikurkite paskyrą." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Nusistatyti paskyrą" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Tvarkyti paskyras" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP adresas" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Atvaizduojamas vardas" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Tema" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Taisyti žinutę" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Jūs" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Pridėti reakciją" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Atidaryti" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Išsaugoti kaip…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ir dar %i rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ir %s rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s ir %s rašo…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s rašo…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Pokalbis prasidėjo" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Pradėta prieš %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Šį skambutį atlikote kitame įrenginyje" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Pokalbis baigėsi" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Baigėsi %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Truko %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Praleistas skambutis" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Praleidote šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s praleido šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Skambutis atmestas" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Atmetėte šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s atmetė šį skambutį" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Skambutis nepavyko" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i valandą" msgstr[1] "%i valandas" msgstr[2] "%i valandų" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minutę" msgstr[1] "%i minutes" msgstr[2] "%i minučių" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "kelias sekundes" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Pristatyta" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Perskaityta" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Šis adresatas norėtų jus pridėti į savo adresatų sąrašą" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Šis pokalbis nepalaiko reakcijų." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Atsakyti" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Šis pokalbis nepalaiko atsakymų." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Atsisiunčiama %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s pasiūlė: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Pasiūlytas failas: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Pasiūlytas failas" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Failo persiuntimas nepavyko" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Failas" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nauja" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Išsamiau apie adresatą" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atnaujinti žinutę" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Nepavyko užmegzti saugaus ryšio" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Atgal" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Pasirinkite viešąjį serverį" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Arba nurodykite serverio adresą" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Vietoj to, prisijungti" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Viskas nustatyta!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Užbaigti" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Siųsti pranešimus apie _rašymą" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Siųsti pranešimus apie _skaitymą" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "Pra_nešimai" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Pranešti, kai gaunama nauja žinutė" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Konvertuoti šypsenėles į šypsniukus" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Vietinis pseudonimas" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Ryšio būsena" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Šalinti paskyrą" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Bendri" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Neturite jokių atvertų pokalbių" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Spustelėkite „+“ norėdami pradėti pokalbį ar prisijungti prie kanalo" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Trumpiniai" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Pokalbis" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Naršymas" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Peršokti prie kito pokalbio" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Peršokti prie ankstesnio pokalbio" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Paskyra" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Slapyvardis" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonimas" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Pridėti adresatą" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Šiuolaikinė XMPP pokalbių kliento programa" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino yra šiuolaikinė atvirojo kodo kliento programa skirta darbalaukiui. Jos " "pagrindinis dėmesys yra pateikti tvarkingą ir patikimą Jabber/XMPP patyrimą " "nepamirštant apie jūsų privatumą." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ji palaiko ištisinį šifravimą naudojant OMEMO ir OpenPGP bei leidžia " "konfigūruoti su privatumu susijusias ypatybes, tokias kaip pranešimus apie " "žinučių skaitymą ir rašymą." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino gauna istoriją iš serverio ir sinchronizuoja žinutes su kitais " "įrenginiais." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nėra aktyvios paieškos" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Rašykite norėdami atlikti paiešką" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nėra atitinkančių žinučių" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Patikrinkite rašybą arba pabandykite pašalinti filtrus" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Siųsti" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Nuostatos" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Trumpiniai" #~ msgid "Remove" #~ msgstr "Šalinti" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Prisijungti prie %s" #~ msgid "No accounts configured" #~ msgstr "Nėra sukonfigūruotų paskyrų" #~ msgid "Add an account" #~ msgstr "Pridėti paskyrą" #~ msgid "Pick another server" #~ msgstr "Pasirinkti kitą serverį" #~ msgid "Local Settings" #~ msgstr "Vietiniai nustatymai" #~ msgid "Notifications" #~ msgstr "Pranešimai" #~ msgid "Pin conversation" #~ msgstr "Prisegti pokalbį" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Prisega pokalbį prie pokalbių sąrašo viršaus" #~ msgid "Only when mentioned" #~ msgstr "Tik paminėjus" #~ msgid "Permissions" #~ msgstr "Leidimai" #~ msgid "Conference Details" #~ msgstr "Išsamiau apie konferenciją" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Abiejomis kryptimis bus blokuojamas bendravimas ir būsenos atnaujinimai" #~ msgid "A password to restrict access to the room" #~ msgstr "Slaptažodis, apribojantis prieigą prie kambario" #~ msgid "Message history" #~ msgstr "Žinučių istorija" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Didžiausias kambario išduodamas praeities žurnalo skaičius" #~ msgid "Convert smileys to emojis" #~ msgstr "Keisti šypsenėles į jaustukus" #~ msgid "Check spelling" #~ msgstr "Tikrinti rašybą" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Spustelėkite čia norėdami pradėti pokalbį ar prisijungti prie kanalo." #~ msgid "Video call incoming" #~ msgstr "Įeinantis vaizdo skambutis" #~ msgid "Call incoming" #~ msgstr "Įeinantis skambutis" #~ msgid "Establishing call" #~ msgstr "Rengiamasi pokalbiui" #~ msgid "Video call establishing" #~ msgstr "Rengiamasi vaizdo skambučiui" #~ msgid "Call establishing" #~ msgstr "Rengiamasi pokalbiui" #~ msgid "Call in progress…" #~ msgstr "Vyksta pokalbis…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Truko %s" #~ msgid "seconds" #~ msgstr "sek." #~ msgid "No active conversations" #~ msgstr "Aktyvių pokalbių nėra" #~ msgid "Main window with conversations" #~ msgstr "Pagrindinis langas su pokalbiais" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s ir dar %i" #~ msgid "You can now start using %s" #~ msgstr "Dabar, galite pradėti naudoti %s" #~ msgid "Open Registration" #~ msgstr "Atverti registraciją" #~ msgid "Save" #~ msgstr "Įrašyti" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ir %s" #~ msgid "%s and %s" #~ msgstr "%s ir %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "rašo…" #~ msgstr[1] "rašo…" #~ msgstr[2] "rašo…" #~ msgid "has stopped typing" #~ msgstr "nustojo rašyti" #~ msgid "%i search results" #~ msgstr "Paieškos rezultatų: %i" #~ msgid "Discover real JIDs" #~ msgstr "Atrasti tikruosius JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kas gali atrasti tikruosius JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "" #~ "Jei nustatytas, norint užeiti į kambarį bus reikalaujama slaptažodžio" dino-0.5.0/main/po/lv.po0000664000000000000000000010340714776241610013505 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-02-10 18:35+0000\n" "Language-Team: none\n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n % 10 == 0 || n % 100 >= 11 && n % 100 <= " "19) ? 0 : ((n % 10 == 1 && n % 100 != 11) ? 1 : 2);\n" "X-Generator: Weblate 4.16-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Atcelt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Izvēlēties" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Īpašnieks" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrators" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Ziņojums ir pārāk garš" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "rediģēja" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "neizdevās nosūtīt" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Nešifrēts" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Tikko" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Es" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Attēls nosūtīts" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fails nosūtīts" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Saņemts attēls" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Saņemts fails" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Izejošais zvans" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Ienākošais zvans" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Vakar" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Lietotājs" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Piešķirt rakstīšanas tiesības" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Atsaukt rakstīšanas tiesības" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Uzaicināt uz konferenci" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Izvēlieties failu" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s no %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "" msgstr[1] "" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" msgstr[1] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "" dino-0.5.0/main/po/meson.build0000664000000000000000000000002514776241610014656 0ustar rootrooti18n.gettext('dino') dino-0.5.0/main/po/nb.po0000664000000000000000000012206414776241610013463 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-01-24 00:10+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.4-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Avbryt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Koble til" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Velg avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Velg" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Bilder" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Alle filer" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Fjern konto %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Kobler til…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Tilkoblet" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Frakoblet" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Feil passord" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ugyldig TLS-sertifikat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Feil" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Kontoer" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Legg til konto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Ingen kontoer aktive" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Logg inn" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Tjeneren kunne ikke bevise at det er %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Ditt operativsystem mangler tiltro til dets sikkerhetssertifikat." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Dets sikkerhetssertifikat er utstedt til et annet domene." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Dets sikkerhetssertifikat vil kun bli gyldig i fremtiden." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Dets sikkerhetssertifikat er utløpt." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Opprett konto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Du kan nå bruke kontoen %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Feil brukernavn eller passord" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Noe gikk galt" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Kunne ikke koble til %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ugyldig adresse" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Inget svar fra tjener" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrer på %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Tjeneren krever at man logger inn via en nettside" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Åpne nettside" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrer" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Se %s for å lære hvordan du registrerer deg" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokker" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Innstillinger" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Rom-oppsett" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Medlemmer" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eier" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Medlem" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Filen overskrider tjenerens maksimale opplastingsstørrelse." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Meldingen er for lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redigert" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "venter …" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "levering mislyktes" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Ukryptert" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Kunne ikke sende melding" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minutt siden" msgstr[1] "%i minutter siden" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Akkurat nå" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Meg" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bilde sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil sendt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bilde mottatt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil mottatt" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Utgående anrop" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Innkommende anrop" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "I går" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Start samtale" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Bruker" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Inviter" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Start privat samtale" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kast ut" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Innvilg skrivetilgang" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Tilbakekall skrivetilgang" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Inviter til konferanse" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Velg fil" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s fra %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Romnavn" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Rombeskrivelse" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Vedvarende" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Rommet vil vedvare etter at siste deltager forlater" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Offentlig søkbart" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Deltagere kan endre emne" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Tilgang til visning av JID-er" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Hvem skal tillates å vise brukernes JID-er?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Passord" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderert" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Bare deltagere med stemme kan sende meldinger" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Kun for medlemmer" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Kun medlemmer har adgang til rommet" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Du tillates ikke å sende meldinger i denne konferansen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Forespør tilgang" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Send fil" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Start samtale" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Lydsamtale" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videoanrop" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Søk i meldinger" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Feilsøkingsinformasjon" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Ringer …" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Ringer …" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s avsluttet samtalen" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s avslo samtalen" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Intet kamera funnet." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofoner" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ingen mikrofon funnet." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Høyttalere" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ingen høytaler funnet." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Inviter til samtale" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Start" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Ta del i kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Neste" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Ta del" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Tar del…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Passord kreves for å ta del i rommet" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Bannlyst fra å ta del i eller å opprette konferanser" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Rommet finnes ikke" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Ikke tillatt å opprette rom" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Rommet er kun for medlemmer" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Velg et annet kallenavn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Rommet har for mange deltagere" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Legg til" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Om Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "I dag" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i søkeresultat" msgstr[1] "%i søkeresultater" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "I %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Med %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Innkommende videosamtale" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Innkommende videogruppeanrop" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Innkommende gruppeanrop" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Avvis" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Godta" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abonnementsforespørsel" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Nekt" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitasjon til %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s inviterte deg til %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Tilgangsforespørsel" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ber om tillatelse til å skrive i %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Send skrivevarsling" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Send meldingskvitteringer" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "På" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Av" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Forvalg: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Forespør" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Forespør tilgang til meldingsforsendelse" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Velkommen til Dino." #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Logg inn eller opprett en konto for å starte." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Sett opp konto" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Behandle kontoer" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Rediger melding" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Du" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Legg til reaksjon" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Åpne" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Lagre som …" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s og %i andre skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s og %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s skriver…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Samtale startet" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Startet for %s siden" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Du håndterte denne samtalen på en annen enhet" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Samtale avsluttet" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Avsluttet %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Varte %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Tapt anrop" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Du besvarte ikke dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s tok ikke dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Anrop avvist" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Du avslo dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s avslo dette anropet" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Anrop mislyktes" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "én time" msgstr[1] "%i timer" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "ett minutt" msgstr[1] "%i minutter" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "et par sekunder" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Levert" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Lest" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Denne kontakten ønsker å legge deg til på kontaktlisten sin" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Denne samtalen støtter ikke reaksjoner." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Svar" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Denne samtalen støtter ikke svar." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Laster ned %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s tilbød: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fil tilbudt: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fil tilbudt" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Filoverføring mislyktes" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fil" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetaljer" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Oppdater melding" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Kunne ikke etablere sikker forbindelse" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tilbake" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Velg en offentlig tjener" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Eller angi en tjeneradresse" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Logg inn istedenfor" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Ferdig oppsatt." #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Fullfør" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Varsle ved mottak av ny melding" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokalt alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Generelt" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du har ingen åpne chatter" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klikk på + for å starte en chat eller bli med i en kanal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Samtale" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigasjon" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Hopp til neste samtale" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Hopp til forrige samtale" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Kallenavn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Legg til kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moderne XMPP-sludreklient" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino er en moderne friporg-sludringsklient for skrivebordet. Det fokuserer " "på rask og pålitelig XMPP-opplevelse, samtidig som det hegner om " "personvernet." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Det støtter ende-til-ende -kryptering med OMEMO og OpenPGP, og tillater " "oppsett av personvernsrelaterte funksjoner som meldingskvitteringer og " "skrivevarsling." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino henter historikk fra tjeneren og synkroniserer meldinger med andre " "enheter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Inget aktivt søk" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skriv for å starte et søk" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Ingen samsvarende meldinger" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Sjekk stavingen eller prøv å fjerne filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Send" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Innstillinger" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Tastatursnarveier" #~ msgid "Remove" #~ msgstr "Fjern" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Logg inn på %s" #~ msgid "No accounts configured" #~ msgstr "Ingen kontoer satt opp" #~ msgid "Add an account" #~ msgstr "Legg til en konto" #~ msgid "Pick another server" #~ msgstr "Velg en annen tjener" #~ msgid "Local Settings" #~ msgstr "Lokale innstillinger" #~ msgid "Notifications" #~ msgstr "Merknader" #~ msgid "Pin conversation" #~ msgstr "Fest samtale" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fester samtalen øverst i samtalelisten" #~ msgid "Only when mentioned" #~ msgstr "Bare når nevnt" #~ msgid "Permissions" #~ msgstr "Tilganger" #~ msgid "Conference Details" #~ msgstr "Konferansedetaljer" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Kommunikasjon og statusoppdateringer i begge retninger er blokkert" #~ msgid "A password to restrict access to the room" #~ msgstr "Et passord for å begrense tilgang til rommet" #~ msgid "Message history" #~ msgstr "Meldingshistorikk" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maksimalt mengde historikk som skal utstedes av rommet" #~ msgid "Convert smileys to emojis" #~ msgstr "Konverter smilefjes til emoji-er" #~ msgid "Check spelling" #~ msgstr "Sjekk staving" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klikk her for å starte en samtale, eller ta del i en kanal." #~ msgid "No active conversations" #~ msgstr "Ingen kontoer aktive" #~ msgid "Main window with conversations" #~ msgstr "Hovedvindu med samtaler" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s og %i andre" #~ msgid "You can now start using %s" #~ msgstr "Du kan nå begynne å bruke %s" #~ msgid "Open Registration" #~ msgstr "Åpne registrering" #~ msgid "Save" #~ msgstr "Lagre" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s og %s" #~ msgid "%s and %s" #~ msgstr "%s og %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "skriver…" #~ msgstr[1] "skriver…" #~ msgid "has stopped typing" #~ msgstr "har tatt pause i skrivinga" #~ msgid "%i search results" #~ msgstr "%i søkeresultater" #~ msgid "Discover real JIDs" #~ msgstr "Oppdag ekte JID-er" #~ msgid "Who may discover real JIDs?" #~ msgstr "Hvem har tilgang til oppdagelse av ekte JID-er?" #~ msgid "Password required for room entry, if any" #~ msgstr "Passord som kreves for å ta del i rommet. La stå tomt for inget" #~ msgid "Failed connecting to %s" #~ msgstr "Klarte ikke å koble til %s" #~ msgid "Join Conference" #~ msgstr "Ta del i konferanse" #~ msgid "Communicate happiness." #~ msgstr "Kommuniser glede." #~ msgid "Quit" #~ msgstr "Avslutt" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID skal være av typen \"bruker@eksmepel.no\"" #~ msgid "Copy Link Address" #~ msgstr "Kopier lenkeadresse" #~ msgid "Copy" #~ msgstr "Kopier" #~ msgid "Select All" #~ msgstr "Velg alle" #~ msgid "Search" #~ msgstr "Søk" #~ msgid "Send message marker" #~ msgstr "Send meldingsmarkør" #~ msgid "Start Chat" #~ msgstr "Start sludring" #~ msgid "Request presence updates" #~ msgstr "Forespør tilstedeværelsesoppdateringer" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Dette er en merknad i programmet. Klikk på knappen for å avvise" #~ msgid "Join on startup" #~ msgstr "Ta del ved oppstart" #~ msgid "Add Chat" #~ msgstr "Legg til sludring" dino-0.5.0/main/po/nl.po0000664000000000000000000012526314776241610013501 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-02-25 04:41+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.2-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Annuleren" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Verbinden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Account uitschakelen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Account inschakelen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Kies een profielfoto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Kiezen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Afbeeldingen" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Alle bestanden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Wil je '%s' verwijderen?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Bezig met verbinden…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Verbonden" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Verbinding verbroken" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Onjuist wachtwoord" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ongeldig TLS-certificaat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Fout" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Accounts" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Uitgeschakelde accounts" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Account toevoegen" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Geen actieve accounts" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Inloggen" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "De server kan niet aantonen dat het %s is." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Je besturingssysteem vertrouwt het beveiligingscertificaat niet." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Het beveiligingscertificaat is afgegeven voor een ander domein." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Het beveiligingscertificaat is pas geldig vanaf een latere datum." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Het beveiligingscertificaat is verlopen." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Account aanmaken" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Je kunt '%s' nu gebruiken." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Onjuiste gebruikersnaam of wachtwoord" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Er is iets misgegaan" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Kan geen verbinding maken met %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ongeldig adres" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Geen antwoord van server" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registreren op %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "De server vereist registratie via een website" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Website openen" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registreren" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Ga naar %s voor meer informatie over het registreren" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Gebruiker blokkeren" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blokkeer volledig domein" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Deblokkeren" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Vastgezet" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Vastzetten" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Geblokkeerd" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domein is geblokkeerd" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blokkeren" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Dempen" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificaties aangezet" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificatie voor vermeldingen" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Gedempt" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificaties uitgeschakeld" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Over" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Instellingen" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Ruimte instellingen" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Encryptie" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Deelnemers" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Eigenaar" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Beheerder" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Lid" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Het bestand overschrijdt de maximaal toegestane upload grootte." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Het bericht is te lang" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "bewerkt" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "in wachtrij…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "verzenden mislukt" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Onversleuteld" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Het bericht kan niet worden verstuurd" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuut geleden" msgstr[1] "%i minuten geleden" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Zojuist" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Afbeelding verstuurd" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Bestand verstuurd" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Afbeelding ontvangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Bestand ontvangen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Uitgaande oproep" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Inkomende oproep" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Gisteren" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Gesprek starten" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Gebruiker" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Uitnodigen" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Privégesprek starten" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Wegsturen" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Schrijfmachtiging verlenen" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Schrijfmachtiging weigeren" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Uitnodigen voor groepsgesprek" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Kies een bestand" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s op %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Naam van groepsgesprek" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Beschrijving van groepsgesprek" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Blijvend" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Het groepsgesprek blijft bestaan als de laatste deelnemer het verlaat" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Openbaar" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Deelnemers mogen het onderwerp aanpassen" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Toestemming om JID's te bekijken" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Wie mag de JID's bekijken?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Wachtwoord" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Gemodereerd" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Enkel deelnemers met stem mogen berichten sturen" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Enkel leden" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Enkel leden mogen deelnemen aan het groepsgesprek" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "In deze conferentie mag je geen berichten versturen." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Toestemming vragen" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Verstuur een bestand" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Bellen" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Audiogesprek" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videogesprek" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Zoeken naar berichten" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Gesprek details" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Gesprek sluiten" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Foutopsporingsinformatie" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Bezig met bellen…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Bezig met overgaan…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s heeft de oproep beëindigd" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s hebt/heeft de oproep geweigerd" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camera's" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Geen camera aangetroffen." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoons" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Geen microfoon aangetroffen." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Luidsprekers" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Geen luidspreker aangetroffen." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Uitnodigen voor gesprek" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Starten" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Deelnemen aan kanaal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Volgende" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Deelnemen" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Bezig met deelnemen…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Wachtwoord voor groepsgesprek vereist" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Deelnemen aan of aanmaken van groepsgesprekken verboden" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Groepsgesprek bestaat niet" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Aanmaken van groepsgesprek niet toegestaan" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Groepsgesprek is alleen voor leden" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Kies een andere bijnaam" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Groepsgesprek heeft te veel deelnemers" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Toevoegen" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Over Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Vandaag" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i zoekresultaat" msgstr[1] "%i zoekresultaten" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Op %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Met %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Inkomende video-oproep" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Inkomend videogroepsgesprek" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Inkomend groepsgesprek" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Afwijzen" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Toestaan" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abonneerverzoek" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Weigeren" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Uitnodiging voor %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s heeft je uitgenodigd voor %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Machtigingsverzoek" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s verzoekt toestemming om te mogen schrijven in %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Aan-het-typenmeldingen versturen" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Leesbevestigingen versturen" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Aan" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Uit" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Standaard: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Verzoek sturen" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Vraag toestemming om berichten te versturen" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Welkom bij Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Log in of maak een account om aan de slag te gaan." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Account instellen" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Accounts beheren" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP adres" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Gebruikersnaam" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Onderwerp" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Bericht bewerken" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Jij" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Reactie toekennen" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Openen" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Opslaan als…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s en %i anderen zijn aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s en %s zijn aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s zijn %s aan het typen…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s is aan het typen…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Oproep gestart" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Begonnen: %s geleden" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Deze oproep is afgehandeld op een ander apparaat" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Het gesprek is beëindigd" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Beëindigd: %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Duur: %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Je hebt deze oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s heeft deze oproep gemist" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Je hebt deze oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s heeft deze oproep geweigerd" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Oproep mislukt" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i uur" msgstr[1] "%i uur" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuut" msgstr[1] "%i minuten" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "enkele seconden" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Afgeleverd" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Gelezen" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Deze contactpersoon wil je toevoegen aan zijn/haar lijst" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Dit gesprek ondersteunt geen reacties." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Beantwoorden" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Dit gesprek ondersteunt geen antwoorden." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Bezig met downloaden van %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s biedt aan: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Bestand aangeboden: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Bestand aangeboden" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Bestandsoverdracht mislukt" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Bestand" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nieuw" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Contactpersooninformatie" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Bericht bijwerken" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Kan geen beveiligde verbinding opzetten" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Terug" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Kies een openbare server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Of voer een serveradres in" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Inloggen op account" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Klaar!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Voltooien" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Verzend schrijf notificaties" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Verzend _Gelezen melding" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificaties" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Melding tonen bij nieuw bericht" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Zet smileys om naar emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokale alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Verbinding status" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Verwijder account" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Algemeen" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Je hebt geen geopende gesprekken" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klik op ‘+’ om een gesprek te starten of deel te nemen aan een kanaal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Sneltoetsen" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Gesprek" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigatie" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ga naar volgend gesprek" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ga naar vorig gesprek" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Account" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Bijnaam" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Contactpersoon toevoegen" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Een moderne XMPP-chatclient" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino is een moderne, vrije chattoepassing voor je computer. Dino biedt een " "eenvoudige en betrouwbare Jabber-/XMPP-ervaring, met privacy in het " "achterhoofd." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Dino ondersteunt end-to-endversleuteling met OMEMO en OpenPGP en staat je " "toe privacy-gerelateerde functies, zoals leesbevestigingen en aan-het-" "typenmeldingen, in te stellen." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino haalt de geschiedenis op van de server en synchroniseert berichten met " "andere apparaten." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Geen actieve zoekopdracht" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Typ om te beginnen met zoeken" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Geen overeenkomende berichten" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Controleer de spelling of verwijder filters" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Versturen" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Voorkeuren" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Sneltoetsen" #~ msgid "Remove" #~ msgstr "Verwijderen" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Inloggen op %s" #~ msgid "No accounts configured" #~ msgstr "Geen accounts ingesteld" #~ msgid "Add an account" #~ msgstr "Account toevoegen" #~ msgid "Pick another server" #~ msgstr "Andere server kiezen" #~ msgid "Local Settings" #~ msgstr "Lokale instellingen" #~ msgid "Notifications" #~ msgstr "Meldingen" #~ msgid "Pin conversation" #~ msgstr "Gesprek vastmaken" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Maakt het gesprek bovenaan de gesprekslijst vast" #~ msgid "Only when mentioned" #~ msgstr "Alleen bij vermeldingen" #~ msgid "Permissions" #~ msgstr "Machtigingen" #~ msgid "Conference Details" #~ msgstr "Groepsgesprekinformatie" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Communicatie en statusupdates worden in beide richtingen geblokkeerd" #~ msgid "A password to restrict access to the room" #~ msgstr "Een wachtwoord om toegang tot het gesprek te beperken" #~ msgid "Message history" #~ msgstr "Berichtgeschiedenis" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Het maximumaantal berichten dat het groepsgesprek toont" #~ msgid "Convert smileys to emojis" #~ msgstr "Smileys omzetten naar emoji’s" #~ msgid "Check spelling" #~ msgstr "Spelling controleren" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Klik hier om een gesprek te starten of deel te nemen aan een kanaal." #~ msgid "Video call incoming" #~ msgstr "Inkomend videogesprek" #~ msgid "Call incoming" #~ msgstr "Inkomende oproep" #~ msgid "Establishing call" #~ msgstr "Bezig met verbinden…" #~ msgid "Video call establishing" #~ msgstr "Bezig met verbinden…" #~ msgid "Call establishing" #~ msgstr "Bezig met verbinden…" #~ msgid "Call in progress…" #~ msgstr "Bezig met bellen…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Duur: %s" #~ msgid "seconds" #~ msgstr "seconden" #~ msgid "No active conversations" #~ msgstr "Geen gesprekken actief" #~ msgid "Main window with conversations" #~ msgstr "Hoofdvenster met gesprekken" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s en %i anderen" #~ msgid "You can now start using %s" #~ msgstr "U kunt %s nu beginnen gebruiken" #~ msgid "Open Registration" #~ msgstr "Open registratie" #~ msgid "Save" #~ msgstr "Opslaan" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s en %s" #~ msgid "%s and %s" #~ msgstr "%s en %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "is aan het typen…" #~ msgstr[1] "zijn aan het typen…" #~ msgid "has stopped typing" #~ msgstr "is gestopt met typen" #~ msgid "%i search results" #~ msgstr "%i zoekresultaten" #~ msgid "Discover real JIDs" #~ msgstr "Zichtbaarheid van JID’s" #~ msgid "Who may discover real JIDs?" #~ msgstr "Wie mag de JID’s van de deelnemers zien?" #~ msgid "Password required for room entry, if any" #~ msgstr "Wachtwoord voor groepsgesprek, indien nodig" #~ msgid "Failed connecting to %s" #~ msgstr "Verbinden met %s mislukt" #~ msgid "Join Conference" #~ msgstr "Deelnemen aan groepsgesprek" #~ msgid "Communicate happiness." #~ msgstr "Communiceer geluk." #~ msgid "Quit" #~ msgstr "Afsluiten" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID moet de vorm ‘gebruiker@voorbeeld.nl’ volgen" #~ msgid "Copy Link Address" #~ msgstr "Verwijzingsadres kopiëren" #~ msgid "Copy" #~ msgstr "Kopiëren" #~ msgid "Select All" #~ msgstr "Alles selecteren" #~ msgid "Search" #~ msgstr "Zoeken" #~ msgid "Send message marker" #~ msgstr "Berichtmarkers sturen" #~ msgid "Start Chat" #~ msgstr "Gesprek beginnen" #~ msgid "Request presence updates" #~ msgstr "Vraag om aanwezigheidsupdates" #~ msgid "This is an app-notification. Click the button to dismiss" #~ msgstr "Dit is een melding. Klik om te sluiten" #~ msgid "Join on startup" #~ msgstr "Deelnemen bij opstarten" #~ msgid "Add Chat" #~ msgstr "Gesprek beginnen" dino-0.5.0/main/po/oc.po0000664000000000000000000012212314776241610013461 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anullar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Connexion" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desactivar lo compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activar lo compte" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Seleccionar l’avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Seleccionar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imatges" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Totes los fichièrs" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Volètz suprimir lo compte %s ?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Connexion…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Connectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconnectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Marrit senhal" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Lo certificat TLS es invalid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Error" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Comptes" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Ajustar un compte" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "I a pas cap de compte actiu" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "S’identificar" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Lo servidor a pas pogut provar qu’es %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Vòstre sistèma operatiu se fisa pas del certificat prepausat." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Son certificat de seguretat foguèt provesit per un autre domeni." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Son certificat de seguretat serà pas que valid dins lo futur." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Son certificat a expirat." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Crear un compte" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Ara podètz utilizar lo compte %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Marrit nom d’utilizaire o senhal" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Quicòm a trucat" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Connexion impossibla a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "L’adreça es invalida" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Cap de responsa del servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Se marcar sus %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Lo servidor requesís una inscripcion via lo site web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Dobrir lo site web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Se marcar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Consultatz %s per mai d’informacions tocant cossí se marchar" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blocar" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Paramètres" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuracion de la sala" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membres" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietari" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrador" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membre" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" "Lo fichièr es mai pesuc que la talha maximala de mandadís sul servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Lo messatge es tròp long" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "modificat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "mandadís…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "fracàs del mandadís" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Pas chifrat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Mandadís del messatge impossible" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "fa %i minuta" msgstr[1] "fa %i minutas" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Ara meteis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ieu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imatge enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fichièr enviat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imatge recebuda" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fichièr recebut" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Sonada sortissenta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Sonada dintranta" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ièr" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Començar la discussion" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizaire" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Començar una discussion privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Autorizar l’escritura" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revocar l’autorizacion d’ecritura" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar a la conferéncia" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Seleccionar fichièr" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nom de la sala" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descripcion de la sala" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistent" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "La sala demorarà après la sortida del darrièr ocupant" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Discussion publica" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Los ocupants pòdon cambiar lo subjècte" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permission de veire los JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Qual pòt veire los JID dels participants ?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Senhal" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderada" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Solament los ocupant amb votz pòdon enviar de messatges" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Membres solament" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Solament los membres pòdon dintrar a la sala" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Aquesta conferéncia vos autoriza pas a enviar de messatges." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Demanda d’autorizacion" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Enviar un fichièr" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Aviar una sonada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Sonada àudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Sonada vidèo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Cercar de messatges" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informacions de desbugatge" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Sonada…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Tinda…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s a arrestat la sonada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s a regetat la sonada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camèras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Cap de camèra pas trobada." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfòns" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Cap de microfòn pas trobat." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Nautparlaires" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Cap de nautparlaire pas trobat." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar a la sonada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Començar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Rejónher una sala" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Seguent" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Rejónher" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Connexion a la sala…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Senhal requesit per dintrar dins la sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Sètz forabandit e podètz pas participar o crear de conferéncias" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "La sala existís pas" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Creacion de sala defenduda" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala solament pels membres" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Causissètz un escais-nom diferent" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Tròp de participants a la sala" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ajustar" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "A prepaus de Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Uèi" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultat de recèrca" msgstr[1] "%i resultats de recèrca" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "En %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Amb %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Sonada vidèo dintranta" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Sonada vidèo de grop dintranta" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Sonada de grop dintranta" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Regetar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Demanda d’abonament" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Regetar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Convit a %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s vos a convidat a %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Demanda d’autorizacion" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s demanda l’autorizacion d’escriure dins %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Enviar una notificacion quand escrivètz" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Enviar un acusat de lectura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Activat" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Desactivat" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Per defaut : %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Demandar" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Demandar la permission d’enviar de messatges" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "La benvenguda a Dino !" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Connectatz-vos o creatz un compte per començar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurar lo compte" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gerir los comptes" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Modificar lo messatge" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Vos" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Ajustar una reaccion" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Dobrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Enregistrar jos…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i son a escriure…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s son a escriure…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s escrivon…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s escriu…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Sonada començada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Començat fa %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Avètz pres aquesta sonada d’un autre aparelh estant" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Sonada acabda" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Acabat a %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durada %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Sonada mancada" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Avètz mancat aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s a mancat una sonada" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Sonada regetada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Avètz regetada aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s a regetada aquesta sonada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Sonada fracassada" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i ora" msgstr[1] "%i oras" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuta" msgstr[1] "%i minutas" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "qualques segondas" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Recebut" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Legit" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Aqueste contacte volriá vos ajustar a sa lista de contactes" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Aquesta conversacion gerís pas las reaccions." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Respondre" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Aquesta conversacion gerís pas las responsas." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Telecargament %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s a ofrit : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fichièr ofrit : %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fichièr ofrit" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Fracàs del transferiment de fichièr" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fichièr" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhs del contacte" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizar lo messatge" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Establiment impossible d’una connexion segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tornar" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Causissètz un servidor public" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "O indicatz l’adreça d’un servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Connectatz-vos allòc" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tot es prèst !" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Terminar" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "M’avisar quand un nòu messatge arriba" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "General" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Avètz pas cap conversacion dobèrta" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Clicatz per començar una conversacion o rejónher un salon" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Discussion" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegacion" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Sautar la discussion seguenta" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Sautar a la discussion precedenta" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Compte" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Escais-nom" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Ajustar un contacte" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Client XMPP modèrn" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino es un client de chat liure e modèrn per l’ordenador de burèu. Ensaja de " "provesir una experiéncia neta e fisabla de Jabber/XMPP en téner en compte " "vòstra confidencialitat." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Compatible amb lo chiframent OMEMO e OpenPGP del cap a la fin e permet de " "configurar de foncionalitats ligadas amb la confidencialitat coma los " "acusats de lectura e las notificacions d’escritura." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino recupèra l’istoric del servidor e sincroniza los messatges amb d’autres " "periferics." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Cap de recèrca activa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Començatz d’escriure per aviar una recèrca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Cap de messatge correspondent" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verificatz l’ortografia o ensajatz de suprimir de filtres" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Acorchis clavièr" #~ msgid "Remove" #~ msgstr "Suprimir" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Se connectar a %s" #~ msgid "No accounts configured" #~ msgstr "Cap de compte pas configurat" #~ msgid "Add an account" #~ msgstr "Ajustar un compte" #~ msgid "Pick another server" #~ msgstr "Causissètz un autre servidor" #~ msgid "Local Settings" #~ msgstr "Paramètres locals" #~ msgid "Notifications" #~ msgstr "Notificacions" #~ msgid "Pin conversation" #~ msgstr "Penjar la conversacion" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Penjar la conversacion ennaut de la lista de las conversacions" #~ msgid "Only when mentioned" #~ msgstr "Solament quand vos mencionan" #~ msgid "Permissions" #~ msgstr "Permissions" #~ msgid "Conference Details" #~ msgstr "Detalhs de la conferéncia" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Totes los escambis de messatges e d’estat son blocats dins las doas " #~ "direccions" #~ msgid "A password to restrict access to the room" #~ msgstr "Un senhal per restrénher l’accès a sala" #~ msgid "Message history" #~ msgstr "Istoric dels messatges" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Nombre maximum de messatge tornats per la sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Convertir los « smileys » en emojis" #~ msgid "Check spelling" #~ msgstr "Verificar l’ortografia" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clicatz aquí per començar una conversacion o jónher una sala." #~ msgid "No active conversations" #~ msgstr "Cap de discussion activa" #~ msgid "Main window with conversations" #~ msgstr "Fenèstra màger amb conversacions" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e %i autres" #~ msgid "You can now start using %s" #~ msgstr "Podètz ara començar d’utilizar %s" #~ msgid "Open Registration" #~ msgstr "Inscripcion dubèrtas" #~ msgid "Save" #~ msgstr "Enregistrar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "es a escriure…" #~ msgstr[1] "son a escriure…" #~ msgid "has stopped typing" #~ msgstr "a quitat d‘escriure" #~ msgid "%i search results" #~ msgstr "%i resultats de recèrca" #~ msgid "Discover real JIDs" #~ msgstr "Descobèrta dels JID reals" #~ msgid "Who may discover real JIDs?" #~ msgstr "Qual pòt descobrir los JID reals ?" #~ msgid "Password required for room entry, if any" #~ msgstr "Senhal requesit per dintrar a la sala, se cal" #~ msgid "Failed connecting to %s" #~ msgstr "Fracàs de la connexion a %s" #~ msgid "Join Conference" #~ msgstr "Rejónher la conferéncia" dino-0.5.0/main/po/pl.po0000664000000000000000000012413014776241610013473 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-27 16:16+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.8-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anuluj" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Połącz" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Wyłącz konto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Włącz konto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Wybierz awatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Wybierz" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Obrazy" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Wszystkie pliki" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Usunąć konto %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Łączenie…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Połączony" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Rozłączony" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Błędne hasło" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Nieprawidłowy certyfikat TLS" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Błąd" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Konta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Wyłączone konta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Dodaj konto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Brak aktywnych kont" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Zaloguj się" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Serwer nie mógł udowodnić że jest %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Jego certyfikat bezpieczeństwa nie jest zaufany w systemie operacyjnym tego " "komputera." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Jego certyfikat bezpieczeństwa został wystawiony dla innej domeny." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Jego certyfikat bezpieczeństwa nie jest jeszcze ważny." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Jego certyfikat bezpieczeństwa utracił ważność." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Załóż konto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Możesz teraz użyć konta %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Niepoprawna nazwa użytkownika lub hasło" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Coś się nie powiodło" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Nie udało się połączyć z %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Niepoprawny adres" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Brak odpowiedzi z serwera" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Zarejestruj się na %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Serwer wymaga rejestracji przez stronę internetową" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Otwórz stronę internetową" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Zarejestruj się" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Dowiedz się na %s, jak się zarejestrować" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Zablokuj użytkownika" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Zablokuj całą domenę" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Odblokuj" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Przypięte" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Przypnij" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Zablokowano" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domena jest zablokowana" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Zablokuj" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Wycisz" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Włączono powiadomienia" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Powiadomienia o wzmiankach" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Wyciszono" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Wyłączono powiadomienia" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "O kontakcie" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Ustawienia" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Konfiguracja pokoju" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Szyfrowanie" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Członkowie" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Właściciel" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Członek" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Plik przekracza maksymalny rozmiar wysyłanych plików dla tego serwera." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Wiadomość zbyt długa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "edytowane" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "wysyłanie…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "wysyłanie nie powiodło się" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Bez szyfrowania" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Nie można wysłać wiadomości" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minuta temu" msgstr[1] "%i minuty temu" msgstr[2] "%i minut temu" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Przed chwilą" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ja" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Wysłano obraz" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Wysłano plik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Odebrano obraz" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Odebrano plik" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Połączenie wychodzące" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Połączenie przychodzące" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Wczoraj" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Rozpocznij rozmowę" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Użytkownik" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Zaproś" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Rozpocznij prywatną rozmowę" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Wyrzuć" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Udziel pozwolenia na pisanie" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Odwołaj pozwolenie na pisanie" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Zaproś do konferencji" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Wybierz plik" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s z %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nazwa pokoju" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Opis pokoju" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Trwały" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Pokój nie zostanie usunięty po opuszczeniu przez ostatniego członka" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Publicznie wyszukiwalny" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Zmiana tematu możliwa przez użytkowników" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Zezwolenie na przeglądanie JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Kto może wyświetlać identyfikatory JID użytkowników?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Hasło" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderowany" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Tylko użytkownicy z prawem głosu mogą wysyłać wiadomości" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Tylko dla członków" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Tylko członkowie mogą dołączyć do pokoju" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Ta konferencja nie pozwala na wysyłanie wiadomości." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Prośba o dostęp" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Wyślij plik" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Rozpocznij połączenie" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Połączenie głosowe" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Połączenie wideo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Wyszukaj wiadomości" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Szczegóły konwersacji" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Zamknij konwersację" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informacje debugowania" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Łączenie…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Łączenie…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s zakończył połączenie" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s odrzucił połączenie" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamery" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nie znaleziono kamer." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofony" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nie znaleziono mikrofonów." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Głośniki" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nie znaleziono głośników." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Zaproś do Rozmowy" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Rozpocznij" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Dołącz do kanału" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Dalej" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Dołącz" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Dołączanie…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Wstęp do pokoju wymaga hasła" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Dołączanie lub założenie konferencji zostało zablokowane" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Pokój nie istnieje" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Utworzenie pokoju niedozwolone" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Pokój tylko dla członków" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Wybierz inny nick" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Zbyt wiele osób w pokoju" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Dodaj" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "O Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Dzisiaj" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i wynik wyszukiwania" msgstr[1] "%i wyniki wyszukiwania" msgstr[2] "%i wyników wyszukiwania" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "W %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Z %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Przychodzące połączenie wideo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Przychodząca grupowa rozmowa wideo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Przychodząca rozmowa grupowa" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Odrzuć" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Akceptuj" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Prośba o subskrypcję" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Odmów" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Zaproszenie do %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s zaprosił cię do %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Prośba o dostęp" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s prosi o pozwolenie na pisanie na czacie z %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Wyślij powiadomienie o pisaniu" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Wyślij potwierdzenia odczytu" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Włączone" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Wyłączone" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Domyślnie: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Prośba" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Poproś o pozwolenie na wysyłanie wiadomości" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Witaj w Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Zaloguj się lub załóż konto, aby rozpocząć." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Załóż konto" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Zarządzaj kontami" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adres XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Wyświetlana nazwa użytkownika" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Temat" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Popraw wiadomość" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ty" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Emocje" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Otwórz" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Zapisz jako…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s i %i innych piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s i %s piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s i %s piszą…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s pisze…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Połączenie rozpoczęte" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Połączenie rozpoczęte %s temu" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "To połączenie zostało odebrane na innym urządzeniu" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Połączenie zakończone" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Zakończone o %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Trwało %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Połączenie nieodebrane" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Nie odebrałeś tego połączenia" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s nie odebrał tego połączenia" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Połączenie odrzucone" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Odrzuciłeś to połączenie" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s odrzucił to połączenie" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Łączenie nie powiodło się" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "godzinę" msgstr[1] "%i godziny" msgstr[2] "%i godzin" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "minutę" msgstr[1] "%i minuty" msgstr[2] "%i minut" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "kilka sekund temu" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Dostarczono" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Czytaj" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Ta osoba chciałaby Cię dodać do swoich kontaktów" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Ta rozmowa nie pozwala na dodawanie emotek." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Odpowiedz" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Ta konwersacja nie umożliwia wysyłanie odpowiedzi." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Pobieranie %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "oferowany %s: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Oferowany plik: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Oferta pliku" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Przesyłanie pliku nie powiodło się" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Plik" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nowy" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Szczegóły kontaktu" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Aktualizuj wiadomość" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Nie udało się nawiązać bezpiecznego połączenia" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Wstecz" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Wybierz serwer publiczny" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Lub wprowadź adres serwera" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Przejdź do logowania" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Wszystko gotowe!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Zakończ" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Wysyłaj _powiadomienia o pisaniu" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Wysyłaj _potwierdzenia odczytania" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Powiadomienia" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Powiadom o przychodzącej wiadomości" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Zamieniaj Smileys na Emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokalny nick" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Stan połączenia" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Usuń konto" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Ogólne" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nie masz otwartych czatów" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Kliknij znak + by rozpocząć rozmowę lub przyłączyć się do kanału" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Skróty klawiszowe" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Rozmowa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Nawigacja" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Przejdź do następnej rozmowy" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Przejdź do poprzedniej rozmowy" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Pseudonim" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudonim" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Dodaj kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Nowoczesny komunikator XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino jest nowoczesnym, otwartym komunikatorem. Skupia się na prostej " "obsłudze sieci Jabber/XMPP dbając o twoją prywatność." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Obsługuje szyfrowanie od końca do końca za pomocą OMEMO i OpenPGP, a także " "daje kontrolę nad funkcjami wpływającymi na prywatność, jak powiadomienia o " "pisaniu czy odczytaniu wiadomości." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino pobiera historię rozmów z serwera i synchronizuje wiadomości z innymi " "urządzeniami." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Brak aktywnego wyszukiwania" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Zacznij pisać, aby wyszukać" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Brak pasujących wiadomości" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Sprawdź pisownię lub spróbuj usunąć filtry" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Wyślij" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferencje" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Skróty klawiszowe" #~ msgid "Remove" #~ msgstr "Usuń" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Zaloguj się do %s" #~ msgid "No accounts configured" #~ msgstr "Brak skonfigurowanych kont" #~ msgid "Add an account" #~ msgstr "Dodaj konto" #~ msgid "Pick another server" #~ msgstr "Wybierz inny serwer" #~ msgid "Local Settings" #~ msgstr "Ustawienia lokalne" #~ msgid "Notifications" #~ msgstr "Powiadomienia" #~ msgid "Pin conversation" #~ msgstr "przypnij wyżej rozmowę" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Przypnij rozmowę na samej górze listy rozmów" #~ msgid "Only when mentioned" #~ msgstr "Tylko, gdy wspomniano nick" #~ msgid "Permissions" #~ msgstr "Pozwolenia" #~ msgid "Conference Details" #~ msgstr "Szczegóły konferencji" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Blokowanie wysyłania statusu oraz wiadomości w obu kierunkach" #~ msgid "A password to restrict access to the room" #~ msgstr "Hasło ograniczające dostęp do pokoju" #~ msgid "Message history" #~ msgstr "Historia wiadomości" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maksymalna liczba przechowywanych wiadomości" #~ msgid "Convert smileys to emojis" #~ msgstr "Zamieniaj tekst na emotikony" #~ msgid "Check spelling" #~ msgstr "Sprawdź pisownie" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Kliknij tutaj, aby rozpocząć rozmowę albo dołączyć do kanału." #~ msgid "No active conversations" #~ msgstr "Brak aktywnych rozmów" #~ msgid "Main window with conversations" #~ msgstr "Główne okno rozmów" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s i %i innych" #~ msgid "You can now start using %s" #~ msgstr "Możesz teraz używać konto %s" #~ msgid "Open Registration" #~ msgstr "Otwórz stronę" #~ msgid "Save" #~ msgstr "Zapisz" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s i %s" #~ msgid "%s and %s" #~ msgstr "%s i %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "pisze…" #~ msgstr[1] "piszą…" #~ msgstr[2] "piszą…" #~ msgid "has stopped typing" #~ msgstr "przestał pisać" #~ msgid "%i search results" #~ msgstr "%i wyniki wyszukiwania" #~ msgid "Discover real JIDs" #~ msgstr "Uprawnienie do wyświetlenia JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kto może widzieć JID użytkowników?" #~ msgid "Password required for room entry, if any" #~ msgstr "Hasło, aby ograniczyć dostęp do pokoju" #~ msgid "Failed connecting to %s" #~ msgstr "Nie udało się połączyć z %s" #~ msgid "Join Conference" #~ msgstr "Dołącz do konferencji" #~ msgid "Quit" #~ msgstr "Wyjdź" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID powinien być w formie \"użytkownik@przykład.com\"" #~ msgid "Copy Link Address" #~ msgstr "Skopiuj adres odnośnika" #~ msgid "Copy" #~ msgstr "Kopiuj" #~ msgid "Select All" #~ msgstr "Wybierz wszystkie" #~ msgid "Search" #~ msgstr "Wyszukaj" #~ msgid "Send message marker" #~ msgstr "Wyślij informację o odczytaniu wiadomości" #~ msgid "Start Chat" #~ msgstr "Rozpocznij rozmowę" dino-0.5.0/main/po/pt.po0000664000000000000000000012011414776241610013501 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-06 11:12+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Conectar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desativar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Ativar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Escolher avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Selecionar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imagens" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Todos os ficheiros" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Remover conta %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conectando…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Palavra-passe incorreta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Erro" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Contas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Contas desativadas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Adicionar conta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Nenhuma conta ativa" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Registar" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor não conseguiu provar ser %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O seu certificado securitário não é confiável para o seu sistema operacional." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "O seu certificado securitário é emitido para outro domínio." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "O seu certificado securitário só se tornará válido no futuro." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "O seu certificado securitário está expirado." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Criar conta" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Agora pode usar a conta %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nome ou Palavra-passe errada" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Algo deu errado" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Não foi possível se conectar a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Endereço inválido" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Sem resposta do servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registe-se em %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "O servidor requer que se inscreva através de um site" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Abrir website" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Verifique %s para informação de como se inscrever" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloquear utilizador" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloquear domínio inteiro" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Desbloquear" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fixado" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fixar" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqueado" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domínio bloqueado" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloquear" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Silenciar" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificações ativadas" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificações para menções" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silenciado" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificações desativadas" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Sobre" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Configurações" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuração da sala" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Criptografia" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membros" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "O ficheiro excede o tamanho máximo de envios do servidor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mensagem muito longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "Pendente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "Entrega falhou" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Não-criptografado" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Não foi possível enviar a mensagem" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min atrás" msgstr[1] "%i mins atrás" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Agora há pouco" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagem enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Ficheiro enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagem recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Ficheiro recebido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Chamada de Saída" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Chamada Recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ontem" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Iniciar conversa" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizador" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Banir" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permissão de escrita" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar permissão de escrita" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar para Conferência" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Selecionar ficheiro" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrição da sala" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistente" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "A sala vai continuar depois do último integrante sair" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Publicamente pesquisável" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Integrantes podem mudar o assunto" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permissão de visualizar JIDs" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quem consegue visualizar os JIDs dos utilizadores?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Palavra-passe" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderado" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Somente integrantes com voz podem enviar mensagens" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Somente membros" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Somente membros podem entrar na sala" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Essa conferência não permite que envie mensagens." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Pedir permissão" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Enviar um ficheiro" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Fazer chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de Voz" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de Vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Procurar mensagens" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Pormenores da Conversa" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Fechar Conversa" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Depuração Informação" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "A Chamar…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "A Tocar…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s Terminou a Chamada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s Rejeitou a Chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Câmera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Câmera não encontrada." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfone" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Microfone não encontrado." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Autofalantes" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Sem Autofalantes." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar para chamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Juntar-se a um canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Próximo" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Juntar-se" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Juntando-se…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Palavra-passe necessária para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Proibido de entrar ou criar uma conferência" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Sala não existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Não permitido criar sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala somente para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolha um apelido diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Quantidade máxima da sala" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adicionar" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Sobre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoje" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da pesquisa" msgstr[1] "%i resultados da pesquisa" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Em %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Com %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "A receber Chamada de Vídeo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "A receber Chamada de Vídeo de Grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "A Receber Chamada de Grupo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceitar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Pedido de assinatura" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Negar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Convite para %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s convidou te para %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Pedido de permissão" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s está pedindo permissão para escrever em %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Enviar notificação ao digitar" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Enviar confirmação de leitura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Ligado" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Desligado" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Predefinição: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Pedir" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Pedir permissão para enviar mensagens" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Seja bem vindo ao Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Entre ou crie uma conta para começar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurar a conta" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gerir contas" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Endereço XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nome de exibição" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Tópico" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Editar mensagem" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Você" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Adicionar reação" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Guardar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i outros estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s está digitando…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Chamada Iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s decorridos" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Atendeu esta Chamada em outro aparelho" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Chamada terminada" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Terminada aos %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durou %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Chamada Perdida" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Perdeu esta Chamada" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Chamada Recusada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Recusou esta Chamada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s recusou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Chamada falhada" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i Hora" msgstr[1] "%i Horas" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "alguns segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Entregue" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Lido" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Enviar solicitação" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Ainda não tem atualizações de estado deste contacto." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Este contato gostaria de adicioná-lo à sua lista de contatos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Esta conversa não suporta reações." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Responder" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Esta conversa não suporta respostas." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "A descarregar %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "A descarregar %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "A enviar %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofereceu: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Ficheiro oferecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Ficheiro oferecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transferência de ficheiro falhou" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Ficheiro" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Novo" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhes do contato" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atualizar mensagem" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Autenticar" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Não foi possível estabelecer uma conexão segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Voltar" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Escolha um servidor publico" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ou especifique um endereço de servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Entrar em vez disso" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tudo configurado!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finalizado" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Enviar _Notificações de Digitação" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Enviar _Confirmações de Leitura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificações" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notificar quando uma nova mensagem chegar" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Converter Emoticons em Emojis" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Pseudônimo local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Estado da conexão" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Remover conta" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Alterar a palavra-passe" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Alterar" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Palavra-passe atual" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Palavra-passe nova" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Confirmar palavra-passe" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Geral" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Não tem conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Clique no + para iniciar uma conversa ou entrar num canal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Mostrar preferências" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Atalhos do teclado" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegação" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ir para a próxima conversa" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ir para a conversa anterior" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Apelido" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudônimo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adicionar contato" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Moderno cliente de chat XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é um moderno chat de código aberto para desktop. Ele é focado em prover " "uma transparente e confiável experiência Jabber/XMPP, tendo em mente a sua " "privacidade." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suporte criptografia ponta a ponta com OMEMO e OpenPGP e permite configurar " "privacidade—características relacionadas às notificações de leitura, " "recebimento e escrita." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtém o histórico do servidor e sincroniza mensagens com outros " "aparelhos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sem busca ativa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escreva para iniciar a busca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sem mensagens correspondentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verifique a grafia ou tente remover filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferências" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Atalhos de Teclado" #~ msgid "Remove" #~ msgstr "Remover" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Registar-se em %s" #~ msgid "No accounts configured" #~ msgstr "Nenhuma conta configurada" #~ msgid "Add an account" #~ msgstr "Adicionar conta" #~ msgid "Pick another server" #~ msgstr "Escolha outro servidor" #~ msgid "Local Settings" #~ msgstr "Configurações locais" #~ msgid "Notifications" #~ msgstr "Notificações" #~ msgid "Only when mentioned" #~ msgstr "Somente quando mencionado" #~ msgid "Permissions" #~ msgstr "Permissões" #~ msgid "Conference Details" #~ msgstr "Detalhes da Conferência" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Comunicação e atualizações de estado dos dois lados estão bloqueadas" #~ msgid "A password to restrict access to the room" #~ msgstr "Uma palavra-passe para restringir o acesso à sala" #~ msgid "Message history" #~ msgstr "Histórico de mensagens" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Quantidade máxima de histórico gerado pela sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Converter smileys para emojis" #~ msgid "Check spelling" #~ msgstr "Verificar ortografia" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clique aqui para iniciar uma conversa or entrar num canal." dino-0.5.0/main/po/pt_BR.po0000664000000000000000000012420214776241610014066 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-12 18:09+0000\n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Cancelar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Conectar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Desativar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Ativar conta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Escolher avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Selecionar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imagens" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Todos os arquivos" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Remover conta %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conectando…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Desconectado" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Senha incorreta" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificado TLS inválido" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Erro" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Contas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Contas desativadas" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Adicionar Conta" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Nenhuma conta ativa" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Entrar" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "O servidor contactado não conseguiu provar que representa %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "O certificado de segurança do servidor não é considerado confiável por seu " "sistema operacional." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Seu certificado de segurança for emitido para outro domínio." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Seu certificado de segurança se tornará válido apenas no futuro." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Seu certificado de segurança está expirado." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Criar conta" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Você agora pode usar a conta %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nome ou senha incorretos" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Algo deu errado" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Não foi possível se conectar a %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Endereço inválido" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Sem resposta do servidor" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registre-se em %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "O servidor requer registro através de um site" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Abrir website" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrar" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Verifique %s para informação de como se registrar" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bloquear usuário" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Bloquear domínio inteiro" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Desbloquear" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fixado" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fixar" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Bloqueado" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domínio bloqueado" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bloquear" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Silenciar" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificações ativadas" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificações para menções" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silenciado" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificações desativadas" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Sobre" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Configurações" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configuração da Sala" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Criptografia" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membros" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Dono" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membro" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "O arquivo ultrapassa o máximo do tamanho de upload." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mensagem muito longa" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editado" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "pendente…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "entrega falhou" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Sem Criptografia" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Não foi possível enviar a mensagem" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d de %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d de %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i min atrás" msgstr[1] "%i min atrás" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Agora há pouco" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagem enviada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Arquivo enviado" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagem recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Arquivo recebido" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Chamada realizada" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Chamada recebida" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ontem" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Iniciar Conversa" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Usuário" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Convidar" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Iniciar conversa privada" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Expulsar" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Conceder permissão de escrita" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revogar permissão de escrita" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Convidar para Conferência" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Selecionar arquivo" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Nome da sala" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrição da sala" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistente" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "A sala vai continuar existindo mesmo que o último integrante saia" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Publicamente pesquisável" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Integrantes podem mudar o assunto" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permissão de visualizar JIDs" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Quem consegue visualizar os JIDs dos usuários?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Senha" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderado" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Somente integrantes com voz podem enviar mensagens" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Somente membros" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Somente membros podem entrar na sala" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Essa conferência não permite que você envie mensagens." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Pedir permissão" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Enviar um arquivo" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Iniciar chamada" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Chamada de áudio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Chamada de vídeo" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Buscar mensagens" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detalhes da Conversa" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Fechar Conversa" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informações de depuração" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Chamada em andamento…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Chamando…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s terminou a chamada" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s recusou a chamada" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Câmeras" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nenhuma câmera encontrada." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfones" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nenhum microfone encontrado." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Alto-falantes" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nenhum alto-falante encontado." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Convidar para Chamada" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Iniciar" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Juntar-se a um Canal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Próximo" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Juntar-se" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Juntando-se…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Senha necessária para entrar na sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Proibido de entrar ou criar uma conferência" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Sala não existe" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Sem permissão de criar sala" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sala somente para membros" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Escolha um apelido diferente" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Capacidade de usuários na sala excedida" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adicionar" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Sobre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hoje" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i resultado da pesquisa" msgstr[1] "%i resultados da pesquisa" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Em %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Com %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Recebendo chamada de vídeo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Recebendo chamada de vídeo em grupo" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Recebendo chamada em grupo" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Rejeitar" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Aceitar" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Pedido de assinatura" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Negar" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Convite para %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s te convidou para %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Pedido de permissão" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s está pedindo permissão para escrever em %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Enviar notificação ao digitar" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Enviar confirmação de leitura" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Ligado" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Desligado" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Padrão: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Pedir" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Pedir permissão para enviar mensagens" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Bem vindo ao Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Entre ou crie uma conta para começar." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurar a conta" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Gerenciar contas" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Endereço XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nome de exibição" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Tópico" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Editar mensagem" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Você" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Adicionar reação" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Abrir" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salvar como…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s e %i outros estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s e %s estão digitando…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s está digitando…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Chamada iniciada" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Começou há %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Você respondeu esta chamada em outro dispositivo" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Chamada terminada" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Terminou às %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Durou %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Chamada perdida" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Você perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s perdeu esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Chamada rejeitada" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Você rejeitou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s rejeitou esta chamada" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Chamada falhou" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i hora" msgstr[1] "%i horas" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minuto" msgstr[1] "%i minutos" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "alguns segundos" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Entregue" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Lido" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Enviar solicitação" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Você não tem atualizações de status deste contato ainda." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Este contato gostaria de adicioná-lo à sua lista de contatos" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Esta conversa não suporta reações." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Responder" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Esta conversa não suporta respostas." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Baixando %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Baixando %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Enviando %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofereceu: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Arquivo oferecido: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Arquivo oferecido" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transferência de arquivo falhou" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Arquivo" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Novo" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalhes do Contato" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Atualizar mensagem" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Login" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Não foi possível estabelecer uma conexão segura" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Voltar" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Escolha um servidor público" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ou especifique um endereço de servidor" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Entrar em vez disso" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tudo configurado!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finalizado" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Enviar _Notificações de Digitação" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Enviar _Confirmações de Leitura" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificações" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notificar quando uma nova mensagem chegar" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Converter Emoticons em Emojis" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Pseudônimo local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Estado da conexão" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Remover conta" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Alterar senha" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Alterar" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Senha atual" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Nova senha" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Confirmar senha" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Geral" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Você não tem conversas abertas" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Clique no + para iniciar uma conversa ou entrar em um canal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Mostrar preferências" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Atalhos do teclado" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Conversa" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navegação" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Ir para a próxima conversa" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Ir para a conversa anterior" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Conta" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Apelido" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Pseudônimo" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adicionar Contato" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Cliente de Chat XMPP Moderno" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino é um cliente moderno de código aberto de chat para desktop. Ele foca em " "oferecer uma experiência Jabber/XMPP transparente e confiável levando em " "consideração a sua privacidade." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Ele suporta criptografia ponta-a-ponta com OMEMO e OpenPGP e, além disso, " "permite configurar funcionalidades relativas à privacidade como notificações " "de leitura, recebimento e escrita." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino obtém o histórico do servidor e sincroniza mensagens com outros " "dispositivos." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Sem busca ativa" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Escreva para iniciar uma busca" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Sem mensagens correspondentes" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verifique a grafia ou tente remover filtros" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Enviar" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferências" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Atalhos de Teclado" #~ msgid "Remove" #~ msgstr "Remover" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Entre em %s" #~ msgid "No accounts configured" #~ msgstr "Nenhuma conta configurada" #~ msgid "Add an account" #~ msgstr "Adicionar uma conta" #~ msgid "Pick another server" #~ msgstr "Escolher outro servidor" #~ msgid "Local Settings" #~ msgstr "Configurações Locais" #~ msgid "Notifications" #~ msgstr "Notificações" #~ msgid "Only when mentioned" #~ msgstr "Somente quando mencionado" #~ msgid "Permissions" #~ msgstr "Permissões" #~ msgid "Conference Details" #~ msgstr "Detalhes da Conferência" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Comunicação e atualizações de status dos dois lados estão bloqueadas" #~ msgid "A password to restrict access to the room" #~ msgstr "Uma senha para restringir o acesso à sala" #~ msgid "Message history" #~ msgstr "Histórico de mensagens" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Quantidade máxima de histórico armazenado na sala" #~ msgid "Convert smileys to emojis" #~ msgstr "Converter smileys para emojis" #~ msgid "Check spelling" #~ msgstr "Verificar ortografia" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Clique aqui para inicial uma conversa ou entrar em um canal." #~ msgid "No active conversations" #~ msgstr "Nenhuma conversa ativa" #~ msgid "Main window with conversations" #~ msgstr "Janela principal com as conversas" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s e outros %i" #~ msgid "You can now start using %s" #~ msgstr "Agora você pode começar a usar %s" #~ msgid "Open Registration" #~ msgstr "Abrir Site de Inscrição" #~ msgid "Save" #~ msgstr "Salvar" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s e %s" #~ msgid "%s and %s" #~ msgstr "%s e %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "está digitando…" #~ msgstr[1] "estão digitando…" #~ msgid "has stopped typing" #~ msgstr "parou de digitar" #~ msgid "%i search results" #~ msgstr "%i resultado(s) da busca" #~ msgid "Discover real JIDs" #~ msgstr "Descobrir JIDs reais" #~ msgid "Who may discover real JIDs?" #~ msgstr "Quem pode encontrar JIDs reais?" #~ msgid "Password required for room entry, if any" #~ msgstr "Senha requerida para entrar na sala, se houver" #~ msgid "Failed connecting to %s" #~ msgstr "Falha ao se conectar com %s" #~ msgid "Join Conference" #~ msgstr "Juntar-se a uma conferência" #~ msgid "Communicate happiness." #~ msgstr "Comunique-se alegremente." #~ msgid "Quit" #~ msgstr "Sair" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "O Jabber ID deve estar no formato \"usuário@exemplo.com\"" #~ msgid "Copy Link Address" #~ msgstr "Copiar endereço do link" #~ msgid "Copy" #~ msgstr "Copiar" #~ msgid "Select All" #~ msgstr "Selecionar tudo" #~ msgid "Search" #~ msgstr "Pesquisar" #~ msgid "Send message marker" #~ msgstr "Enviar marcador de mensagem" #~ msgid "Start Chat" #~ msgstr "Iniciar bate-papo" #~ msgid "Request presence updates" #~ msgstr "Pedir atualizações sobre presença" #~ msgid "Join on startup" #~ msgstr "Conectar-se ao inicializar" #~ msgid "Add Chat" #~ msgstr "Adicionar bate-papo" dino-0.5.0/main/po/ro.po0000664000000000000000000012473014776241610013506 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-14 17:31+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anulare" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Conectare" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Dezactivare cont" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Activare cont" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Selectare avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Selectare" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Imagini" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Toate fișierele" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Se șterge contul %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Conectare…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Conectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Deconectat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Parolă greșită" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Certificat TLS invalid" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Eroare" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Conturi" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Conturi dezactivate" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Adaugă un cont" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Nici un cont activ" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Autentificare" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Serverul nu a putut dovedi că este %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Sistemul dumneavoastră de operare nu are încredere în certificatul său de " "securitate." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Certificatul său de securitate este emis pentru alt domeniu." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Certificatul său de securitate va deveni valabil numai în viitor." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Certificatul său de securitate este expirat." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Creează cont" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Acum puteți începe să utilizați contul %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Nume utilizator sau parolă greșite" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "A apărut o problemă" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Nu s-a putut face conectarea la %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Adresă invalidă" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Nici un răspuns de la server" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Înregistrare pe %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Serverul necesită înregistrarea printr-un site web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Deschide site-ul" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Înregistrează-te" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Aflați pe %s informații despre cum se face înregistrarea" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Blochează utilizator" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blochează întregul domeniu" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Deblochează" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Fixat" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fixare" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Blocat" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domeniu blocat" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blochează" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Suprimă" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Notificări activate" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Notificări pentru mențiuni" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Silențios" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Notificări dezactivate" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Despre" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Setări" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Configurare discuție" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Criptare" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Membri" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Proprietar" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Administrator" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Membru/ă" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Fișierul depășește dimensiunea maximă de încărcare a serverului." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mesaj prea lung" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "editat" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "în așteptare…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "livrare eșuată" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Necriptat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Nu se poate transmite mesajul" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "Acum %i minut" msgstr[1] "Acum %i minute" msgstr[2] "Acum %i de minute" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Chiar acum" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Eu" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Imagine trimisă" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fișier trimis" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Imagine primitä" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fișier primit" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Apel efectuat" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Apel primit" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Ieri" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Pornește o conversație" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Utilizator" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Invită" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Pornește o conversație privată" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Dă afară" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Acordă permisiunea de a scrie" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Revoca permisiunea de a scrie" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Invită la o conferință" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Selectare fișier" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s de la %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Numele discuției" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Descrierea discuției" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Persistentă" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Discuția va continua să existe și după ce ultima persoană va ieși" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Vizibilă public" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Membrii pot schimba subiectul" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Permisiunea de a vizualiza JID-uri" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Cine are voie să vadă JID-urile ocupanților?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Parolă" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderată" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Doar membrii cu drept de voce vor putea trimite mesaje" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Doar pentru membri" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Numai membrii se pot alătura discuției" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Această conferință nu vă permite să trimiteți mesaje." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Solicitare permisiune" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Trimite fișier" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Începe un apel" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Apel audio" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Apel video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Caută mesaje" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Detalii conversație" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Închide conversație" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Informații de depanare" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Se apelează…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Sună…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s a încheiat apelul" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s a refuzat apelul" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camere" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Nici o cameră găsită." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microfoane" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Nici un microfon găsit." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Difuzoare" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Nici un difuzor găsit." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Invitați la apel" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Pornire" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Alăturați-vă canalului" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Următorul" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Alăturați-vă" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Conectare…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Este necesară o parolă pentru a te putea alătura acestei discuții" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Aveți interdicție de a vă alătura sau de a crea o conferință" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Această discuție nu există" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Este interzisă crearea unei discuții" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Discuție accesibilă numai membrilor" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Alegeți un nume diferit" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Discuția are prea mulți membrii" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Adaugă" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Despre Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Azi" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i rezultat de căutare" msgstr[1] "%i rezultate de căutare" msgstr[2] "%i de rezultate de căutare" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "În %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Cu %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Apel video primit" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Apel de grup video primit" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Apelul de grup primit" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Respinge" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Acceptă" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Cerere de abonare" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Refuză" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Invitaţie la %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s v-a invitat la %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Solicitare de permisiune" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s solicită permisiunea de a scrie în %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Trimite notificare la tastare" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Trimite notificare la primire" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Da" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Nu" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Implicit: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Cerere" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Solicita permisiunea de a trimite mesaje" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Bine ați venit la Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Autentificați-vă sau creați un cont pentru a începe." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Configurare cont" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Administrare conturi" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adresă XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Nume afișat" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Subiect" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Editare mesaj" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Tu" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Adaugă reacție" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Deschide" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Salvează ca…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s și încă %i tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s și %s tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s și %s tastează…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s tastează…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Apelul a început" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "A început acum %s" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Ați recepționat acest apel pe un alt dispozitiv" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Apel încheiat" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "S-a încheiat la %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "A durat %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Apel ratat" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Ați pierdut acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s a pierdut acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Apel refuzat" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Ați refuzat acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s a refuzat acest apel" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Apelul a eșuat" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i oră" msgstr[1] "%i ore" msgstr[2] "%i de ore" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minute" msgstr[2] "%i de minut" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "câteva secunde" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Livrat" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Citit" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Trimite cerere" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Nu primiți încă actualizări de stare de la acest contact." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Acest contact dorește să vă adauge la lista ei/lui de contacte" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Această conversație nu acceptă reacții." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Răspunde" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Această conversație nu acceptă răspunsuri." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Desacărcare %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Se descarcă %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Încărcare %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s a oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fișier oferit: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fișier oferit" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Transferul fișierului a eșuat" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fișier" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nou" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Detalii contact" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Actualizare mesaj" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Autentificare" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Nu s-a putut stabili o conexiune securizată" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Înapoi" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Alegeți un server public" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Sau specificați adresa serverului" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Înapoi la conectare" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Gata!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Finalizare" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Trimite notificare la _tastare" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Trimite notificare la _citire" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Notificări" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Notifică atunci când este primit un mesaj nou" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "Convertește zâmbilicii în _emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias local" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Stare conexiune" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Elimină cont" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Schimbare parolă" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Schimbă" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Parolă curentă" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Parolă nouă" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Confirmare parolă" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "General" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Nu aveți nici o discuție deschisă" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Apăsați + pentru a porni o discuție sau să vă alăturați unui grup" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Arată preferințe" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Scurtături de tastatură" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Discuție" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigare" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Salt la următoarea conversație" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Salt la conversația anterioară" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Cont" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nume" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Adaugă contact" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Client XMPP de discuții modern" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino este un client de chat modern, cu sursă deschisă, pentru calculatoare. " "Se concentrează să furnizeze o experiență Jabber/XMPP clară și fiabilă, " "ținând cont de confidențialitatea dumneavoastră." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Suportă criptare de la un capăt la altul prin intermediul OMEMO și OpenPGP, " "și permite configurarea caracteristicilor legate de confidențialitate precum " "trimiterea notificărilor de primire și tastare." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino preia istoricul discuțiilor de pe server și sincronizează mesajele cu " "celelalte dispozitive." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Nici o căutare activă" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Tastați ceva pentru a porni căutarea" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Nici un mesaj nu se potrivește" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Verificați ortografia sau încercați să eliminați filtre" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Trimite" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Preferințe" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Scurtături de tastatură" #~ msgid "Remove" #~ msgstr "Șterge" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Autentificare în %s" #~ msgid "No accounts configured" #~ msgstr "Nici un cont configurat" #~ msgid "Add an account" #~ msgstr "Adaugă un cont" #~ msgid "Pick another server" #~ msgstr "Alege alt server" #~ msgid "Local Settings" #~ msgstr "Setări locale" #~ msgid "Notifications" #~ msgstr "Notificări" #~ msgid "Pin conversation" #~ msgstr "Fixează discuția" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fixează discuția în partea de sus a listei de discuții" #~ msgid "Only when mentioned" #~ msgstr "Doar la mențiuni" #~ msgid "Permissions" #~ msgstr "Permisiuni" #~ msgid "Conference Details" #~ msgstr "Detalii conferință" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Comunicațiile și actualizările de stare sunt blocate în ambele sensuri" #~ msgid "A password to restrict access to the room" #~ msgstr "O parolă pentru a restricționa accesul la discuție" #~ msgid "Message history" #~ msgstr "Istoric mesaje" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Numărul maxim de mesaje din istoric ce va fi afișat în discuție" #~ msgid "Convert smileys to emojis" #~ msgstr "Convertește zâmbilici în emoticoane" #~ msgid "Check spelling" #~ msgstr "Verificare ortografie" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "" #~ "Apăsați aici pentru a porni o conversație sau a vă alătura unui canal." #~ msgid "No active conversations" #~ msgstr "Nici o conversație activă" #~ msgid "Main window with conversations" #~ msgstr "Fereastra principală de conversații" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s și încă %i alții" #~ msgid "You can now start using %s" #~ msgstr "Acum puteți utiliza %s" #~ msgid "Open Registration" #~ msgstr "Deschis pentru înregistrare" #~ msgid "Save" #~ msgstr "Salvare" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s și %s" #~ msgid "%s and %s" #~ msgstr "%s și %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "tastează…" #~ msgstr[1] "tastează…" #~ msgstr[2] "tastează…" #~ msgid "has stopped typing" #~ msgstr "s-a oprit din scris" #~ msgid "%i search results" #~ msgstr "%i rezultate la căutare" #~ msgid "Discover real JIDs" #~ msgstr "Descoperă adresele adevărate" #~ msgid "Who may discover real JIDs?" #~ msgstr "Cine poate descoperi adresele adevărate?" #~ msgid "Password required for room entry, if any" #~ msgstr "Dacă există, o parolă va fi necesară pentru alăturarea la discuție" #~ msgid "Failed connecting to %s" #~ msgstr "Eroare la conectarea cu %s" #~ msgid "Join Conference" #~ msgstr "Alătură-te unei conferințe" #~ msgid "Quit" #~ msgstr "Ieșire" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID trebuie să fie de forma “utilizator@exemplu.ro”" #~ msgid "Copy Link Address" #~ msgstr "Copiază adresa legăturii" #~ msgid "Copy" #~ msgstr "Copiere" #~ msgid "Select All" #~ msgstr "Selectează tot" #~ msgid "Search" #~ msgstr "Căutare" #~ msgid "Send message marker" #~ msgstr "Trimite marcajul mesajului" #~ msgid "Start Chat" #~ msgstr "Pornește o discuție" dino-0.5.0/main/po/ru.po0000664000000000000000000013615014776241610013513 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-06 14:24+0300\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 5.0-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Отмена" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Подключиться" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Отключить учётную запись" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Включить учётную запись" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Выбрать аватарку" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Выбрать" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Изображения" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Все файлы" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Удалить учётную запись %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Подключение…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Подключено" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Отключено" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Неверный пароль" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Недействительный TSL сертификат" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Ошибка" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Учётные записи" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Отключённые учётные записи" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Добавить учётную запись" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Нет активных учётных записей" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Войти" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Сервер не смог доказать, что %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Его сертификат безопасности не доверяет вашей операционной системе." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Его сертификат безопасности выдаётся другому домену." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Его сертификат безопасности станет действительным только в будущем." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Срок действия его сертификата безопасности истёк." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Создать учётную запись" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Теперь вы можете использовать учётную запись %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Неправильный логин или пароль" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Что-то пошло не так" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Не удаётся подключиться к %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Недействительный адрес" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Нет ответа от сервера" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Регистрация в %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Сервер требует пройти авторизацию через сайт" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Открыть веб-сайт" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Зарегистрироваться" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Прочтите %s, чтобы узнать о процессе авторизации" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Заблокировать пользователя" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Заблокировать весь домен" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Разблокировать" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Закреплено" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Закрепить" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Заблокировано" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Домен заблокирован" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Заблокировать" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Выключить звук" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Уведомления включены" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Уведомления для упоминаний" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Звук выключен" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Уведомления отключены" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "О программе" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Настройки" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Настройки комнаты" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Шифрование" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Участники" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Владелец" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Администратор" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Участник" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Размер файла превышает максимальный размер загрузки сервера." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Сообщение слишком длинное" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "изменено" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "ожидание…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "доставка не удалась" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Не зашифровано" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Не удаётся отправить сообщение" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%H∶%M, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%l∶%M, %x %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i минута назад" msgstr[1] "%i минуты назад" msgstr[2] "%i минут назад" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Только что" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Я" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Изображение отправлено" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Файл отправлен" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Изображение получено" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Файл получен" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Исходящий звонок" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Входящий звонок" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Вчера" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Начать чат" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Пользователь" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Пригласить" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Начать личный чат" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Выгнать" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Выдать разрешение писать" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Отобрать разрешение писать" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Пригласить в чат" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Выберите файл" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s из %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Название комнаты" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Описание комнаты" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Сохранить комнату пустой" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Комната сохранится, даже если уйдёт последний посетитель" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Доступность для публичного поиска" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Посетители могут менять тему чата" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Разрешение на просмотр JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Кому разрешено просматривать JID посетителей?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Пароль" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Модерируемый" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Только посетители с голосом (разрешение) могут отправлять сообщения" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Только для участников" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Только участники могут заходить в комнату" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Вам запретили отправлять сообщения в этой беседе." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Запросить разрешение" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Прикрепить файл" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Начать звонок" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Аудиозвонок" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Видеозвонок" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Поиск сообщений" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Информация о чате" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Закрыть чат" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Отладочная информация" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Вызов…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Звонок…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s завершил(а) звонок" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s отклонил(а) звонок" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Камеры" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Камера не найдена." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Микрофоны" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Микрофон не найден." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Динамики" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Динамик не найден." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Пригласить на звонок" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Начать" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Присоединиться к каналу" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Далее" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Присоединиться" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Присоединение…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Требуется пароль для входа в комнату" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Вам запрещено заходить в конференции или создавать их" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Данной комнаты не существует" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Вам запрещено создавать комнаты" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Комната только для участников" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Выберите другой никнейм" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "В комнате слишком много посетителей" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Добавить" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "О Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Сегодня" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i результат поиска" msgstr[1] "%i результата поиска" msgstr[2] "%i результатов поиска" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "В %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "С %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Входящий видеозвонок" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Входящий групповой видеозвонок" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Входящий групповой звонок" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Отклонить" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Принять" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Требуется авторизация" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Отклонить" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Приглашение в %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s пригласил вас в %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Запрос на разрешения" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s запросил(а) разрешение на возможность писать в %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Отправлять уведомления при наборе сообщения" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Отправлять уведомления о прочтении" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "ВКЛ" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "ВЫКЛ" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "По умолчанию: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Запросить" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Запросить разрешение на отправку сообщений" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Добро пожаловать в Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Войдите или создайте учётную запись, чтобы начать использование." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Настроить учётную запись" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Управление учётными записями" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP-адрес" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Отображаемое имя" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Тема" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Отредактировать сообщение" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Вы" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Добавить реакцию" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Открыть" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Сохранить как…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s и ещё %i печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s и %s печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s и %s печатают…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s печатает…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Звонок начат" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Начат %s назад" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Вы обработали этот звонок на другом устройстве" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Звонок завершён" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Завершён в %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Продлился %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Пропущенный звонок" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Вы пропустили этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s пропустил(а) этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Звонок отклонён" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Вы отклонили этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s отклонил(а) этот звонок" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Звонок не удался" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i час" msgstr[1] "%i часа" msgstr[2] "%i часов" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i минута" msgstr[1] "%i минуты" msgstr[2] "%i минут" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "несколько секунд" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Доставлено" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Прочитано" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Этот контакт хочет добавить вас в свой список контактов" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Эта чат не поддерживает реакции." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Ответить" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Этот чат не поддерживает ответов." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Скачивание %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s. Приблизительный размер: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Приблизительный размер файла: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Предложен файл" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Ошибка передачи файла" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Файл" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Новые" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Информация о контакте" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Редактировать сообщение" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Не удалось установить защищённое соединение" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Назад" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Выберите публичный сервер" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Или введите адрес сервера сами" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Войти вместо" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Всё готово!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Закончить" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Отправлять _уведомления при наборе сообщения" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Отправлять _уведомления о прочтении" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Уведомления" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Уведомлять о новых сообщениях" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Преобразовывать смайлики в эмодзи" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Локальный псевдоним" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Состояние соединения" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Удалить учётную запись" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Основные" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Тут пустовато" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Нажмите + для того, чтобы начать чат или присоединиться к каналу" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Комбинации клавиш" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Чат" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Навигация" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Перейти к следующему чату" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Перейти к предыдущему чату" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Уч. запись" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Никнейм" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Псевдоним" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Добавить контакт" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Современный XMPP клиент" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino — это современный клиент для чатов с открытым исходным кодом, " "направленный на надёжное и приватное использование Jabber/XMPP на " "персональных компьютерах." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Он поддерживает сквозное шифрование через OMEMO и OpenPGP и позволяет " "настраивать такие функции, как уведомления о прочтении и наборе сообщений." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino загружает историю с сервера и синхронизирует сообщения с другими " "устройствами." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Поиск по сообщениям" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Начните писать то, что хотите найти" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Нет подходящих сообщений" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Проверьте словари или измените критерии поиска" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Отправить" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Опции" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Комбинации клавиш" #~ msgid "Remove" #~ msgstr "Удалить" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Вход в уч. запись %s" #~ msgid "No accounts configured" #~ msgstr "Нет настроенных уч. записей" #~ msgid "Add an account" #~ msgstr "Добавить учётную запись" #~ msgid "Pick another server" #~ msgstr "Выбрать другой сервер" #~ msgid "Local Settings" #~ msgstr "Локальные настройки" #~ msgid "Notifications" #~ msgstr "Уведомления" #~ msgid "Pin conversation" #~ msgstr "Прикрепить чат" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Прикрепить чат в верху списка чатов" #~ msgid "Only when mentioned" #~ msgstr "Только при упоминании" #~ msgid "Permissions" #~ msgstr "Разрешения" #~ msgid "Conference Details" #~ msgstr "Информация о конференции" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Переписка и обновления статусов заблокированы в оба направления" #~ msgid "A password to restrict access to the room" #~ msgstr "Пароль для ограничения доступа к комнате" #~ msgid "Message history" #~ msgstr "История сообщений" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Максимальное количество логов, оставляемое комнатой" #~ msgid "Convert smileys to emojis" #~ msgstr "Превращать смайлы в эмодзи" #~ msgid "Check spelling" #~ msgstr "Проверка орфографии" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Нажмите здесь, чтобы начать беседу или присоединиться к каналу." #~ msgid "No active conversations" #~ msgstr "Нет активных чатов" #~ msgid "Main window with conversations" #~ msgstr "Главное окно с чатами" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s и ещё %i человек" #~ msgid "You can now start using %s" #~ msgstr "Теперь вы можете использовать %s" #~ msgid "Open Registration" #~ msgstr "Открытая регистрация" #~ msgid "Save" #~ msgstr "Сохранить" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s и %s" #~ msgid "%s and %s" #~ msgstr "%s и %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "печатает…" #~ msgstr[1] "печатают…" #~ msgstr[2] "печатают…" #~ msgid "has stopped typing" #~ msgstr "перестал печатать" #~ msgid "%i search results" #~ msgstr "Результаты поиска по запросу «%i»" #~ msgid "Discover real JIDs" #~ msgstr "Показывать настоящие JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "Кто может видеть настоящие JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "Требуется пароль для входа в комнату, если" #~ msgid "Failed connecting to %s" #~ msgstr "Не удалось соединение с %s" #~ msgid "Join Conference" #~ msgstr "Войти в конференцию" #~ msgid "Quit" #~ msgstr "Выйти" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID должен быть в виде «user@example.com»" #~ msgid "Copy Link Address" #~ msgstr "Копировать адресс ссылки" #~ msgid "Copy" #~ msgstr "Копировать" #~ msgid "Select All" #~ msgstr "Выбрать всё" #~ msgid "Search" #~ msgstr "Поиск" #~ msgid "Send message marker" #~ msgstr "Отправлять маркеры сообщений" #~ msgid "Start Chat" #~ msgstr "Начать чат" dino-0.5.0/main/po/si.po0000664000000000000000000011371314776241610013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-10-04 02:12+0000\n" "Language-Team: none\n" "Language: si\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "weblate.org>\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.1-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "අවලංගු කරන්න" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "සම්බන්ධ වන්න" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "තෝරන්න" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "පින්තූර" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "සියලු ගොනු" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "%s ගිණුම ඉවත් කරන්න ද?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "සම්බන්ධවෙමින්…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "සම්බන්ධ වී ඇත" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "සම්බන්ධ වී නැත" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "මුරපදය වැරදියි" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "ගිණුම්" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "ගිණුම එක් කරන්න" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "සක්‍රිය කළ ගිණුම් නැත" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "ගිණුම සාදන්න" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "ඔබට දැන් %s ගිණුම භාවිත කළ හැකියි." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "පරිශිලක නාමය හෝ මුරපදය වැරදියි" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "%s වෙත සම්බන්ධ වීමට නොහැකි විය" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "ලිපිනය වැරදියි" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "ලියාපදිංචි වන්න" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "අවහිර කරන්න" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "සැකසුම්" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "සාමාජිකයන්" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "හිමිකරු" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "පරිපාලක" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "සාමාජික" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "පණිවිඩය දිග වැඩියි" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%p %l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "මිනි. %iකට පෙර" msgstr[1] "මිනි. %iකට පෙර" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "මේ දැන්" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "මම" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "පින්තූරය යවන ලදි" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "ගොනුව යවන ලදි" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "පින්තූරය ලැබුණි" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "ගොනුව ලැබුණි" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "යන ඇමතුම" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "එන ඇමතුම" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "ඊයේ" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "සංවාදයක් අරඹන්න" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "පරිශීලක" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "ආරාධනා කරන්න" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "පෞද්ගලික සංවාදයක් අරඹන්න" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "ගොනුව තෝරන්න" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s, %s වෙතින්" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "මුරපදය" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "සාමාජිකයන් පමණයි" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "අවසර ඉල්ලන්න" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "ගොනුවක් යවන්න" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "ඇමතුම අරඹන්න" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "ශ්‍රව්‍ය ඇමතුම" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "දෘෂ්‍ය ඇමතුම" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "පණිවිඩ සොයන්න" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "අමතමින්…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "නාදවෙමින්…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s විසින් ඇමතුම අවසන් කරන ලදි" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s විසින් ඇමතුම ප්‍රතික්ෂේප කරන ලදි" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "ඇමතුමට ආරාධනා කරන්න" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "අරඹන්න" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "නාලිකාවකට සම්බන්ධ වන්න" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "ඊළඟ" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "සම්බන්ධ වන්න" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "සම්බන්ධවෙමින්…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dino පිළිබඳ" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "අද" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%b %d, %a" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "සෙවුම් ප්‍රතිඵල %i ක්" msgstr[1] "සෙවුම් ප්‍රතිඵල %i ක්" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s තුළ" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s සමග" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "එන දෘෂ්‍ය ඇමතුම" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "එන කණ්ඩායම් දෘෂ්‍ය ඇමතුම" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "එන කණ්ඩායම් ඇමතුම" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "ප්‍රතික්ෂේප කරන්න" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "පිළිගන්න" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "ප්‍රතික්ෂේප කරන්න" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "සක්‍රියයි" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "අක්‍රියයි" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "ගිණුමක් පිහිටුවන්න" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "ගිණුම් කලමනාකරණය කරන්න" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "ඔබ" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s සහ තවත් %i දෙනෙක් යතුරු ලියමින්…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s සහ %s යතුරු ලියමින්…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s සහ %s යතුරු ලියමින්…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s යතුරු ලියමින්…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "ඇමතුම ආරම්භ විය" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%sකට පෙර ආරම්භ විය" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "ඇමතුම අවසන් විය" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%sට අවසන් විය" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "%sක් පැවතුණි" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "ඇමතුම මගහැරුණි" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "ඔබට මෙම ඇමතුම මගහැරුණි" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%sට මෙම ඇමතුම මගහැරුණි" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "පැය %i" msgstr[1] "පැය %i" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "මිනිත්තු %i" msgstr[1] "මිනිත්තු %i" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "තත්පර කිහිපය" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%sක් බාගනිමින්…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "ගොනුව ඉදිරිපත් කරන ලදි" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "ගොනුව හුවමාරුව අසාර්ථක වුණි" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "ගොනුව" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "පණිවිඩය යාවත්කාල කරන්න" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "පෙර" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "අවසන් කරන්න" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "නව පණිවිඩයක් පැමිණි විට දැනුම්දෙන්න" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "සංවාදය" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "ඊළඟ සංවාදයට පනින්න" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "පෙර සංවාදයට පනින්න" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "ගිණුම" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" #: main/data/global_search.ui:27 msgid "No active search" msgstr "" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "ගැළපෙන පණිවිඩ නැත" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "අක්ෂර වින්‍යාසය පරීක්ෂා කරන්න හෝ පෙරහන් ඉවත් කිරීමට උත්සහ කරන්න" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "යවන්න" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "" #~ msgid "Remove" #~ msgstr "ඉවත් කරන්න" #~ msgid "Add an account" #~ msgstr "ගිණුමක් එක් කරන්න" #~ msgid "Notifications" #~ msgstr "දැනුම්දීම්" #~ msgid "Message history" #~ msgstr "පණිවිඩ ඉතිහාසය" dino-0.5.0/main/po/sq.po0000664000000000000000000012067714776241610013517 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 08:50+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Anuloje" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Lidhuni" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Çaktivizoje llogarinë" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Aktivizoje llogarinë" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Përzgjidhni avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Përzgjidhe" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Figura" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Krejt kartelat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Të hiqet llogaria %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Po lidhet…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "U lidh" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "U shkëput" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Fjalëkalim i gabuar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Dëshmi TLS e pavlefshme" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Gabim" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Llogari" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Llogari të çaktivizuara" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Shtoni Llogari" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Pa llogari aktive" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Hyni" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Shërbyesi s’provoi dot se është %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Dëshmia e tij e sigurisë nuk besohet nga sistemi juaj operativ." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Dëshmia e tij e sigurisë është emetuar nga tjetër përkatësi." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Dëshmia e tij e sigurisë do të bëhet e vlefshme vetëm në të ardhmen." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Dëshmia e tij e sigurisë ka skaduar." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Krijoni llogari" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Tani mund të përdorni llogarinë %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Emër përdoruesi ose fjalëkalim i gabuar" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Diçka shkoi keq" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "S’u lidh dot te %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Adresë e pavlefshme" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "S’ka përgjigje nga shërbyesi" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Regjistrohuni në %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Shërbyesi kërkon doemos të bëhet regjistrim përmes një sajti" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Hap sajtin" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Regjistrohuni" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Për hollësi se si të regjistroheni, shihni te %s" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Bllokoje përdoruesin" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blloko krejt përkatësinë" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Zhbllokoje" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "E fiksuar" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Fiksoje" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "E bllokuar" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Përkatësi e bllokuar" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Bllokoje" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Heshtoje" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Njoftimet u aktivizuan" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Njoftime për përmendje" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Heshtuar" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Njoftimet u çaktivizuan" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Mbi" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Rregullime" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Formësim Dhome" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Fshehtëzim" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Anëtarë" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Pronar" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Përgjegjës" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Anëtar" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Kjo kartelë e tejkalon madhësinë maksimum për ngarkime të shërbyesit." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mesazh shumë i gjatë" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "përpunoi" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "pezull…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "dërgimi dështoi" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "I pafshehtëzuar" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "S’arrihet të dërgohet mesazh" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minutë më parë" msgstr[1] "%i minuta më parë" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Mu tani" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Unë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Figura u dërgua" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Kartela u dërgua" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "U mor figurë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "U mor kartelë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Thirrje e dërguar" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Thirrje e marrë" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %B" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Dje" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Nisni Bisedë" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Përdorues" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Ftoje" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Nisni bisedë private" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Përzëre" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Akordojini leje shkrimi" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Shfuqizojini leje shkrimi" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Ftoni në Konferencë" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Përzgjidhni kartelë" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s prej %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Emër i dhomës" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Përshkrim i dhomës" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "E qëndrueshme" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Dhoma do të mbetet, pasi të dalë i pranishmi i fundit" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Mund të kërkohet publikisht" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Të pranishmit mund të ndryshojnë subjektin" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Leje për të parë JID-ra" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Cilit i është lejuar të shohë JID pjesëmarrësish?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Fjalëkalim" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "I moderuar" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Vetëm të pranishëm me zë mund të dërgojnë mesazhe" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Vetëm anëtarët" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Në dhomë mund të futen vetëm anëtarë" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Kjo konferencë s’ju lejon të dërgoni mesazhe." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Kërkesë për leje" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Dërgoni një kartelë" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Ndihmëz për butonin “nis thirrje”" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Thirrje me zë" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Thirrje me video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Kërko te mesazhet" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Hollësi Bisede" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Mbylle Bisedën" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Hollësi diagnostikimi" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Po thirret…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Po i bihet ziles…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s përfundoi thirrjen" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s s’pranoi thirrjen" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kamera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "S’u gjet kamera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofona" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "S’u gjet mikrofon." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Altoparlantë" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "S’u gjetën altoparlantë." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Ftoni për Thirrje" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Fillo" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Hyni në Kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Pasuesi" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Merrni pjesë" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Po hyhet…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lypset fjalëkalim për të hyrë në dhomë" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "I dëbuar nga pjesëmarrje apo krijim konference" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Dhoma s’ekziston" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "S’keni leje të krijoni dhomë" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Dhomë vetëm për anëtarë" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Zgjidhni nofkë tjetër" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Shumë të pranishëm në dhomë" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Shto" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Mbi Dino-n" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Sot" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i përfundim kërkimi" msgstr[1] "%i përfundime kërkimi" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Në %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Me %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Thirrje ardhëse me video" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Thirrje ardhëse me video për grupin" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Thirrje ardhëse për grupin" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Hidhe tej" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Pranojeni" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Kërkesë pajtimi" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Mohojeni" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Ftesë për te %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s ju ftoi te %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Kërkesë për leje" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s kërkon leje për shkrim te %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Dërgo njoftime shtypjesh" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Dërgo dëftesa leximi" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "On" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Off" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Parazgjedhje: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Kërkesë" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Kërko leje për dërgim mesazhesh" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Mirë se vini te Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Që t’ia filloni, bëni hyrjen ose krijoni një llogari." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Ujdisni llogari" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Administroni llogari" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "Adresë XMPP" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Emër në ekran" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Subjekt" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Përpunoni mesazhin" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ju" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Shtoni reagim" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Hape" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Ruajeni si…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s dhe %i të tjerë po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s dhe %s po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s, dhe %s po shtypin…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s po shkruan…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Nisi thirrja" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Filluar %s më parë" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Me këtë thirrje u morët në pajisje tjetër" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Thirrja përfundoi" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Përfundoi më %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Zgjati %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Thirrje e humbur" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "E humbët këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s e humbi këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Thirrja s’u pranua" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "S’e pranuat këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s s’e pranoi këtë thirrje" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Thirrja dështoi" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i orë" msgstr[1] "%i orë" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minutë" msgstr[1] "%i minuta" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "pak sekonda" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "U dorëzua" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "I lexuar" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Dërgo kërkesë" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "S’merrni ende prej këtij kontakti përditësime gjendjesh." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Ky kontakt do të donte t’ju shtonte te lista e tyre e kontakteve" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Kjo bisedë nuk mbulon reagime." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Përgjigjuni" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Kjo bisedë nuk mbulon përgjigje." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Po shkarkohen %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Po shkarkohet %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Po ngarkohen %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s ofroi: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "U ofrua kartelë: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "U ofrua kartelë" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Shpërngulja e kartelës dështoi" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Kartelë" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "E re" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Hollësi Kontakti" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Përditësoni mesazhin" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Hyrje" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "S’u vendos dot një lidhje të sigurt" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Mbrapsht" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Zgjidhni një shërbyes publik" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Ose specifikoni një adresë shërbyesi" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Hyni, më mirë" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Gjithçka e ujdisur!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Përfundoje" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Dërgo Njoftime _Shtypjesh" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Dërgo Dëftesa _Leximi" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Njoftime" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Njoftomë kur mbërrin mesazh i ri" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Shndërroji Emotikonet në Emoxhi" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Alias vendor" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Gjendje lidhjeje" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Hiqe llogarinë" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Ndryshoni fjalëkalimin" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Ndryshoje" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Fjalëkalimi i tanishëm" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Fjalëkalim i ri" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Ripohoni fjalëkalimin" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Të përgjithshme" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "S’keni fjalosje të hapura" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klikoni mbi +, që të nisni një fjalosje, ose të hyni në një kanal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Shfaq parapëlqime" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Shkurtore tastiere" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Bisedë" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Lëvizje" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Hidhu te biseda pasuese" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Hidhu te biseda e mëparshme" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Llogari" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Nofkë" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Shtoni Kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Klient Modern Fjalosjesh XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino është një klient modern fjalosjesh, me burim të hapur, për desktop. Ai " "përqendrohet në dhënien e një mënyre funksionimi të qartë dhe të qëndrueshme " "për protokoll Jabber/XMPP, teksa ka në mendje privatësinë tuaj." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Mbulon fshehtëzim skaj-më-skaj me OMEMO dhe OpenPGP dhe lejon formësim " "veçorish të lidhura me privatësinë, bie fjala, dëshmish leximi dhe njoftime " "shtypjeje." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino e sjell historikun prej shërbyesve dhe njëkohëson mesazhet në pajisje " "të tjera." #: main/data/global_search.ui:27 msgid "No active search" msgstr "S’ka kërkim aktiv" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Shtypni që të niset një kërkim" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "S’ka mesazhe me përputhje" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kontrolloni drejtshkrimin ose provoni të hiqni filtra" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Dërgoje" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Parapëlqime" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Shkurtore Tastiere" #~ msgid "Remove" #~ msgstr "Hiqe" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Hyni te %s" #~ msgid "No accounts configured" #~ msgstr "S’ka llogari të formësuara" #~ msgid "Add an account" #~ msgstr "Shtoni një llogari" #~ msgid "Pick another server" #~ msgstr "Zgjidhni një tjetër shërbyes" #~ msgid "Local Settings" #~ msgstr "Rregullime Vendore" #~ msgid "Notifications" #~ msgstr "Njoftime" #~ msgid "Pin conversation" #~ msgstr "Fiksoje bisedën" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "E fikson bisedën në krye të listës së bisedave" #~ msgid "Only when mentioned" #~ msgstr "Vetëm kur përmendet" #~ msgid "Permissions" #~ msgstr "Leje" #~ msgid "Conference Details" #~ msgstr "Hollësi Konference" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "" #~ "Përditësimet mbi komunikimin dhe gjendjet, nga cilido kah, janë të " #~ "bllokuara" #~ msgid "A password to restrict access to the room" #~ msgstr "Një fjalëkalim për të kufizuar hyrje te dhoma" #~ msgid "Message history" #~ msgstr "Historik mesazhesh" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Sasi maksimum mesazhesh historiku të emetuar nga dhoma" #~ msgid "Convert smileys to emojis" #~ msgstr "Shndërro emotikonet në emoji" #~ msgid "Check spelling" #~ msgstr "Kontroll drejtshkrimi" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klikoni këtu që të nisni një bisedë ose të hyni në një kanal." dino-0.5.0/main/po/sv.po0000664000000000000000000012247014776241610013515 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-05 07:09+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.2-rc\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Avbryt" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Anslut" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Deaktivera konto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Aktivera konto" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Välj avatar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Välj" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Bilder" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Alla filer" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Ta bort konto %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Ansluter…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Ansluten" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Frånkopplad" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Fel lösenord" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Ogiltigt TLS-certifikat" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Fel" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Konton" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Inaktiverade konton" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Lägg till konto" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Inga aktiva konton" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Logga in" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Servern kunde inte bevisa att den är %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Ditt operativsystem litar inte på dess säkerhetscertifikat." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Dess säkerhetscertifikat är utgivet till en annan domän." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Dess säkerhetscertifikat blir giltigt i framtiden." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Dess säkerhetscertifikat har gått ut." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Skapa konto" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Du kan nu börja använda kontot %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Fel användarnamn eller lösenord" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Något gick fel" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Kunde inte ansluta till %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Ogiltig adress" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Inget svar från servern" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Registrera konto på %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Servern kräver registrering genom en webbsida" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Öppna webbsida" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Registrera" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Kolla %s för information om registrering" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Blockera användare" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Blockera hela domänen" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Avblockera" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Pinnade" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Pinna" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Blockerad" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Domän blockerad" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Blockera" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Tysta" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Aviseringar aktiverade" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Aviseringar för omnämnanden" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Tystad" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Aviseringar inaktiverade" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Om" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Inställningar" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Rumskonfiguration" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Kryptering" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Medlemmar" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Ägare" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Admin" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Medlem" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Filen är större än vad servern tillåter." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Meddelandet är för långt" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "redigerad" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "väntar…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "leverans misslyckades" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Okrypterat" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Kunde inte skicka meddelandet" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H : %M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i minut sedan" msgstr[1] "%i minuter sedan" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Nyss" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Jag" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Bild skickad" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Fil skickad" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Bild mottagen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Fil mottagen" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Utgående samtal" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Inkommande samtal" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Igår" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Starta Chatt" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Användare" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Bjud in" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Starta privat konversation" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kasta ut" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Ge skrivrättigheter" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Återkalla skrivrättigheter" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Bjud in till gruppchatt" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Välj fil" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s från %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Namn på rummet" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Beskrivning av rummet" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Behåll" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "" "Rummet kommer att finnas kvar även efter att sista användaren har lämnat" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Offentligt sökbart" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Användare får ändra ämnet" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Tillstånd att visa JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Vem har tillstånd att se användarnas JID?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Lösenord" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderering" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Endast användare med tillåtelse får skicka meddelanden" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Endast för medlemmar" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Endast medlemmar får ansluta till rummet" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Den här konferensen låter dig inte skicka meddelanden." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Be om tillstånd" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Skicka en fil" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Starta samtal" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Ljudsamtal" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Videosamtal" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Sök meddelanden" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Konversationsdetaljer" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Stäng Konversation" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Felsökningsinformation" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Ringer…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Ringer…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s avslutade samtalet" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s avvisade samtalet" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameror" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Ingen kamera hittades." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofoner" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Ingen mikrofon hittades." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Högtalare" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Ingen högtalare hittades." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Bjud in till samtal" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Starta" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Gå med i kanal" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Nästa" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Gå med" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Går med…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Lösenord krävs för att gå med i rummet" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Förbjuden att gå med eller skapa gruppchattar" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Rummet finns inte" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Saknar tillåtelse att skapa rum" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Endast medlemmar tillåtna i rummet" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Välj ett annat smeknamn" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "För många användare i rummet" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Lägg till" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Om Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Idag" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i sökresultat" msgstr[1] "%i sökresultat" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "I %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Med %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Inkommande videosamtal" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Inkommande videogruppsamtal" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Inkommande gruppsamtal" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Avslå" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Godkänn" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Prenumerationsförfrågan" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Neka" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Inbjudan till %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s bjöd in dig till %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Åtkomstbegäran" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s ber om tillstånd att skriva i %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Skicka skriftaviseringar" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Skicka läsbekräftelser" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "På" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Av" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Standard: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Fråga" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Be om tillstånd att skicka meddelanden" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Välkommen till Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Logga in eller skapa ett konto för att komma igång." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Ställ in konto" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Hantera konton" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP-adress" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Visningsnamn" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Ämne" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Redigera meddelande" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Du" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Lägg till reaktion" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Öppna" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Spara som…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s och %i andra skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s och %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s och %s skriver…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s skriver…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Samtal startat" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Startade för %s sedan" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Du hanterade detta samtal på en annan enhet" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Samtal avlutat" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Avslutades %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Varade %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Missat samtal" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Du svarade inte på det här samtalet" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s svarade inte på det här samtalet" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Avvisat samtal" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Du avvisade detta samtal" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s avvisade detta samtal" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Samtal misslyckades" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i timme" msgstr[1] "%i timmar" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i minut" msgstr[1] "%i minuter" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "några sekunder" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Levererat" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Läst" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Denna kontakt vill lägga till dig i sin kontaktlista" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Den här konversationen stödjer inte reaktioner." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Svara" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Den här konversationen stödjer inte svar." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Tar emot %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s erbjöd: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Fil erbjuden: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Fil erbjuden" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Filöverföring misslyckades" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Fil" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Nytt" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kontaktdetaljer" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Uppdatera meddelande" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Kunde inte etablera en säker anslutning" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Tillbaka" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Välj en offentlig server" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Eller ange en serveraddress" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Logga in i stället" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Färdigt!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Slutför" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Skicka _Skriftaviseringar" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Skicka _läskvitton" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Aviseringar" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Avisera när ett nytt meddelande mottages" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Konvertera Smileys till Emoji" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Lokalt alias" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Anslutningsstatus" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Ta bort konto" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Allmänt" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Du har inga öppna konversationer" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Klicka på + för att starta en chatt eller gå med i en kanal" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Tangentbordsgenvägar" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Konversation" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigering" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Hoppa till nästa konversation" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Hoppa till föregående konversation" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Konto" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Smeknamn" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Alias" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Lägg till kontakt" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Modern XMPP-chattklient" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino är en moden chattklient för skrivbordet med öppen källkod. Den erbjuder " "en elegant och pålitligt upplevelse av Jabber/XMPP samtidigt som den ser " "efter din integritet." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Dino stödjer end-to-end-kryptering med OMEMO och OpenPGP och tillåter " "konfigurering av funktioner med integritetspåverkan som läsbekräftelser och " "skriftaviseringar." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino hämtar historik från servern och synkroniserar meddelanden med andra " "enheter." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Ingen aktiv sökning" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Skriv för att börja söka" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Inga meddelande matchade" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kolla stavningen eller ta bort filter" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Skicka" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Inställningar" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Snabbkommandon" #~ msgid "Remove" #~ msgstr "Ta bort" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Logga in på %s" #~ msgid "No accounts configured" #~ msgstr "Inga konfigurerade konton" #~ msgid "Add an account" #~ msgstr "Lägg till konto" #~ msgid "Pick another server" #~ msgstr "Välj en annan server" #~ msgid "Local Settings" #~ msgstr "Lokala inställningar" #~ msgid "Notifications" #~ msgstr "Aviseringar" #~ msgid "Pin conversation" #~ msgstr "Fäst konversation" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Fäst konversationen överst i konversationslistan" #~ msgid "Only when mentioned" #~ msgstr "Vid omnämnande" #~ msgid "Permissions" #~ msgstr "Tillstånd" #~ msgid "Conference Details" #~ msgstr "Konferensdetaljer" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Kommunikation och statusuppdatering i båda riktningar blockeras" #~ msgid "A password to restrict access to the room" #~ msgstr "Det krävs ett lösenord för att få tillträde till rummet" #~ msgid "Message history" #~ msgstr "Meddelandehistorik" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Maxgräns på meddelandehistoriken i rummet" #~ msgid "Convert smileys to emojis" #~ msgstr "Konvertera smileys till emojin" #~ msgid "Check spelling" #~ msgstr "Kontrollera stavning" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Klicka här för att starta en konversation eller gå med i en kanal." #~ msgid "Video call incoming" #~ msgstr "Inkommande videosamtal" #~ msgid "Call incoming" #~ msgstr "Inkommande samtal" #~ msgid "Establishing call" #~ msgstr "Upprättar samtal" #~ msgid "Video call establishing" #~ msgstr "Upprättar videosamtal" #~ msgid "Call establishing" #~ msgstr "Upprättar samtal" #~ msgid "Call in progress…" #~ msgstr "Samtal pågår…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "Varade i %s" #~ msgid "seconds" #~ msgstr "några sekunder" #~ msgid "No active conversations" #~ msgstr "Inga aktiva chattar" #~ msgid "Main window with conversations" #~ msgstr "Huvudfönster med konversationer" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s och %i andra" #~ msgid "You can now start using %s" #~ msgstr "Du kan nu börja använda %s" #~ msgid "Open Registration" #~ msgstr "Öppna Registreringen" #~ msgid "Save" #~ msgstr "Spara" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s och %s" #~ msgid "%s and %s" #~ msgstr "%s och %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "skriver…" #~ msgstr[1] "skriver…" #~ msgid "has stopped typing" #~ msgstr "har slutat skriva" #~ msgid "%i search results" #~ msgstr "%i sökresultat" #~ msgid "Discover real JIDs" #~ msgstr "Upptäckt riktiga JID:n" #~ msgid "Who may discover real JIDs?" #~ msgstr "Vem får upptäcka riktiga JID:n?" #~ msgid "Password required for room entry, if any" #~ msgstr "Lösenord som krävs för tillträde, om något" #~ msgid "Failed connecting to %s" #~ msgstr "Anslutning till %s misslyckades" #~ msgid "Join Conference" #~ msgstr "Anslut till gruppchatt" dino-0.5.0/main/po/ta.po0000664000000000000000000014232314776241610013470 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-12-19 19:12+0000\n" "Language-Team: none\n" "Language: ta\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.9.2-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "ரத்துசெய்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "இணை" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "கணக்கை முடக்கு" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "கணக்கை இயக்கவும்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "அவதாரத்தைத் தேர்ந்தெடுக்கவும்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "தேர்ந்தெடு" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "படங்கள்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "அனைத்து கோப்புகள்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "கணக்கு %s ஐ அகற்றவா?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "இணைத்தல்…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "இணைக்கப்பட்டுள்ளது" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "துண்டிக்கப்பட்டது" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "தவறான கடவுச்சொல்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "தவறான TLS சான்றிதழ்" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "பிழை" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "கணக்குகள்" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "முடக்கப்பட்ட கணக்குகள்" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "கணக்கைச் சேர்க்கவும்" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "செயலில் கணக்குகள் இல்லை" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "விடுபதிகை" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "சேவையகம் இது %s என்பதை நிரூபிக்க முடியவில்லை." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "அதன் பாதுகாப்பு சான்றிதழ் உங்கள் இயக்க முறைமையால் நம்பப்படவில்லை." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "அதன் பாதுகாப்பு சான்றிதழ் மற்றொரு களத்திற்கு வழங்கப்படுகிறது." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "அதன் பாதுகாப்பு சான்றிதழ் எதிர்காலத்தில் மட்டுமே செல்லுபடியாகும்." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "அதன் பாதுகாப்பு சான்றிதழ் காலாவதியாகிறது." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "கணக்கை உருவாக்கவும்" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "நீங்கள் இப்போது %s கணக்கைப் பயன்படுத்தலாம்." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "தவறான பயனர்பெயர் அல்லது கடவுச்சொல்" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "ஏதோ தவறு நடந்தது" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "%s உடன் இணைக்க முடியவில்லை" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "தவறான முகவரி" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "சேவையகத்திலிருந்து எந்த பதிலும் இல்லை" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "%s இல் பதிவு செய்யுங்கள்" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "சேவையகம் ஒரு வலைத்தளத்தின் மூலம் பதிவுபெற வேண்டும்" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "திறந்த வலைத்தளம்" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "பதிவு செய்யுங்கள்" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "பதிவுபெறுவது எப்படி என்பது குறித்த தகவலுக்கு %s ஐ சரிபார்க்கவும்" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "தொகுதி பயனர்" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "முழு களத்தைத் தடு" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "தடை" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "பின்" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "முள்" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "தடுக்கப்பட்டது" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "டொமைன் தடுக்கப்பட்டது" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "தொகுதி" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "முடக்கு" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "அறிவிப்புகள் இயக்கப்பட்டன" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "குறிப்புகளுக்கான அறிவிப்புகள்" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "முடக்கிய" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "அறிவிப்புகள் முடக்கப்பட்டன" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "பற்றி" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "அமைப்புகள்" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "அறை உள்ளமைவு" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "குறியாக்கம்" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "உறுப்பினர்கள்" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "உரிமையாளர்" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "நிர்வாகி" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "உறுப்பினர்" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "கோப்பு சேவையகத்தின் அதிகபட்ச பதிவேற்ற அளவை மீறுகிறது." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "செய்தி மிக நீளமானது" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "திருத்தப்பட்டது" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "நிலுவையில் உள்ளது…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "டெலிவரி தோல்வியடைந்தது" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "மறைகுறியாக்கப்படாதது" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "செய்தியை அனுப்ப முடியவில்லை" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %h∶ %m" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶ %m %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %h∶ %m" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶ %m %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %h∶ %m" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶ %m %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%m" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶ %m %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i நான் மணித்துளி" msgstr[1] "%i நான் நிமிடங்களுக்கு முன்பு" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "இப்போது" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "நான்" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "படம் அனுப்பப்பட்டது" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "கோப்பு அனுப்பப்பட்டது" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "படம் பெறப்பட்டது" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "கோப்பு பெறப்பட்டது" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "வெளிச்செல்லும் அழைப்பு" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "உள்வரும் அழைப்பு" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "நேற்று" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "உரையாடலைத் தொடங்குங்கள்" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "பயனர்" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "அழைக்கவும்" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "தனிப்பட்ட உரையாடலைத் தொடங்கவும்" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "கிக்" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "எழுத இசைவு வழங்கவும்" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "எழுதும் இசைவு ரத்து" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "மாநாட்டிற்கு அழைக்கவும்" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "கோப்பைத் தேர்ந்தெடு" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s இலிருந்து %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "அறையின் பெயர்" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "அறையின் விளக்கம்" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "விடாமுயற்சி" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "கடைசி குடியிருப்பாளர் வெளியேறிய பிறகு அறை நீடிக்கும்" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "பகிரங்கமாக தேடக்கூடியது" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "குடியிருப்பாளர்கள் இந்த விசயத்தை மாற்றலாம்" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "குழந்தைகளைப் பார்க்க இசைவு" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "குடியிருப்பாளர்களின் குழந்தைகளைப் பார்க்க யார் அனுமதிக்கப்படுகிறார்கள்?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "கடவுச்சொல்" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "மிதமான" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "குரல் கொண்ட குடியிருப்பாளர்கள் மட்டுமே செய்திகளை அனுப்பக்கூடும்" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "உறுப்பினர்கள் மட்டுமே" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "உறுப்பினர்கள் மட்டுமே அறைக்குள் நுழையலாம்" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "இந்த மாநாடு செய்திகளை அனுப்ப உங்களை அனுமதிக்காது." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "இசைவு கோருங்கள்" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "ஒரு கோப்பை அனுப்பவும்" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "அழைப்பைத் தொடங்குங்கள்" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "ஆடியோ அழைப்பு" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "வீடியோ அழைப்பு" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "தேடல் செய்திகளைத் தேடுகிறது" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "உரையாடல் விவரங்கள்" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "உரையாடலை மூடு" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "பிழைத்திருத்த செய்தி" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "அழைப்பு…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "ரிங்கிங்…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s அழைப்பை முடித்தன" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s அழைப்பை மறுத்துவிட்டது" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "கேமராக்கள்" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "கேமரா எதுவும் கிடைக்கவில்லை." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "மைக்ரோஃபோன்கள்" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "மைக்ரோஃபோன் இல்லை." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "பேச்சாளர்கள்" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "பேச்சாளர் எதுவும் கிடைக்கவில்லை." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "அழைக்க அழைக்கவும்" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "தொடங்கு" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "சேனலில் சேரவும்" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "அடுத்தது" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "சேர" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "இணைத்தல்…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "அறைக்குள் நுழைய கடவுச்சொல் தேவை" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "மாநாட்டில் சேர அல்லது உருவாக்க தடை விதிக்கப்பட்டது" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "அறை இல்லை" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "அறையை உருவாக்க அனுமதிக்கப்படவில்லை" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "உறுப்பினர்கள் மட்டுமே அறை" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "வேறு நிக் தேர்வு செய்யவும்" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "அறையில் அதிகமான குடியிருப்பாளர்கள்" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "கூட்டு" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "டினோ பற்றி" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "இன்று" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i நான் முடிவைத் தேடுகிறேன்" msgstr[1] "%i நான் முடிவுகளைத் தேடுகிறேன்" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s இல்" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s உடன்" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "உள்வரும் வீடியோ அழைப்பு" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "உள்வரும் வீடியோ குழு அழைப்பு" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "உள்வரும் குழு அழைப்பு" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "நிராகரிக்கவும்" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "ஏற்றுக்கொள்" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "சந்தா கோரிக்கை" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "மறுக்கவும்" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s க்கு அழைப்பு" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s உங்களை %s அழைத்தன" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "இசைவு கோரிக்கை" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, fuzzy, c-format msgid "%s requests the permission to write in %s" msgstr "%s இல் எழுத இசைவு கோருகிறது %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "தட்டச்சு அறிவிப்புகளை அனுப்பவும்" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "வாசிப்பு ரசீதுகளை அனுப்பவும்" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "ஆன்" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "அணை" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "இயல்புநிலை: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "கோரிக்கை" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "செய்திகளை அனுப்ப இசைவு கோருங்கள்" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "டினோவுக்கு வருக!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "தொடங்குவதற்கு உள்நுழைக அல்லது ஒரு கணக்கை உருவாக்கவும்." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "கணக்கை அமைக்கவும்" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "கணக்குகளை நிர்வகிக்கவும்" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "எக்ச்எம்பிபி முகவரி" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "காட்சி பெயர்" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "தலைப்பு" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "செய்தியைத் திருத்தவும்" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "நீங்கள்" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "எதிர்வினை சேர்க்கவும்" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "திற" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "சேமிக்கவும்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s மற்றும் %i மற்றவர்கள் தட்டச்சு செய்கிறார்கள்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s, %s தட்டச்சிடுகின்றனர்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s, %s தட்டச்சிடுகின்றனர்…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s தட்டச்சிடுகின்றார்…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "அழைப்பு தொடங்கியது" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s முன்பு தொடங்கியது" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "இந்த அழைப்பை மற்றொரு சாதனத்தில் கையாண்டீர்கள்" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "அழைப்பு முடிந்தது" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%s இல் முடிந்தது" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "நீடித்த %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "அழைப்பு தவறவிட்டது" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "இந்த அழைப்பை நீங்கள் தவறவிட்டீர்கள்" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, fuzzy, c-format msgid "%s missed this call" msgstr "%s இந்த அழைப்பை %தவறவிட்டார்" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "அழைப்பு மறுக்கப்பட்டது" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "இந்த அழைப்பை நீங்கள் மறுத்துவிட்டீர்கள்" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s இந்த அழைப்பை மறுத்துவிட்டன" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "அழைப்பு தோல்வியடைந்தது" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i நான் மணி" msgstr[1] "%i நான் மணிநேரம்" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i நான் மணித்துளி" msgstr[1] "%i நான் நிமிடங்கள்" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "சில வினாடிகள்" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "வழங்கப்பட்டது" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "படிக்க" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "இந்த தொடர்பு உங்களை அவர்களின் தொடர்பு பட்டியலில் சேர்க்க விரும்புகிறது" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "இந்த உரையாடல் எதிர்வினைகளை ஆதரிக்காது." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "பதில்" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "இந்த உரையாடல் பதில்களை ஆதரிக்கவில்லை." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "%s பதிவிறக்கப்படுகிறது…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s வழங்கப்படுகின்றன: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "கோப்பு வழங்கப்படுகிறது: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "கோப்பு வழங்கப்படுகிறது" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "கோப்பு பரிமாற்றம் தோல்வியடைந்தது" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "கோப்பு" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "புதிய" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "தொடர்பு விவரங்கள்" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "செய்தியைப் புதுப்பிக்கவும்" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "பாதுகாப்பான இணைப்பை நிறுவ முடியவில்லை" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "பின்" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "பொது சேவையகத்தைத் தேர்வுசெய்க" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "அல்லது சேவையக முகவரியைக் குறிப்பிடவும்" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "அதற்கு பதிலாக உள்நுழைக" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "அனைத்தும் அமைக்கப்பட்டன!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "முடிக்க" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "தட்டச்சு அறிவிப்புகளை அனுப்பவும்" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "வாசிப்பு ரசீதுகளை அனுப்பவும்" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_ குறிப்புகள்" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "ஒரு புதிய செய்தி வரும்போது அறிவிக்கவும்" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_ ச்மைல்களை ஈமோசிக்கு மாற்றவும்" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "உள்ளக மாற்றுப்பெயர்" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "இணைப்பு நிலை" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "கணக்கை அகற்று" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "பொது" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "உங்களிடம் திறந்த அரட்டைகள் இல்லை" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "அரட்டையைத் தொடங்க + அல்லது சேனலில் சேர + சொடுக்கு செய்க" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "விசைப்பலகை குறுக்குவழிகள்" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "உரையாடல்" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "வானோடல்" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "அடுத்த உரையாடலுக்கு செல்லவும்" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "முந்தைய உரையாடலுக்கு செல்லவும்" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "கணக்கு" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "நிக்" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "மாற்றுப்பெயர்" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "தொடர்பைச் சேர்க்கவும்" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "நவீன எக்ச்எம்பிபி அரட்டை வாங்கி" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "டினோ டெச்க்டாப்பிற்கான நவீன திறந்த மூல அரட்டை வாங்கி ஆகும். இது உங்கள் தனியுரிமையை " "மனதில் கொண்டிருக்கும்போது தூய்மையான மற்றும் நம்பகமான சாபர்/எக்ச்எம்பிபி அனுபவத்தை " "வழங்குவதில் கவனம் செலுத்துகிறது." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "இது OMEMO மற்றும் OpenPGP உடன் இறுதி முதல் இறுதி குறியாக்கத்தை ஆதரிக்கிறது மற்றும் " "தனியுரிமை தொடர்பான அம்சங்களான வாசிப்பு ரசீதுகள் மற்றும் தட்டச்சு அறிவிப்புகள் போன்றவற்றை " "உள்ளமைக்க அனுமதிக்கிறது." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "டினோ சேவையகத்திலிருந்து வரலாற்றைப் பெறுகிறது மற்றும் பிற சாதனங்களுடன் செய்திகளை " "ஒத்திசைக்கிறது." #: main/data/global_search.ui:27 msgid "No active search" msgstr "செயலில் தேடல் இல்லை" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "தேடலைத் தொடங்க தட்டச்சு செய்க" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "பொருந்தக்கூடிய செய்திகள் இல்லை" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "எழுத்துப்பிழை சரிபார்க்கவும் அல்லது வடிப்பான்களை அகற்ற முயற்சிக்கவும்" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "அனுப்பு" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "விருப்பத்தேர்வுகள்" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "விசைப்பலகை குறுக்குவழிகள்" #~ msgid "Remove" #~ msgstr "அகற்று" #, c-format #~ msgid "Sign in to %s" #~ msgstr "%s இல் உள்நுழைக" #~ msgid "No accounts configured" #~ msgstr "கணக்குகள் எதுவும் கட்டமைக்கப்படவில்லை" #~ msgid "Add an account" #~ msgstr "ஒரு கணக்கைச் சேர்க்கவும்" #~ msgid "Pick another server" #~ msgstr "மற்றொரு சேவையகத்தைத் தேர்ந்தெடுங்கள்" dino-0.5.0/main/po/tr.po0000664000000000000000000012230514776241610013507 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-23 19:09+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.1-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "İptal" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Bağlan" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Hesabı devre dışı bırak" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Hesabı etkinleştir" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Avatar seç" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Seç" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Görseller" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Tüm dosyalar" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Hesabı sil %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Bağlanıyor…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Bağlandı" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Bağlantı koptu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Yanlış parola" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Geçersiz TLS Sertifikası" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Hata" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Hesaplar" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Devre dışı hesaplar" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Hesap Ekle" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Etkin hesap yok" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Giriş yap" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Sunucu, %s olduğunu kanıtlayamadı." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Güvenlik sertifikasına işletim sisteminiz güvenmiyor." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Güvenlik sertifikası başka bir etki alanına verildi." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Güvenlik sertifikası yalnızca gelecekte geçerli olacak." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Güvenlik sertifikasının süresi doldu." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Hesap oluştur" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Artık %s hesabını kullanabilirsiniz." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Yalnış kullanıcı adı veya parola" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Bir şeyler yanlış gitti" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Bağlantı kurulamadı %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Geçersiz adres" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Sunucudan yanıt yok" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Kaydol - %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Sunucu bir websitesine kayıt olmanı şart koşuyor" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Web sitesini aç" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Kayıt" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Nasıl kaydolacağınızla ilgili bilgi için %s sayfasına bakın" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Kullanıcıyı engelle" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Tüm etki alanını engelle" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Engeli kaldır" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Sabitlendi" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Sabitle" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Engellendi" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Etki alanı engellendi" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Engelle" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Sesini kapat" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Bildirimler etkinleştirildi" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Bahsetmeler için bildirimler" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Sesi kapatıldı" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Bildirimler devre dışı bırakıldı" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Hakkında" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Ayarlar" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Oda Ayarları" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Şifreleme" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Üyeler" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Sahip" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Yönetici" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Üye" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Dosya, sunucunun azami yükleme boyutunu aşıyor." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Mesaj çok uzun" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "düzenlendi" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "bekliyor…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "teslimat başarısız" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Şifrelenmemiş" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Mesaj gönderilemedi" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b %d, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b %d, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H.%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i dakika önce" msgstr[1] "%i dakıka önce" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Şu an" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Ben" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Resim gönderildi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Dosya gönderildi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Resim alındı" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Dosya alındı" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Giden arama" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Gelen arama" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Dün" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Sohbet Başlat" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Kullanıcı" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Davet et" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Özel sohbet başlat" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Kov" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Yazma izni ver" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Yazma iznini iptal et" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Görüşmeye Davet et" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Dosya seç" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s - %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Oda ismi" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Oda tanımı" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Kalıcılık" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Oda, son katılımcı ayrıldıktan sonra da devam eder" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Herkese açık aranabilir" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Katılımcılar başlığı değiştirebilir" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JID'leri görüntüleme izni" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Katılımcıların JID'lerini kimler görüntüleyebilir?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Parola" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Moderasyonlu" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Sadece sesli katılımcılar mesaj gönderebilir" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Sadece üyeler" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Sadece üyeler odaya giriş yapabilir" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Bu görüşme mesaj göndermenize izin vermiyor." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "İzin iste" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Dosya gönder" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Arama başlat" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Sesli arama" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Görüntülü arama" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Mesajları ara" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Sohbet Ayrıntıları" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Sohbeti Kapat" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Hata ayıklama bilgileri" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Aranıyor…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Çalıyor…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s aramayı sonlandırdı" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s aramayı reddetti" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Kameralar" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Kamera bulunamadı." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Mikrofonlar" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Mikrofon bulunamadı." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Hoparlörler" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Hoparlör bulunamadı." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Aramaya Davet Et" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Başla" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Kanala katıl" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "İleri" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Katıl" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Katılıyor…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Odaya girmek için parola gerekli" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Görüşmeye katılmak veya oluşturmaktan engellendiniz" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Böyle bir oda yok" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Oda oluşturulmasına izin verilmedi" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Sadece üyeler odası" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Başka bir takma isim seçin" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Odada çok fazla katılımcı var" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Ekle" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Dino Hakkında" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Bugün" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i arama sonucu" msgstr[1] "%i arama sonucu" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "%s içinden" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "%s ile" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Gelen görüntülü arama" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Gelen görüntülü grup araması" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Gelen grup araması" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Reddet" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Kabul et" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Abonelik isteği" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Reddet" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s'e davet" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s seni %s'e davet etti" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "İzin isteği" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s, %s için yazma izni istiyor" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "\"Yazıyor\" bildirimi gönder" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "\"Okundu\" bilgisi gönder" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Açık" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Kapalı" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Varsayılan: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "İste" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Mesaj göndermek için izin isteyin" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Dino'ya hoş geldiniz!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Başlamak için oturum açın veya bir hesap oluşturun." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Hesap oluştur" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Hesapları yönet" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP Adresi" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Görünen ad" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Konu" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Mesajı düzenle" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Sen" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Tepki ekle" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Aç" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Farklı kaydet…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s ve %i diğerleri yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s ve %s yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s ve %s yazıyor…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s yazıyor…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Arama başladı" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "%s önce başladı" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Bu aramayı başka bir aygıtta yaptınız" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Arama sona erdi" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "%s'de sona erdi" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "%s sürdü" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Cevapsız arama" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Bu aramayı kaçırdınız" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s bu aramayı kaçırdı" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Arama reddedildi" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Bu aramayı reddettiniz" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s bu aramayı reddetti" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Arama başarısız" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i saat" msgstr[1] "%i saat" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i dakika" msgstr[1] "%i dakika" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "birkaç saniye" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Teslim edildi" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Okundu" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Bu kişi seni kendi kişiler listesine eklemek istiyor" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Bu sohbet tepkileri desteklemiyor." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Yanıtla" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Bu sohbet yanıtları desteklemiyor." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "İndiriliyor %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s teklif etti: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Dosya teklif edildi: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Dosya teklif edildi" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Dosya transferi başarısız" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Dosya" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Yeni" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Kişi Ayrıntıları" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Mesajı güncelle" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Güvenli bir bağlantı oluşturulamadı" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Geri" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Genel bir sunucu seç" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Veya özel bir sunucu adresi" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Bunun yerine oturum açın" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Hepsi tamam!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Bitir" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "_Yazma Bildirimleri Gönder" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "_Okundu Bilgisi Gönder" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "_Bildirimler" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Mesaj ulaştığında bildirim gönder" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "Gülen Yüzleri Emoji'ye _Dönüştür" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Yerel mahlas" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Bağlantı durumu" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Hesabı kaldır" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Genel" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Henüz burada sohbet yok" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Sohbet başlatmak veya bir kanala katılmak için +'ya tıklayın" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Klavye kısayolları" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Sohbet" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Navigasyon" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Bir sonraki sohbete geç" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Bir önceki sohbete geç" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Hesap" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Takma isim" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Mahlas" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Kişi Ekle" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Modern XMPP Sohbet İstemcisi" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino masaüstü bilgisayarlar için modern, açık kaynaklı bir sohbet " "uygulamasıdır. Gizlilik hassasiyetinizi göz önünde bulundurarak temiz ve " "güvenilir bir Jabber/XMPP deneyimi sunmaya odaklanır." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "OMEMO ve OpenPGP ile baştan sona şifreleme destekler ve \"okundu\" bilgisi, " "\"yazıyor...\" bildirimi gibi gizlilikle alakalı özelliklerin " "ayarlanabilmesini sağlar." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino sunucudan konuşma geçmişini sunucudan çeker ve diğer cihazlara " "senkronize eder." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Henüz arama yapılmadı" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Arama başlatmak için bir şey yaz" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Hiç mesaj bulunamadı" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Yazım hatalarını kontrol et ya da filtreleri kaldırmayı dene" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Gönder" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Tercihler" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Klavye Kısayolları" #~ msgid "Remove" #~ msgstr "Sil" #, c-format #~ msgid "Sign in to %s" #~ msgstr "%s'e giriş yap" #~ msgid "No accounts configured" #~ msgstr "Hiç hesap ayarlanmamış" #~ msgid "Add an account" #~ msgstr "Bir hesap ekle" #~ msgid "Pick another server" #~ msgstr "Başka sunucu seç" #~ msgid "Local Settings" #~ msgstr "Yerel Ayarlar" #~ msgid "Notifications" #~ msgstr "Bildirimler" #~ msgid "Pin conversation" #~ msgstr "Sohbeti sabitle" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Sohbeti, sohbet listesinin en üstüne sabitler" #~ msgid "Only when mentioned" #~ msgstr "Sadece atıf yapıldığında" #~ msgid "Permissions" #~ msgstr "İzinler" #~ msgid "Conference Details" #~ msgstr "Görüşme Ayrıntıları" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "İletişim ve durum güncellemeler her iki taraf için engellenir" #~ msgid "A password to restrict access to the room" #~ msgstr "Odaya erişimi kısıtlayan bir şifre" #~ msgid "Message history" #~ msgstr "Mesaj geçmişi" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Odada kaydedilecek en fazla mesaj sayısı" #~ msgid "Convert smileys to emojis" #~ msgstr "İfadeleri emoji olarak göster" #~ msgid "Check spelling" #~ msgstr "Yazım denetimi" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "Bir sohbet başlatmak ya da kanala katılmak için buraya tıkla." #~ msgid "Video call incoming" #~ msgstr "Gelen görüntülü arama" #~ msgid "Call incoming" #~ msgstr "Gelen arama" #~ msgid "Establishing call" #~ msgstr "Arama oluşturuluyor" #~ msgid "Video call establishing" #~ msgstr "Görüntülü arama oluşturuluyor" #~ msgid "Call establishing" #~ msgstr "Arama oluşturuluyor" #~ msgid "Call in progress…" #~ msgstr "Arama devam ediyor…" #, c-format #~ msgid "Lasted for %s" #~ msgstr "%s sürdü" #~ msgid "seconds" #~ msgstr "saniye" #~ msgid "No active conversations" #~ msgstr "Aktif sohbet yok" #~ msgid "%s, %s and %i others" #~ msgstr "%s, %s ve %i diğerleri" #~ msgid "You can now start using %s" #~ msgstr "Şimdi %s kullanmaya başlayabilirsin" #~ msgid "Open Registration" #~ msgstr "Açık Kaydolma" #~ msgid "Save" #~ msgstr "Kaydet" #~ msgid "%s, %s and %s" #~ msgstr "%s, %s ve %s" #~ msgid "%s and %s" #~ msgstr "%s ve %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "yazıyor…" #~ msgstr[1] "yazıyorlar…" #~ msgid "has stopped typing" #~ msgstr "yazmayı sonlandırdı" #~ msgid "%i search results" #~ msgstr "%i adet sonuc bulundu" #~ msgid "Discover real JIDs" #~ msgstr "Gerçek JIDs keşfetme" #~ msgid "Who may discover real JIDs?" #~ msgstr "Kimler gerçek JIDs keşfedebilir?" #~ msgid "Password required for room entry, if any" #~ msgstr "Odaya giriş için yapmak için parola ( gerekirse )" dino-0.5.0/main/po/uk.po0000664000000000000000000013236014776241610013503 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-13 08:50+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.10.3-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Скасувати" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Приєднатися" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "Вимкнути обліковий запис" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "Увімкнути обліковий запис" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Вибрати аватар" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Вибрати" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Зображення" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Усі файли" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Видалити обліковий запис %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "З'єднання…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Під’єднано" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Роз'єднано" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Хибний пароль" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Недійсний сертифікат TLS" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Помилка" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Облікові записи" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "Вимкнені облікові записи" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Додати обліковий запис" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Немає активних облікових записів" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Увійти" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Сервер не зміг підтвердити, що це %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "Ваша операційна система не довіряє його сертифікату безпеки." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Його сертифікат безпеки видано іншому домену." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Його сертифікат безпеки стане дійсним тількі у майбутньому." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Його сертифікат безпеки закінчився." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Створити обліковий запис" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Тепер ви можете використовувати обліковий запис %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Неправильне ім'я користувача чи пароль" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Щось пішло не так" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Не вдалося підключитися до %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Недійсна адреса" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Немає відповіді від сервера" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Зареєструватись в %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Сервер вимагає реєстрації через веб-сайт" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Відкрити веб-сайт" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Зареєструватися" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Прочитайте %s, щоб дізнатися про процес реєстрації" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "Заблокувати користувача" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "Заблокувати весь домен" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "Розблокувати" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "Прикріплено" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "Прикріпити" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "Заблоковано" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "Домен заблоковано" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Заблокувати" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "Заглушити" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "Сповіщення активовані" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "Сповіщення про згадки" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "Заглушено" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "Сповіщення вимкнено" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "Докладніше" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Налаштування" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Налаштування кімнати" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "Шифрування" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Члени" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Власник" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Адміністратор" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Учасник" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Розмір файлу перевищує дозволений для завантаження на сервер." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Повідомлення задовге" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "відредаговано" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "очикування…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "доставка невдалася" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Нешифровано" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Не вдалося надіслати повідомлення" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%H∶%M, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%l∶%M, %x %p" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%d %b, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%d %b, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i хвилина тому" msgstr[1] "%i хвилини тому" msgstr[2] "%i хвилин тому" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Щойно" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Я" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Зображення надіслано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Файл надіслано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Зображення отримано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Файл отримано" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Вихідний виклик" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Вхідний виклик" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b %d" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Учора" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Почати розмову" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Користувач" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Запросити" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Почати приватну розмову" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Вигнати" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Видати дозвіл писати" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Відкликати дозвіл писати" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Запросити до конференції" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Виберіть файл" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s з %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Назва кімнати" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Опис кімнати" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Постійна" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Кімната буде збережена після в виходу останнього відвідувача" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Доступний для загального пошуку" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Відвідувачи можуть змінювати тему" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Дозвіл на перегляд JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Кому дозволено перегляд JID відвідувачів?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Пароль" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Модерований" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Тільки відвідувачи за голосом можуть відсилати повідомлення" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Тільки для учасників" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Тільки учасники можуть зайти до кімнати" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Вам заборонено відправляти повідомлення в цій бесіді." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Попросити дозвіл" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Відіслати файл" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Распочати виклик" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Аудіо виклик" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Відео виклик" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Пошук повідомлень" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "Деталі розмови" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "Закрити розмову" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Налагоджувальна інформація" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Йде виклик…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Йде дзвінок…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s заверши(в|ла) виклик" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s відхилив виклик" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Камери" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Камер не знайдено." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Мікрофони" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Мікрофонів не знайдено." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Колонки" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Колонок не знайдено." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Викликати" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Почати" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Приєднатись до каналу" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Далі" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Приєднатися" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Приєднання…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Для входу в кімнату потрібно ввести пароль" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Вам заборонено приєднуватися або створювати конференції" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Кімната не існує" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Вам заборонено створювати кімнати" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Кімната тільки для учасників" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Оберіть інше прізвисько" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "У кімнаті занадто багато відвідувачів" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Додати" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "О Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Сьогодні" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%a, %b %d" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i результат пошуку" msgstr[1] "%i результати пошуку" msgstr[2] "%i результатів пошуку" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "В %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "З %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Вхідний відео виклик" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Вхідний групповий відео виклик" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Вхідний групповий виклик" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Відхилити" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Прийняти" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Необхідна авторизація" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Заборонити" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Запрошення до %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s запросив вас до %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Запит на дозвіл" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s запитує дозвіл писати в %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Надситали сповіщення про введення тексту" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Надсилати сповіщення про прочтення" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Включити" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Виключити" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "За замовчуванням: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Запросити" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Запросити дозвіл на відсилку повідомлення" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Ласкаво просимо в Діно!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Увійдіть в систему або створіть обліковий запис, щоб почати." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Налаштування облікового запису" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Керування обліковими записами" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP Адреса" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "Ім'я для відображення" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "Тема" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Редагувати повідомлення" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Ви" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Додати реакцію" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Відкрити" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Зберегти як…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s та %i інших друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s та %s друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s і %s друкують…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s друкує…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Виклик разпочато" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Розпочато %s тому" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Ви обробили цей виклик на іншому пристрої" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Виклик закінчено" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Закінчено в %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Тривав %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Виклик пропущено" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Ви пропустили виклик" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s пропустил(а) цей виклик" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Виклик відхиленно" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Ви відхилили виклик" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s відхили(в|ла) виклик" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Помилка виклика" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i година" msgstr[1] "%i години" msgstr[2] "%i годин" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i минута" msgstr[1] "%i минути" msgstr[2] "%i минут" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "декілька секунд" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Доставлено" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Прочитано" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "Надіслати запит" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "Ви ще не отримуєте оновлення статусу від цього контакту." #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Цей контакт хоче додати вас до свого списку контактів" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Ця бесіда не підтримує реакций." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Відповісти" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Ця бесіда не підтримує відповідей." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "Завантаження %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Завантаження %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "Вивантаження %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s приблизний розмір: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Приблизний розмір файлу: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Запропонований Файл" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Помилка передачі файлу" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Файл" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "Нове" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Контактні данні" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Оновити повідомлення" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "Логін" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Не вдалося встановити безпечне з'єднання" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Назад" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Оберіть публічний сервер" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Або вкажіть адресу сервера" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Увійти замість" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Все готово!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Закінчити" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "Слати сповіщення про _друкування" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "Слати сповіщення про _читання" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "Сповіщення" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Сповіщати про надходження нового повідомлення" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "_Перетворювати смайлики на емодзі" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Місцевий псевдонім" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "Стан під'єднання" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "Видалити обліковий запис" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "Змінити пароль" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "Зміна" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "Поточний пароль" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "Новий пароль" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "Підтвердьте пароль" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Загальні" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "У вас немає відкритих спілкуваннь" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Натисніть + щоб роспочати спілкування або приєднатися до каналу" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "Показати вподобання" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "Клавіатурні комбінації" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Бесіда" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Навігація" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Перейти до наступної бесіди" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Перейти до попередньої бесіди" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Обліковий запис" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Прізвисько" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Псевдонім" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Добавити контакт" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "Сучасний кліент XMPP" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino - це сучасний клієнт чату з відкритим вихідним кодом для комп’ютера. " "Він зосереджений на забезпеченні чистого та надійного досвіду Jabber/XMPP, " "зважаючи на вашу конфіденційність." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Він підтримує наскрізне шифрування за допомогою OMEMO та OpenPGP і дозволяє " "налаштовувати пов’язані з конфіденційністю функції, такі як сповіщення про " "прочитання та сповіщення про введення тексту." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "" "Dino отримує історію з сервера та синхронізує повідомлення з іншими " "пристроями." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Пошук по повідомленням" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Введіть, щоб почати пошук" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Повідомлень не знайдено" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Перевірте набраний текст або спробуйте видалити фільтри" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Відіслати" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "Уподобання" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Гарячі клавіши" #~ msgid "Remove" #~ msgstr "Видалити" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Вхід в обліковий запис %s" #~ msgid "No accounts configured" #~ msgstr "Облікові записи не налаштовано" #~ msgid "Add an account" #~ msgstr "Добавити обліковий запис" #~ msgid "Pick another server" #~ msgstr "Оберіть інший сервер" #~ msgid "Local Settings" #~ msgstr "Локальні налаштування" #~ msgid "Notifications" #~ msgstr "Сповіщення" #~ msgid "Pin conversation" #~ msgstr "Закріпити бесіду" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Закріпити бесіду вгорі списку розмов" #~ msgid "Only when mentioned" #~ msgstr "Тільки якщо вказано" #~ msgid "Permissions" #~ msgstr "Дозволи" #~ msgid "Conference Details" #~ msgstr "Деталі конференції" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Зв'язок і оновлення статусу в будь-якому напрямку заблоковано" #~ msgid "A password to restrict access to the room" #~ msgstr "Пароль обмежує доступ до кімнати" #~ msgid "Message history" #~ msgstr "Історія повідомлень" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Максимальна кількість повідомлень в історії кімнати" #~ msgid "Convert smileys to emojis" #~ msgstr "Перетворювати смайли на емодзі" #~ msgid "Check spelling" #~ msgstr "Перевіряти правопис" dino-0.5.0/main/po/vi.po0000664000000000000000000012132414776241610013500 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-04-28 07:51+0000\n" "Language-Team: none\n" "Language: vi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.18-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "Huỷ" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "Kết nối" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "Chọn ảnh đại diện" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "Chọn" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "Hình ảnh" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "Toàn bộ tệp tin" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "Gỡ bỏ tài khoản %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "Đang kết nối…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "Đã kết nối" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "Đã ngắt kết nối" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "Sai mật khẩu" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "Chứng thực TLS không hợp lệ" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "Lỗi" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "Tài khoản" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "Thêm tài khoản" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "Không có tài khoản nào hoạt động" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "Đăng nhập" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "Máy chủ không thể xác minh đó là %s." #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "" "Chứng chỉ bảo mật của nó không được tin tưởng bởi hệ điều hành của bạn." #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "Chứng chỉ bảo mật của nó được cấp cho một tên miền khác." #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "Chứng chỉ bảo mật của nó chỉ có hiệu lực trong tương lai." #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "Chứng chỉ bảo mật của nó đã hết hạn." #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "Tạo tài khoản" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "Bây giờ bạn có thể dùng tài khoản %s." #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "Sai tên đăng nhập hoặc mật khẩu" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "Đã xảy ra lỗi" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "Không thể kết nối đến %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "Địa chỉ không đúng" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "Không có phản hồi từ máy chủ" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "Đăng ký trên %s" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "Máy chủ yêu cầu đăng ký thông qua một trang web" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "Mở trang web" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "Đăng ký" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "Xem %s để có thêm thông tin về cách đăng ký" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "Chặn" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "Cài đặt" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "Cấu hình phòng" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "Danh sách thành viên" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "Chủ sở hữu" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "Quản trị viên" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "Thành viên" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "Tệp tin vượt quá kích thước tải lên tối đa của máy chủ." #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "Tin nhắn quá dài" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "đã chỉnh sửa" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "đang gửi…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "gửi thất bại" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "Không mã hoá" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "Không thể gửi tin nhắn" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%H:%M, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%l:%M %p, %x" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%H:%M, %d %b" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%l:%M %p, %d %b" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%a, %H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%a, %l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%l∶%M %p" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i phút trước" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "Ngay bây giờ" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "Tôi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "Đã gửi ảnh" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "Đã gửi file" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "Đã nhận ảnh" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "Đã nhận file" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "Cuộc gọi đi" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "Cuộc gọi đến" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%d %b" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "Hôm qua" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "Bắt đầu cuộc trò chuyện" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "Người dùng" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "Mời" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "Bắt đầu một cuộc trò chuyện riêng tư" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "Đuổi" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "Cấp quyền chat" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "Thu hồi quyền chat" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "Mời tham gia Cuộc họp" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "Chọn tệp tin" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s từ %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "Tên phòng" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "Mô tả phòng" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "Duy trì phòng" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "Phòng sẽ được duy trì sau khi người tham gia cuối cùng rời khỏi" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "Cho phép tìm thấy công khai" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "Người tham gia có thể thay đổi chủ đề" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "Quyền xem JID" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "Ai là người được xem JID của người tham gia?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "Mật khẩu phòng" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "Kiểm duyệt viên" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "Chỉ người tham gia có âm thanh mới được gửi tin nhắn" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "Chỉ thành viên" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "Chỉ thành viên mới có thể tham gia phòng" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "Cuộc họp này không cho phép bạn gửi tin nhắn." #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "Yêu cầu quyền" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "Gửi một tệp tin" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "Bắt đầu cuộc gọi" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "Cuộc gọi thoại" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "Cuộc gọi video" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "Tìm tin nhắn" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "Thông tin gỡ lỗi" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "Đang gọi…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "Đang đổ chuông…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s đã kết thúc cuộc gọi" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s đã từ chối cuộc gọi" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "Camera" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "Không tìm thấy camera." #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "Microphone" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "Không tìm thấy microphone." #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "Loa" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "Không tìm thấy loa." #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "Mời tham gia Cuộc gọi" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "Bắt đầu" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "Tham gia kênh" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "Tiếp tục" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "Tham gia" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "Đang tham gia…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "Cần mật khẩu để vào phòng này" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "Bạn bị cấm tham gia và tạo cuộc họp" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "Phòng không tồn tại" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "Không được phép tạo phòng" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "Phòng chỉ dành cho thành viên" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "Hãy chọn một biệt danh khác" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "Có quá nhiều người tham dự trong phòng" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "Thêm" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "Về Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "Hôm nay" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%a, %d %b" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "Có %i kết quả" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "Trong %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "Với %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "Cuộc gọi video đến" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "Cuộc gọi nhóm video đến" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "Cuộc gọi nhóm đến" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "Từ chối" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "Chấp nhận" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "Yêu cầu theo dõi" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "Từ chối" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "Lời mời tham gia %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s đã mời bạn tham gia %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "Yêu cầu cấp quyền" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s yêu cầu quyền được chat trong %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "Hiện trạng thái Đang nhập" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "Hiện trạng thái Đã đọc" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "Bật" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "Tắt" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "Mặc định: %s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "Yêu cầu" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "Yêu cầu quyền để gửi tin nhắn" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "Chào mừng đến với Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "Đăng nhập hoặc tạo một tài khoản để bắt đầu." #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "Tạo tài khoản" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "Quản lý các tài khoản" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "Sửa tin nhắn" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "Bạn" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "Bày tỏ cảm xúc" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "Mở" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "Lưu dưới dạng…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s và %i người khác đang nhập…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s và %s đang nhập…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s và %s đang nhập…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s đang nhập…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "Cuộc gọi đã bắt đầu" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "Đã bắt đầu %s trước" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "Bạn đang tham gia cuộc gọi này trên một thiết bị khác" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "Cuộc gọi đã kết thúc" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "Kết thúc lúc %s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "Kéo dài %s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "Cuộc gọi bị nhỡ" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "Bạn đã bỏ lỡ cuộc gọi này" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s đã bỏ lỡ cuộc gọi này" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "Đã từ chối cuộc gọi" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "Bạn đã từ chối cuộc gọi này" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s đã từ chối cuộc gọi này" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "Cuộc gọi thất bại" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i giờ" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i phút" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "vài giây" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "Đã gửi" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "Đọc" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "Liên hệ này muốn thêm bạn vào danh sách liên hệ của họ" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "Cuộc hội thoại này không hỗ trợ bày tỏ cảm xúc." #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "Phản hồi" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "Cuộc hội thoại này không hỗ trợ phản hồi." #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "Đang tải xuống %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s dung lượng: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "Tệp tin kích thước: %s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "Tệp tin được chia sẻ" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "Truyền tệp tin thất bại" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "Tệp tin" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "Thông tin liên hệ" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "Cập nhật tin nhắn" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "Không thể thiết lập kết nối an toàn" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "Quay lại" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "Chọn một máy chủ công cộng" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "Hoặc chỉ định một địa chỉ máy chủ" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "Thay vào đó, hãy đăng nhập" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "Tất cả đã được thiết lập!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "Hoàn tất" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "Thông báo khi có tin nhắn mới" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "Bí danh cục bộ" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "Chung" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "Bạn không mở cuộc trò chuyện nào" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "Nhấn + để bắt đầu một cuộc trò chuyện hoặc tham gia một kênh" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "Hội thoại" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "Điều hướng" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "Chuyển sang cuộc hội thoại tiếp theo" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "Chuyển đến cuộc hội thoại trước đó" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "Tài khoản" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "Biệt danh" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "Bí danh" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "Thêm Liên hệ" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino là một ứng dụng chat mã nguồn mở hiện đại cho máy tính. Nó tập trung " "vào việc cung cấp một trải nghiệm Jabber/XMPP gọn gàng và tin cậy trong khi " "vẫn quan tâm đến quyền riêng tư của bạn." #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "Nó hỗ trợ mã hoá đầu cuối với OMEMO và OpenPGP, đồng thời cho phép cấu hình " "các tính năng liên quan đến quyền riêng tư như thông báo trạng thái tin nhắn " "và trạng thái đang nhập." #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino lấy lịch sử từ server và đồng bộ tin nhắn với các thiết bị khác." #: main/data/global_search.ui:27 msgid "No active search" msgstr "Không có tìm kiếm nào" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "Nhập để bắt đầu tìm" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "Không khớp tin nhắn nào" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "Kiểm tra chính tả hoặc thử bỏ bớt các bộ lọc" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "Gửi" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "Phím tắt" #~ msgid "Remove" #~ msgstr "Gỡ bỏ" #, c-format #~ msgid "Sign in to %s" #~ msgstr "Đăng nhập vào %s" #~ msgid "No accounts configured" #~ msgstr "Không có tài khoản được cấu hình" #~ msgid "Add an account" #~ msgstr "Thêm một tài khoản" #~ msgid "Pick another server" #~ msgstr "Chọn một máy chủ khác" #~ msgid "Local Settings" #~ msgstr "Cài đặt cục bộ" #~ msgid "Notifications" #~ msgstr "Thông báo" #~ msgid "Pin conversation" #~ msgstr "Ghim cuộc hội thoại" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "Ghim cuộc hội thoại lên đầu danh sách hội thoại" #~ msgid "Only when mentioned" #~ msgstr "Chỉ khi được nhắc đến" #~ msgid "Permissions" #~ msgstr "Quyền" #~ msgid "Conference Details" #~ msgstr "Chi tiết cuộc họp" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "Liên lạc và cập nhật trạng thái giữa hai bên sẽ bị chặn" #~ msgid "A password to restrict access to the room" #~ msgstr "Mật khẩu để hạn chế quyền truy cập đến phòng này" #~ msgid "Message history" #~ msgstr "Lịch sử tin nhắn" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "Số lượng tin nhắn tối đa được lưu lại lịch sử trong phòng" #~ msgid "Convert smileys to emojis" #~ msgstr "Chuyển đổi biểu tượng mặt cười :) thành biểu tượng cảm xúc" #~ msgid "Check spelling" #~ msgstr "Kiểm tra chính tả" #~ msgid "Modern XMPP Chat Client" #~ msgstr "Ứng dụng chat XMPP hiện đại" dino-0.5.0/main/po/zh_CN.po0000664000000000000000000012114214776241610014061 0ustar rootroot# Chinese translations for PACKAGE package # PACKAGE 软件包的简体中文翻译. # Copyright (C) 2017 THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # Tong Hui , 2017. msgid "" msgstr "" "Project-Id-Version: dino\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-09 17:18+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.11-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "取消" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "连接" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "禁用账号" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "启用账号" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "选择头像" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "选择" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "图片" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "所有文件" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "移除账号 %s?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "正在连接…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "已连接" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "已断开连接" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "密码错误" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "TLS 证书无效" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "错误" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "账号" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "已禁用的账号" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "添加账号" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "无活动账号" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "登录" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "服务器无法证明它是 %s。" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "其安全证书未受您的操作系统信任。" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "其安全证书已颁发给另一个域名。" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "其安全证书仅在未来有效。" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "其安全证书已过期。" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "创建账号" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "您现在可以使用账号 %s。" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "用户名或密码错误" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "出了点问题" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "无法连接到 %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "地址无效" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "服务器无响应" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "在 %s 注册" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "服务器要求通过网站注册" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "打开网站" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "注册" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "查看 %s 以获取有关如何注册的信息" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "屏蔽用户" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "屏蔽整个域" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "取消屏蔽" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "已固定" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "固定" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "已屏蔽" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "域已屏蔽" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "屏蔽" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "静音" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "通知已启用" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "提及通知" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "已静音" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "通知已禁用" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "关于" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "设置" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "群聊配置" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "加密方式" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "参与者" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "所有者" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理员" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "成员" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "文件超过了服务器的最大上传大小。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "消息太长" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "已编辑" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "正在发送…" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "发送失败" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "未加密" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "无法发送消息" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x %p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%_m月%_d日 %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%_m月%_d日 %p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%A %H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%A %p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分钟前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "刚刚" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "我" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "图片已发送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "文件已发送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "图片已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "文件已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "去电" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "来电" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%_m月%_d日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨天" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "开始对话" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "访客" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "邀请" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "开始私聊" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "踢出" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "授予发言权" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "撤销发言权" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "邀请加入群聊" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "选择文件" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s 来自 %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "群聊名" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "群聊描述" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "持久" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "最后一位参与者离开后,群聊将仍然存在" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "可公开搜索" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "参与者可以更改主题" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "查看 XMPP 地址的权限" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "允许谁查看参与者的 XMPP 地址?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "密码" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "有审核" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "仅允许有发言权的参与者发送信息" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "仅成员" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "只有成员才能进入此群聊" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "此群聊不允许您发送消息。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "请求权限" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "发送文件" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "开始通话" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "音频通话" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "视频通话" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "搜索消息" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "对话详情" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "关闭对话" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "调试信息" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "正在呼叫…" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "正在响铃…" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "%s 结束了通话" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "%s 拒绝了通话" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "摄像头" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "未找到摄像头。" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "麦克风" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "未找到麦克风。" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "扬声器" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "未找到扬声器。" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "邀请加入通话" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "开始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "加入频道" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "下一步" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "加入" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "正在加入…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "需要密码才能进入群聊" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "禁止加入或创建群聊" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "群聊不存在" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "不允许创建群聊" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "仅成员的群聊" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "请选择其他昵称" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "群聊参与者太多" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "添加" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "关于 Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今天" #: main/src/ui/widgets/date_separator.vala:35 #, no-c-format msgid "%a, %b %d" msgstr "%_m月%_d日 %A" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 条搜索结果" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "在 %s 中" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "与 %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "视频通话来电" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "群聊视频通话来电" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "群聊来电" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "拒绝" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "接受" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "订阅请求" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "拒绝" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "%s 的邀请" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s 邀请您加入 %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "权限请求" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s 请求在 %s 的发言权" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "发送输入通知" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "发送已读回执" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "已启用" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "已禁用" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "默认:%s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "请求" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "请求发送消息的权限" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "欢迎使用 Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "登录或创建账号即可开始。" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "设置账号" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "管理账号" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "XMPP 地址" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "显示名" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "主题" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "编辑消息" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "您" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "添加回应" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "打开" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "另存为…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s,%s 和其他 %i 人正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s,%s 和 %s 正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s 和 %s 正在输入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s 正在输入…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "通话已开始" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "开始于%s前" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "您在另一台设备上接听了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "通话已结束" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "结束于%s" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "持续了%s" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "未接来电" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "您未接此通话" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "%s 未接此通话" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "通话已拒绝" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "您拒绝了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "%s 拒绝了此通话" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "通话失败" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "%i小时" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "%i分钟" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "几秒" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "已发送" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "已读" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "发送请求" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "您尚未接收对方的状态更新。" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "对方想将您添加到联系人列表" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "此对话不支持回应。" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "回复" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "此对话不支持回复。" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "正在下载 %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "正在下载 %s…" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "正在上传 %s… (%u%%)" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "%s 已提供:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "文件已提供:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "文件已提供" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "文件传输失败" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "文件" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "新消息" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "联系人详情" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "更新消息" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "登录" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "无法建立安全连接" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "返回" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "选择公共服务器" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "或指定服务器地址" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "改为登录" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "一切就绪!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "完成" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "发送输入通知(_T)" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "发送已读回执(_R)" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "通知(_N)" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "收到新消息时通知" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "将微笑符号转换为表情符号(_C)" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "本地别名" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "连接状态" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "移除账号" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "更改密码" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "更改" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "当前密码" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "新密码" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "确认密码" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "常规" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "没有打开的聊天" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "点击 + 开始聊天或加入频道" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "显示首选项" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "键盘快捷键" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "对话" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "导航" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "‌转至下个对话" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "转至上个对话" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "账号" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "昵称" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "别名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "添加联系人" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "现代 XMPP 聊天客户端" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino 是适用于桌面的现代开源聊天客户端,专注于提供简洁可靠的 Jabber/XMPP 体" "验,同时注重您的隐私。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "它支持 OMEMO 和 OpenPGP 端到端加密并允许配置隐私相关的功能,如已读回执和输入" "通知。" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino 从服务器获取历史记录并与其他设备同步消息。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "无活动搜索" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "输入以开始搜索" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "无匹配消息" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "检查拼写或尝试移除筛选条件" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "发送" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "首选项" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "键盘快捷键" #~ msgid "Remove" #~ msgstr "移除" #, c-format #~ msgid "Sign in to %s" #~ msgstr "登录 %s" #~ msgid "No accounts configured" #~ msgstr "未配置账号" #~ msgid "Add an account" #~ msgstr "添加账号" #~ msgid "Pick another server" #~ msgstr "选择其他服务器" #~ msgid "Local Settings" #~ msgstr "本地设置" #~ msgid "Notifications" #~ msgstr "通知" #~ msgid "Pin conversation" #~ msgstr "置顶对话" #~ msgid "Pins the conversation to the top of the conversation list" #~ msgstr "将此对话固定在对话列表顶部" #~ msgid "Only when mentioned" #~ msgstr "仅在提及时" #~ msgid "Permissions" #~ msgstr "权限" #~ msgid "Conference Details" #~ msgstr "群聊详情" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "已屏蔽双向交流和状态更新" #~ msgid "A password to restrict access to the room" #~ msgstr "一个限制访问群聊的密码" #~ msgid "Message history" #~ msgstr "消息历史记录" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "群聊储存的历史消息的最大数目" #~ msgid "Convert smileys to emojis" #~ msgstr "将笑脸转换成 Emoji" #~ msgid "Check spelling" #~ msgstr "检查拼写" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "点击此处以开始对话或加入频道。" #~ msgid "No active conversations" #~ msgstr "没有活动的会话" #~ msgid "Main window with conversations" #~ msgstr "带有对话的主窗口" #~ msgid "%s, %s and %i others" #~ msgstr "%s、%s 以及 %i 个其他人" #~ msgid "You can now start using %s" #~ msgstr "您现在可以开始使用%s" #~ msgid "Open Registration" #~ msgstr "开放注册" #~ msgid "Save" #~ msgstr "保存" #~ msgid "%s, %s and %s" #~ msgstr "%s、%s 和 %s" #~ msgid "%s and %s" #~ msgstr "%s 和 %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "正在输入…" #~ msgid "has stopped typing" #~ msgstr "已经停止输入" #~ msgid "%i search results" #~ msgstr "%i 搜索结果" #~ msgid "Discover real JIDs" #~ msgstr "发现真实 JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "谁可能发现真实 JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "如果有的话需要密码才能进入房间" #~ msgid "Failed connecting to %s" #~ msgstr "连接 %s 失败" #~ msgid "Join Conference" #~ msgstr "加入群聊" #~ msgid "Communicate happiness." #~ msgstr "沟通快乐。" #~ msgid "Quit" #~ msgstr "退出" #~ msgid "JID should be of the form “user@example.com”" #~ msgstr "JID 必须形如 “user@example.com”" #~ msgid "Copy Link Address" #~ msgstr "复制链接地址" #~ msgid "Copy" #~ msgstr "复制" #~ msgid "Select All" #~ msgstr "全选" #~ msgid "Search" #~ msgstr "搜索" #~ msgid "Send message marker" #~ msgstr "发送消息标记" #~ msgid "Start Chat" #~ msgstr "开始聊天" #~ msgid "Request presence updates" #~ msgstr "请求在线更新" #~ msgid "Join on startup" #~ msgstr "启动时加入" #~ msgid "Add Chat" #~ msgstr "添加聊天" dino-0.5.0/main/po/zh_TW.po0000664000000000000000000011475314776241610014125 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-12-10 19:29+0000\n" "Language-Team: none\n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: main/src/windows/preferences_window/account_preferences_subpage.vala:71 #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 #: main/src/ui/add_conversation/select_contact_dialog.vala:41 #: main/src/ui/add_conversation/add_conference_dialog.vala:42 #: main/src/ui/application.vala:292 #: main/src/ui/conversation_content_view/file_default_widget.vala:80 #: main/data/message_item_widget_edit_mode.ui:46 #: main/data/preferences_window/change_password_dialog.ui:14 #: main/data/add_conversation/add_groupchat_dialog.ui:12 #: main/data/add_conversation/add_contact_dialog.ui:13 msgid "Cancel" msgstr "取消" #: main/src/windows/preferences_window/account_preferences_subpage.vala:72 msgid "Connect" msgstr "連線" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 #: main/data/preferences_window/account_preferences_subpage.ui:127 msgid "Disable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:94 msgid "Enable account" msgstr "" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 msgid "Select avatar" msgstr "選取頭像" #: main/src/windows/preferences_window/account_preferences_subpage.vala:138 #: main/src/ui/conversation_view_controller.vala:216 msgid "Select" msgstr "選擇" #: main/src/windows/preferences_window/account_preferences_subpage.vala:145 msgid "Images" msgstr "影像" #: main/src/windows/preferences_window/account_preferences_subpage.vala:149 msgid "All files" msgstr "所有檔案" #: main/src/windows/preferences_window/account_preferences_subpage.vala:164 #, c-format msgid "Remove account %s?" msgstr "移除帳號 %s ?" #: main/src/windows/preferences_window/account_preferences_subpage.vala:191 #: main/src/ui/call_window/participant_widget.vala:128 msgid "Connecting…" msgstr "正在連線…" #: main/src/windows/preferences_window/account_preferences_subpage.vala:193 msgid "Connected" msgstr "已連線" #: main/src/windows/preferences_window/account_preferences_subpage.vala:195 msgid "Disconnected" msgstr "連線已斷開" #: main/src/windows/preferences_window/account_preferences_subpage.vala:206 #: main/src/ui/notifier_freedesktop.vala:192 msgid "Wrong password" msgstr "密碼錯誤" #: main/src/windows/preferences_window/account_preferences_subpage.vala:208 #: main/src/ui/notifier_freedesktop.vala:195 msgid "Invalid TLS certificate" msgstr "無效的 TLS 憑證" #: main/src/windows/preferences_window/account_preferences_subpage.vala:211 #: main/src/windows/preferences_window/account_preferences_subpage.vala:213 msgid "Error" msgstr "錯誤" #: main/src/windows/preferences_window/accounts_preferences_page.vala:15 #: main/src/windows/preferences_window/accounts_preferences_page.vala:27 #: main/data/preferences_window/preferences_window.ui:11 msgid "Accounts" msgstr "帳號" #: main/src/windows/preferences_window/accounts_preferences_page.vala:28 msgid "Disabled accounts" msgstr "" #: main/src/windows/preferences_window/accounts_preferences_page.vala:31 #: main/src/windows/preferences_window/add_account_dialog.vala:81 msgid "Add Account" msgstr "新增帳號" #: main/src/windows/preferences_window/accounts_preferences_page.vala:72 #: main/src/windows/preferences_window/encryption_preferences_page.vala:49 #: main/src/ui/main_window.vala:179 msgid "No active accounts" msgstr "没有啓用的帳號" #: main/src/windows/preferences_window/add_account_dialog.vala:126 msgid "Sign in" msgstr "登入" #: main/src/windows/preferences_window/add_account_dialog.vala:143 #, c-format msgid "The server could not prove that it is %s." msgstr "該伺服器無法證明其為 %s。" #: main/src/windows/preferences_window/add_account_dialog.vala:145 msgid "Its security certificate is not trusted by your operating system." msgstr "其安全憑證不被您的作業系統信任。" #: main/src/windows/preferences_window/add_account_dialog.vala:147 msgid "Its security certificate is issued to another domain." msgstr "其安全憑證已被簽發給另一域名。" #: main/src/windows/preferences_window/add_account_dialog.vala:149 msgid "Its security certificate will only become valid in the future." msgstr "其安全憑證在未來方會生效。" #: main/src/windows/preferences_window/add_account_dialog.vala:151 msgid "Its security certificate is expired." msgstr "其安全憑證已過時。" #: main/src/windows/preferences_window/add_account_dialog.vala:159 #: main/data/preferences_window/add_account_dialog.ui:111 msgid "Create account" msgstr "建立帳號" #: main/src/windows/preferences_window/add_account_dialog.vala:178 #, c-format msgid "You can now use the account %s." msgstr "您現在可以使用帳號 %s 了。" #: main/src/windows/preferences_window/add_account_dialog.vala:224 msgid "Wrong username or password" msgstr "使用者名稱或密碼錯誤" #: main/src/windows/preferences_window/add_account_dialog.vala:227 msgid "Something went wrong" msgstr "出現了錯誤" #: main/src/windows/preferences_window/add_account_dialog.vala:248 #: main/src/ui/add_conversation/conference_details_fragment.vala:183 #: main/src/ui/notifier_gnotifications.vala:104 #: main/src/ui/notifier_freedesktop.vala:188 #, c-format msgid "Could not connect to %s" msgstr "無法連線到 %s" #: main/src/windows/preferences_window/add_account_dialog.vala:255 #: main/src/windows/preferences_window/add_account_dialog.vala:265 #: main/src/windows/preferences_window/add_account_dialog.vala:275 #: main/src/windows/preferences_window/add_account_dialog.vala:359 #: main/src/ui/add_conversation/conference_details_fragment.vala:186 msgid "Invalid address" msgstr "無效的位址" #: main/src/windows/preferences_window/add_account_dialog.vala:293 msgid "No response from server" msgstr "伺服器没有回應" #: main/src/windows/preferences_window/add_account_dialog.vala:304 #, c-format msgid "Register on %s" msgstr "在 %s 註冊" #: main/src/windows/preferences_window/add_account_dialog.vala:307 msgid "The server requires to sign up through a website" msgstr "伺服器要求通過網頁進行註冊" #: main/src/windows/preferences_window/add_account_dialog.vala:309 msgid "Open website" msgstr "開啓網頁" #: main/src/windows/preferences_window/add_account_dialog.vala:321 #: main/data/preferences_window/add_account_dialog.ui:294 msgid "Register" msgstr "註冊" #: main/src/windows/preferences_window/add_account_dialog.vala:323 #, c-format msgid "Check %s for information on how to sign up" msgstr "查閱 %s 以取得關於如何註冊的資訊" #: main/src/windows/conversation_details.vala:72 msgid "Block user" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Block entire domain" msgstr "" #: main/src/windows/conversation_details.vala:72 msgid "Unblock" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pinned" msgstr "" #: main/src/windows/conversation_details.vala:90 msgid "Pin" msgstr "" #: main/src/windows/conversation_details.vala:101 msgid "Blocked" msgstr "" #: main/src/windows/conversation_details.vala:105 msgid "Domain blocked" msgstr "" #: main/src/windows/conversation_details.vala:109 msgid "Block" msgstr "封鎖" #: main/src/windows/conversation_details.vala:128 #: main/src/windows/conversation_details.vala:129 msgid "Mute" msgstr "" #: main/src/windows/conversation_details.vala:130 msgid "Notifications enabled" msgstr "" #: main/src/windows/conversation_details.vala:133 msgid "Notifications for mentions" msgstr "" #: main/src/windows/conversation_details.vala:136 #: main/src/windows/conversation_details.vala:137 msgid "Muted" msgstr "" #: main/src/windows/conversation_details.vala:138 msgid "Notifications disabled" msgstr "" #: main/src/windows/conversation_details.vala:193 #: main/data/conversation_details.ui:156 msgid "About" msgstr "" #: main/src/windows/conversation_details.vala:196 msgid "Settings" msgstr "設定" #: main/src/windows/conversation_details.vala:199 msgid "Room Configuration" msgstr "聊天室設定" #: main/src/windows/conversation_details.vala:211 #: main/data/preferences_window/preferences_window.ui:18 msgid "Encryption" msgstr "" #: main/src/windows/conversation_details.vala:221 #: main/src/ui/conversation_titlebar/occupants_entry.vala:14 msgid "Members" msgstr "會員" #: main/src/view_model/conversation_details.vala:129 #: main/src/ui/occupant_menu/list.vala:110 msgid "Owner" msgstr "主人" #: main/src/view_model/conversation_details.vala:130 #: main/src/ui/occupant_menu/list.vala:112 msgid "Admin" msgstr "管理員" #: main/src/view_model/conversation_details.vala:131 #: main/src/ui/occupant_menu/list.vala:114 msgid "Member" msgstr "會員" #: main/src/ui/file_send_overlay.vala:84 msgid "The file exceeds the server's maximum upload size." msgstr "檔案大小超過了伺服器的的上傳限制。" #: main/src/ui/conversation_content/message_item_widget.vala:124 #: main/src/ui/conversation_content_view/message_widget.vala:95 msgid "Message too long" msgstr "訊息太長" #: main/src/ui/conversation_content/message_item_widget.vala:150 #: main/src/ui/conversation_content_view/message_widget.vala:161 msgid "edited" msgstr "已編輯" #: main/src/ui/conversation_content/message_item_widget.vala:159 #: main/src/ui/conversation_content_view/message_widget.vala:170 msgid "pending…" msgstr "" #: main/src/ui/conversation_content/message_item_widget.vala:172 #: main/src/ui/conversation_content_view/message_widget.vala:185 msgid "delivery failed" msgstr "" #: main/src/ui/conversation_content/conversation_content_model.vala:85 #: main/src/ui/chat_input/encryption_button.vala:26 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:162 #: main/data/menu_encryption.ui:14 msgid "Unencrypted" msgstr "未加密" #: main/src/ui/conversation_content/conversation_content_model.vala:101 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:204 msgid "Unable to send message" msgstr "無法傳送訊息" #: main/src/ui/conversation_content/conversation_content_model.vala:122 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:241 #, no-c-format msgid "%x, %H∶%M" msgstr "%x,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:123 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:242 #, no-c-format msgid "%x, %l∶%M %p" msgstr "%x,%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:126 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:245 #, no-c-format msgid "%b %d, %H∶%M" msgstr "%b%d日,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:127 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:246 #, no-c-format msgid "%b %d, %l∶%M %p" msgstr "%b%d日,%p%l:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:130 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:249 #, no-c-format msgid "%a, %H∶%M" msgstr "%A,%H∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:131 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:250 #, no-c-format msgid "%a, %l∶%M %p" msgstr "%A,%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:134 #: main/src/ui/conversation_selector/conversation_selector_row.vala:351 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:253 #, no-c-format msgid "%H∶%M" msgstr "%H:%M" #: main/src/ui/conversation_content/conversation_content_model.vala:135 #: main/src/ui/conversation_selector/conversation_selector_row.vala:352 #: main/src/ui/conversation_content_view/call_widget.vala:175 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:254 #, no-c-format msgid "%l∶%M %p" msgstr "%p%l∶%M" #: main/src/ui/conversation_content/conversation_content_model.vala:139 #: main/src/ui/conversation_selector/conversation_selector_row.vala:355 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:258 #, c-format msgid "%i min ago" msgid_plural "%i mins ago" msgstr[0] "%i 分鐘前" #: main/src/ui/conversation_content/conversation_content_model.vala:141 #: main/src/ui/conversation_selector/conversation_selector_row.vala:357 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:260 msgid "Just now" msgstr "剛才" #: main/src/ui/conversation_selector/conversation_selector_row.vala:165 #: main/src/ui/conversation_selector/conversation_selector_row.vala:190 #: main/src/ui/conversation_selector/conversation_selector_row.vala:205 #: main/src/ui/util/helper.vala:69 main/src/ui/util/helper.vala:73 #: main/src/ui/util/helper.vala:81 msgid "Me" msgstr "我" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "Image sent" msgstr "影像已傳送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:196 #: main/src/ui/notifier_gnotifications.vala:36 #: main/src/ui/notifier_freedesktop.vala:71 msgid "File sent" msgstr "檔案已傳送" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "Image received" msgstr "影像已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:198 #: main/src/ui/notifier_gnotifications.vala:38 #: main/src/ui/notifier_freedesktop.vala:73 msgid "File received" msgstr "檔案已接收" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Outgoing call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:207 #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 #: main/src/ui/conversation_content_view/call_widget.vala:168 msgid "Incoming call" msgstr "" #: main/src/ui/conversation_selector/conversation_selector_row.vala:344 #: main/src/ui/widgets/date_separator.vala:37 #, no-c-format msgid "%b %d" msgstr "%b%d日" #: main/src/ui/conversation_selector/conversation_selector_row.vala:348 #: main/src/ui/widgets/date_separator.vala:28 msgid "Yesterday" msgstr "昨天" #: main/src/ui/conversation_list_titlebar.vala:16 #: main/src/ui/add_conversation/select_contact_dialog.vala:89 #: main/data/gtk/help-overlay.ui:14 main/data/menu_add.ui:7 msgid "Start Conversation" msgstr "開始對話" #: main/src/ui/occupant_menu/list.vala:116 msgid "User" msgstr "使用者" #: main/src/ui/occupant_menu/view.vala:27 #: main/src/ui/occupant_menu/view.vala:145 #: main/src/ui/call_window/call_window_controller.vala:151 msgid "Invite" msgstr "邀請" #: main/src/ui/occupant_menu/view.vala:82 msgid "Start private conversation" msgstr "開始私人對話" #: main/src/ui/occupant_menu/view.vala:90 msgid "Kick" msgstr "踢走" #: main/src/ui/occupant_menu/view.vala:96 msgid "Grant write permission" msgstr "同意寫入權限" #: main/src/ui/occupant_menu/view.vala:102 msgid "Revoke write permission" msgstr "撤回寫入權限" #: main/src/ui/occupant_menu/view.vala:144 msgid "Invite to Conference" msgstr "邀請加入聊天室" #: main/src/ui/conversation_view_controller.vala:216 msgid "Select file" msgstr "選取檔案" #: main/src/ui/util/helper.vala:65 #, c-format msgid "%s from %s" msgstr "%s 來自 %s" #: main/src/ui/util/data_forms.vala:31 msgid "Name of the room" msgstr "聊天室名稱" #: main/src/ui/util/data_forms.vala:34 msgid "Description of the room" msgstr "聊天室介紹" #: main/src/ui/util/data_forms.vala:37 msgid "Persistent" msgstr "保留" #: main/src/ui/util/data_forms.vala:38 msgid "The room will persist after the last occupant leaves" msgstr "聊天室在最後一名成員離開後仍然保留" #: main/src/ui/util/data_forms.vala:41 msgid "Publicly searchable" msgstr "可被公開搜尋" #: main/src/ui/util/data_forms.vala:44 msgid "Occupants may change the subject" msgstr "成員可以變更主題" #: main/src/ui/util/data_forms.vala:47 msgid "Permission to view JIDs" msgstr "JID 檢視權限" #: main/src/ui/util/data_forms.vala:48 msgid "Who is allowed to view the occupants' JIDs?" msgstr "誰可以檢視成員的 JID?" #: main/src/ui/util/data_forms.vala:51 #: main/data/preferences_window/account_preferences_subpage.ui:84 #: main/data/add_conversation/conference_details_fragment.ui:162 #: main/data/add_conversation/add_groupchat_dialog.ui:122 msgid "Password" msgstr "密碼" #: main/src/ui/util/data_forms.vala:55 msgid "Moderated" msgstr "發言" #: main/src/ui/util/data_forms.vala:56 msgid "Only occupants with voice may send messages" msgstr "僅限有發言權的成員可以傳送訊息" #: main/src/ui/util/data_forms.vala:59 msgid "Members only" msgstr "僅限會員" #: main/src/ui/util/data_forms.vala:60 msgid "Only members may enter the room" msgstr "只有會員才可以加入聊天室" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "This conference does not allow you to send messages." msgstr "此聊天室不允許您傳送訊息。" #: main/src/ui/chat_input/chat_input_controller.vala:217 msgid "Request permission" msgstr "請求權限" #: main/src/ui/chat_input/view.vala:46 main/data/gtk/help-overlay.ui:49 #: main/data/file_send_overlay.ui:24 msgid "Send a file" msgstr "傳送檔案" #: main/src/ui/conversation_titlebar/call_entry.vala:13 msgid "Start call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:24 msgid "Audio call" msgstr "" #: main/src/ui/conversation_titlebar/call_entry.vala:25 msgid "Video call" msgstr "" #: main/src/ui/conversation_titlebar/search_entry.vala:12 #: main/data/gtk/help-overlay.ui:43 msgid "Search messages" msgstr "搜尋訊息" #: main/src/ui/conversation_titlebar/menu_entry.vala:20 msgid "Conversation Details" msgstr "" #: main/src/ui/conversation_titlebar/menu_entry.vala:21 msgid "Close Conversation" msgstr "" #: main/src/ui/call_window/participant_widget.vala:115 msgid "Debug information" msgstr "" #: main/src/ui/call_window/participant_widget.vala:124 #: main/src/ui/conversation_content_view/call_widget.vala:152 msgid "Calling…" msgstr "" #: main/src/ui/call_window/participant_widget.vala:126 msgid "Ringing…" msgstr "" #: main/src/ui/call_window/call_window.vala:207 #, c-format msgid "%s ended the call" msgstr "" #: main/src/ui/call_window/call_window.vala:209 #, c-format msgid "%s declined the call" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:25 msgid "Cameras" msgstr "" #: main/src/ui/call_window/video_settings_popover.vala:28 msgid "No camera found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:29 msgid "Microphones" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:32 msgid "No microphone found." msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:85 msgid "Speakers" msgstr "" #: main/src/ui/call_window/audio_settings_popover.vala:88 msgid "No speaker found." msgstr "" #: main/src/ui/call_window/call_window_controller.vala:150 msgid "Invite to Call" msgstr "" #: main/src/ui/add_conversation/select_contact_dialog.vala:90 msgid "Start" msgstr "開始" #: main/src/ui/add_conversation/add_conference_dialog.vala:25 #: main/src/ui/application.vala:292 main/data/gtk/help-overlay.ui:20 #: main/data/menu_add.ui:11 msgid "Join Channel" msgstr "加入聊天室" #: main/src/ui/add_conversation/add_conference_dialog.vala:45 #: main/data/preferences_window/add_account_dialog.ui:246 msgid "Next" msgstr "下一個" #: main/src/ui/add_conversation/add_conference_dialog.vala:60 #: main/src/ui/add_conversation/conference_details_fragment.vala:151 #: main/src/ui/application.vala:292 main/data/join_room_dialog2.ui:190 msgid "Join" msgstr "加入" #: main/src/ui/add_conversation/conference_details_fragment.vala:143 msgid "Joining…" msgstr "正在加入…" #: main/src/ui/add_conversation/conference_details_fragment.vala:164 msgid "Password required to enter room" msgstr "需要密碼才能加入聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:169 msgid "Banned from joining or creating conference" msgstr "被禁止加入或建立聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:171 msgid "Room does not exist" msgstr "聊天室不存在" #: main/src/ui/add_conversation/conference_details_fragment.vala:173 msgid "Not allowed to create room" msgstr "不允許建立聊天室" #: main/src/ui/add_conversation/conference_details_fragment.vala:175 msgid "Members-only room" msgstr "聊天室不對外開放" #: main/src/ui/add_conversation/conference_details_fragment.vala:178 msgid "Choose a different nick" msgstr "選取一個不同的暱稱" #: main/src/ui/add_conversation/conference_details_fragment.vala:180 msgid "Too many occupants in room" msgstr "聊天室人數過多" #: main/src/ui/add_conversation/add_groupchat_dialog.vala:26 #: main/data/add_conversation/add_contact_dialog.ui:18 msgid "Add" msgstr "新增" #: main/src/ui/application.vala:283 main/data/menu_app.ui:15 msgid "About Dino" msgstr "關於 Dino" #: main/src/ui/widgets/date_separator.vala:22 msgid "Today" msgstr "今天" #: main/src/ui/widgets/date_separator.vala:35 #, fuzzy, no-c-format msgid "%a, %b %d" msgstr "%b%d日,%A" #: main/src/ui/global_search.vala:181 #, c-format msgid "%i search result" msgid_plural "%i search results" msgstr[0] "%i 個搜尋結果" #: main/src/ui/global_search.vala:208 #, c-format msgid "In %s" msgstr "在 %s" #: main/src/ui/global_search.vala:208 #, c-format msgid "With %s" msgstr "和 %s" #: main/src/ui/notifier_gnotifications.vala:70 #: main/src/ui/notifier_freedesktop.vala:114 #: main/src/ui/conversation_content_view/call_widget.vala:137 msgid "Incoming video call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming video group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:72 #: main/src/ui/notifier_freedesktop.vala:116 #: main/src/ui/conversation_content_view/call_widget.vala:139 msgid "Incoming group call" msgstr "" #: main/src/ui/notifier_gnotifications.vala:80 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:230 main/data/call_widget.ui:73 msgid "Reject" msgstr "" #: main/src/ui/notifier_gnotifications.vala:81 #: main/src/ui/notifier_gnotifications.vala:97 #: main/src/ui/notifier_gnotifications.vala:134 #: main/src/ui/notifier_gnotifications.vala:150 #: main/src/ui/notifier_freedesktop.vala:125 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:230 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:67 #: main/data/call_widget.ui:81 msgid "Accept" msgstr "同意" #: main/src/ui/notifier_gnotifications.vala:91 #: main/src/ui/notifier_freedesktop.vala:157 msgid "Subscription request" msgstr "線上狀態訂閱請求" #: main/src/ui/notifier_gnotifications.vala:98 #: main/src/ui/notifier_gnotifications.vala:133 #: main/src/ui/notifier_gnotifications.vala:149 #: main/src/ui/notifier_freedesktop.vala:164 #: main/src/ui/notifier_freedesktop.vala:264 #: main/src/ui/conversation_content_view/subscription_notification.vala:68 msgid "Deny" msgstr "拒絕" #: main/src/ui/notifier_gnotifications.vala:123 #: main/src/ui/notifier_freedesktop.vala:220 #, c-format msgid "Invitation to %s" msgstr "邀請加入 %s" #: main/src/ui/notifier_gnotifications.vala:124 #: main/src/ui/notifier_freedesktop.vala:221 #, c-format msgid "%s invited you to %s" msgstr "%s 邀請您加入 %s" #: main/src/ui/notifier_gnotifications.vala:141 #: main/src/ui/notifier_freedesktop.vala:254 msgid "Permission request" msgstr "權限請求" #: main/src/ui/notifier_gnotifications.vala:142 #: main/src/ui/notifier_freedesktop.vala:255 #, c-format msgid "%s requests the permission to write in %s" msgstr "%s 請求權限以寫入 %s" #: main/src/ui/contact_details/settings_provider.vala:29 msgid "Send typing notifications" msgstr "傳送「正在輸入」提示" #: main/src/ui/contact_details/settings_provider.vala:34 msgid "Send read receipts" msgstr "傳送已讀回條" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:69 msgid "On" msgstr "啓用" #: main/src/ui/contact_details/settings_provider.vala:67 #: main/src/ui/contact_details/settings_provider.vala:70 msgid "Off" msgstr "停用" #: main/src/ui/contact_details/settings_provider.vala:68 #, c-format msgid "Default: %s" msgstr "預設:%s" #: main/src/ui/contact_details/permissions_provider.vala:24 msgid "Request" msgstr "請求" #: main/src/ui/contact_details/permissions_provider.vala:26 msgid "Request permission to send messages" msgstr "請求權限以傳送訊息" #: main/src/ui/main_window.vala:170 msgid "Welcome to Dino!" msgstr "歡迎來到 Dino!" #: main/src/ui/main_window.vala:171 msgid "Sign in or create an account to get started." msgstr "開始前請先登入或建立一個帳號。" #: main/src/ui/main_window.vala:172 msgid "Set up account" msgstr "設定帳號" #: main/src/ui/main_window.vala:180 msgid "Manage accounts" msgstr "管理帳號" #: main/src/ui/conversation_details.vala:146 #: main/data/preferences_window/account_preferences_subpage.ui:71 msgid "XMPP Address" msgstr "" #: main/src/ui/conversation_details.vala:151 msgid "Display name" msgstr "" #: main/src/ui/conversation_details.vala:165 msgid "Topic" msgstr "" #: main/src/ui/conversation_content_view/message_widget.vala:233 msgid "Edit message" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:102 msgid "You" msgstr "" #: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/item_actions.vala:9 msgid "Add reaction" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:42 #: main/src/ui/conversation_content_view/file_default_widget.vala:57 msgid "Open" msgstr "" #: main/src/ui/conversation_content_view/file_image_widget.vala:43 #: main/src/ui/conversation_content_view/file_widget.vala:167 #: main/src/ui/conversation_content_view/file_default_widget.vala:58 msgid "Save as…" msgstr "" #: main/src/ui/conversation_content_view/chat_state_populator.vala:108 #, c-format msgid "%s, %s and %i others are typing…" msgstr "%s, %s 和其他 %i 個人正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:115 #, c-format msgid "%s, %s and %s are typing…" msgstr "%s, %s 和 %s 正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:121 #, c-format msgid "%s and %s are typing…" msgstr "%s 和 %s 正在輸入…" #: main/src/ui/conversation_content_view/chat_state_populator.vala:126 #, c-format msgid "%s is typing…" msgstr "%s 正在輸入…" #: main/src/ui/conversation_content_view/call_widget.vala:159 msgid "Call started" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:161 #, c-format msgid "Started %s ago" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:169 msgid "You handled this call on another device" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:174 msgid "Call ended" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:177 #, c-format msgid "Ended at %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:179 #, c-format msgid "Lasted %s" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:183 msgid "Call missed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:185 msgid "You missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:188 #, c-format msgid "%s missed this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:193 msgid "Call declined" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:195 msgid "You declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:198 #, c-format msgid "%s declined this call" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:203 msgid "Call failed" msgstr "" #: main/src/ui/conversation_content_view/call_widget.vala:227 #, c-format msgid "%i hour" msgid_plural "%i hours" msgstr[0] "" #: main/src/ui/conversation_content_view/call_widget.vala:234 #, c-format msgid "%i minute" msgid_plural "%i minutes" msgstr[0] "" #: main/src/ui/conversation_content_view/call_widget.vala:241 msgid "a few seconds" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:194 msgid "Delivered" msgstr "" #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:198 msgid "Read" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:49 msgid "Send request" msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:60 msgid "You do not receive status updates from this contact yet." msgstr "" #: main/src/ui/conversation_content_view/subscription_notification.vala:80 msgid "This contact would like to add you to their contact list" msgstr "此聯絡人想把您新增到他們的聯絡人清單" #: main/src/ui/conversation_content_view/item_actions.vala:21 msgid "This conversation does not support reactions." msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:34 msgid "Reply" msgstr "" #: main/src/ui/conversation_content_view/item_actions.vala:44 msgid "This conversation does not support replies." msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:67 #, c-format msgid "Downloading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:69 #, c-format msgid "Downloading %s…" msgstr "正在下載 %s …" #: main/src/ui/conversation_content_view/file_default_widget.vala:73 #, c-format msgid "Uploading %s… (%u%%)" msgstr "" #: main/src/ui/conversation_content_view/file_default_widget.vala:87 #, c-format msgid "%s offered: %s" msgstr "分享了 %s:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:89 #, c-format msgid "File offered: %s" msgstr "分享了檔案:%s" #: main/src/ui/conversation_content_view/file_default_widget.vala:91 msgid "File offered" msgstr "分享的檔案" #: main/src/ui/conversation_content_view/file_default_widget.vala:97 msgid "File transfer failed" msgstr "檔案傳送失敗" #: main/src/ui/conversation_content_view/quote_widget.vala:34 msgid "File" msgstr "" #: main/src/ui/conversation_content_view/unread_indicator_populator.vala:76 msgid "New" msgstr "" #: main/data/menu_conversation.ui:7 msgid "Contact Details" msgstr "聯絡人詳細資訊" #: main/data/message_item_widget_edit_mode.ui:51 msgid "Update message" msgstr "更新訊息" #: main/data/preferences_window/add_account_dialog.ui:92 msgid "Login" msgstr "" #: main/data/preferences_window/add_account_dialog.ui:147 msgid "Could not establish a secure connection" msgstr "無法建立安全連線" #: main/data/preferences_window/add_account_dialog.ui:165 msgid "Back" msgstr "返回" #: main/data/preferences_window/add_account_dialog.ui:183 msgid "Choose a public server" msgstr "選取一個公眾伺服器" #: main/data/preferences_window/add_account_dialog.ui:207 msgid "Or specify a server address" msgstr "或指定一個伺服器位址" #: main/data/preferences_window/add_account_dialog.ui:225 msgid "Sign in instead" msgstr "登入" #: main/data/preferences_window/add_account_dialog.ui:330 msgid "All set up!" msgstr "全部設定好了!" #: main/data/preferences_window/add_account_dialog.ui:359 msgid "Finish" msgstr "完成" #: main/data/preferences_window/general_preferences_page.ui:9 msgid "Send _Typing Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:22 msgid "Send _Read Receipts" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:38 msgid "_Notifications" msgstr "" #: main/data/preferences_window/general_preferences_page.ui:39 msgid "Notify when a new message arrives" msgstr "有新訊息時提醒" #: main/data/preferences_window/general_preferences_page.ui:55 msgid "_Convert Smileys to Emoji" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:79 msgid "Local alias" msgstr "本地別名" #: main/data/preferences_window/account_preferences_subpage.ui:106 msgid "Connection status" msgstr "" #: main/data/preferences_window/account_preferences_subpage.ui:136 msgid "Remove account" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:5 msgid "Change password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:30 msgid "Change" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:57 msgid "Current password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:62 msgid "New password" msgstr "" #: main/data/preferences_window/change_password_dialog.ui:67 msgid "Confirm password" msgstr "" #: main/data/preferences_window/preferences_window.ui:25 #: main/data/gtk/help-overlay.ui:10 msgid "General" msgstr "一般" #: main/data/unified_main_content.ui:46 msgid "You have no open chats" msgstr "您没有開啓的對話" #: main/data/unified_main_content.ui:47 msgid "Click + to start a chat or join a channel" msgstr "" #: main/data/gtk/help-overlay.ui:26 msgid "Show preferences" msgstr "" #: main/data/gtk/help-overlay.ui:32 msgid "Keyboard shortcuts" msgstr "" #: main/data/gtk/help-overlay.ui:39 msgid "Conversation" msgstr "對話" #: main/data/gtk/help-overlay.ui:56 msgid "Navigation" msgstr "導航" #: main/data/gtk/help-overlay.ui:60 msgid "Jump to next conversation" msgstr "跳到下一個對話" #: main/data/gtk/help-overlay.ui:66 msgid "Jump to previous conversation" msgstr "跳到上一個對話" #: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_contact_dialog.ui:40 msgid "Account" msgstr "帳號" #: main/data/add_conversation/conference_details_fragment.ui:114 #: main/data/add_conversation/add_groupchat_dialog.ui:101 msgid "Nick" msgstr "暱稱" #: main/data/add_conversation/add_groupchat_dialog.ui:144 #: main/data/add_conversation/add_contact_dialog.ui:81 msgid "Alias" msgstr "別名" #: main/data/add_conversation/add_contact_dialog.ui:5 msgid "Add Contact" msgstr "新增聯絡人" #: main/data/im.dino.Dino.appdata.xml.in:6 msgid "Modern XMPP chat client" msgstr "現代化的 XMPP 用戶端聊天軟件" #: main/data/im.dino.Dino.appdata.xml.in:10 msgid "" "Dino is a modern open-source chat client for the desktop. It focuses on " "providing a clean and reliable Jabber/XMPP experience while having your " "privacy in mind." msgstr "" "Dino 是一個爲桌面打造的現代化開放原始碼用戶端聊天軟件。它致力於提供一份簡洁而" "可靠的 Jabber/XMPP 體驗,同時亦尊重您的私隱。" #: main/data/im.dino.Dino.appdata.xml.in:11 msgid "" "It supports end-to-end encryption with OMEMO and OpenPGP and allows " "configuring privacy-related features such as read receipts and typing " "notifications." msgstr "" "它支持 OMEMO 和 OpenPGP 兩種端到端加密方式,並且容許設定私隱相關的特性譬如已" "讀回條和正在輸入提示。" #: main/data/im.dino.Dino.appdata.xml.in:12 msgid "" "Dino fetches history from the server and synchronizes messages with other " "devices." msgstr "Dino 從伺服器取得訊息並與其他裝置同步。" #: main/data/global_search.ui:27 msgid "No active search" msgstr "目前没有搜尋" #: main/data/global_search.ui:28 msgid "Type to start a search" msgstr "輸入關鍵字進行搜尋" #: main/data/global_search.ui:42 msgid "No matching messages" msgstr "找不到符合條件的訊息" #: main/data/global_search.ui:43 msgid "Check the spelling or try to remove filters" msgstr "檢查拼字或嘗試移除篩選器" #: main/data/file_send_overlay.ui:61 msgid "Send" msgstr "傳送" #: main/data/menu_app.ui:7 msgid "Preferences" msgstr "" #: main/data/menu_app.ui:11 msgid "Keyboard Shortcuts" msgstr "鍵盤快捷鍵" #~ msgid "Remove" #~ msgstr "移除" #, c-format #~ msgid "Sign in to %s" #~ msgstr "登入到 %s" #~ msgid "No accounts configured" #~ msgstr "未有設定好的帳號" #~ msgid "Add an account" #~ msgstr "新增帳號" #~ msgid "Pick another server" #~ msgstr "選取另一臺伺服器" #~ msgid "Local Settings" #~ msgstr "本地設定" #~ msgid "Notifications" #~ msgstr "通知" #~ msgid "Only when mentioned" #~ msgstr "僅限被提及時" #~ msgid "Permissions" #~ msgstr "權限" #~ msgid "Conference Details" #~ msgstr "聊天室詳細資訊" #~ msgid "Communication and status updates in either direction are blocked" #~ msgstr "封鎖雙方向的通訊以及狀態更新" #~ msgid "A password to restrict access to the room" #~ msgstr "加入聊天室需要的密碼" #~ msgid "Message history" #~ msgstr "聊天紀錄" #~ msgid "Maximum amount of backlog issued by the room" #~ msgstr "聊天室儲存的最大聊天紀錄數目" #~ msgid "Convert smileys to emojis" #~ msgstr "將文字表情轉換爲表情圖標" #~ msgid "Click here to start a conversation or join a channel." #~ msgstr "點擊此處開始對話或加入聊天室。" #~ msgid "No active conversations" #~ msgstr "没有進行中的對話" #~ msgid "Main window with conversations" #~ msgstr "含有對話的主視窗" #~ msgid "%s, %s and %i others" #~ msgstr "%s、%s 和另外 %i 人" #~ msgid "You can now start using %s" #~ msgstr "您現在可以開始使用 %s" #~ msgid "Open Registration" #~ msgstr "開啟網頁" #~ msgid "Save" #~ msgstr "儲存" #~ msgid "%s, %s and %s" #~ msgstr "%s、%s 和 %s" #~ msgid "%s and %s" #~ msgstr "%s 和 %s" #~ msgid "is typing…" #~ msgid_plural "are typing…" #~ msgstr[0] "正在輸入…" #~ msgid "has stopped typing" #~ msgstr "已經停止輸入" #~ msgid "%i search results" #~ msgstr "%i 個搜尋結果" #~ msgid "Discover real JIDs" #~ msgstr "檢閱真正的 JID" #~ msgid "Who may discover real JIDs?" #~ msgstr "誰可以檢閱真正的 JID?" #~ msgid "Password required for room entry, if any" #~ msgstr "加入聊天室需要的密碼,可不填" dino-0.5.0/main/src/0000775000000000000000000000000014776241610012670 5ustar rootrootdino-0.5.0/main/src/main.vala0000664000000000000000000000147414776241610014467 0ustar rootrootusing Dino.Entities; using Dino.Ui; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino { void main(string[] args) { try{ string? exec_path = args.length > 0 ? args[0] : null; SearchPathGenerator search_path_generator = new SearchPathGenerator(exec_path); Intl.textdomain(GETTEXT_PACKAGE); internationalize(GETTEXT_PACKAGE, search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR)); Gtk.init(); Dino.Ui.Application app = new Dino.Ui.Application() { search_path_generator=search_path_generator }; Plugins.Loader loader = new Plugins.Loader(app); loader.load_all(); app.run(args); loader.shutdown(); } catch (Error e) { warning(@"Fatal error: $(e.message)"); } } } dino-0.5.0/main/src/ui/0000775000000000000000000000000014776241610013305 5ustar rootrootdino-0.5.0/main/src/ui/add_conversation/0000775000000000000000000000000014776241610016627 5ustar rootrootdino-0.5.0/main/src/ui/add_conversation/add_conference_dialog.vala0000664000000000000000000001414714776241610023741 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Xep; namespace Dino.Ui { public class AddConferenceDialog : Gtk.Dialog { private Stack stack = new Stack(); private Button cancel_button = new Button(); private Button ok_button; private SelectJidFragment select_fragment; private ConferenceDetailsFragment details_fragment; private ConferenceList conference_list; private ListBox conference_list_box; private StreamInteractor stream_interactor; public AddConferenceDialog(StreamInteractor stream_interactor) { Object(use_header_bar : 1); this.title = _("Join Channel"); this.modal = true; this.default_width = 460; this.default_height = 550; this.stream_interactor = stream_interactor; stack.visible = true; stack.vhomogeneous = false; get_content_area().append(stack); setup_headerbar(); setup_jid_add_view(); setup_conference_details_view(); show_jid_add_view(); } private void show_jid_add_view() { cancel_button.set_label(_("Cancel")); cancel_button.clicked.disconnect(show_jid_add_view); cancel_button.clicked.connect(on_cancel); ok_button.label = _("Next"); ok_button.sensitive = select_fragment.done; ok_button.clicked.connect(on_next_button_clicked); details_fragment.fragment_active = false; details_fragment.notify["done"].disconnect(set_ok_sensitive_from_details); select_fragment.notify["done"].connect(set_ok_sensitive_from_select); stack.transition_type = StackTransitionType.SLIDE_RIGHT; stack.set_visible_child_name("select"); } private void show_conference_details_view() { cancel_button.set_icon_name("go-previous-symbolic"); cancel_button.clicked.disconnect(on_cancel); cancel_button.clicked.connect(show_jid_add_view); ok_button.label = _("Join"); ok_button.sensitive = details_fragment.done; ok_button.clicked.disconnect(on_next_button_clicked); details_fragment.fragment_active = true; select_fragment.notify["done"].disconnect(set_ok_sensitive_from_select); details_fragment.notify["done"].connect(set_ok_sensitive_from_details); stack.transition_type = StackTransitionType.SLIDE_LEFT; stack.set_visible_child_name("details"); animate_window_resize(details_fragment); } private void setup_headerbar() { ok_button = new Button() { can_focus=true }; ok_button.add_css_class("suggested-action"); HeaderBar header_bar = get_header_bar() as HeaderBar; header_bar.show_title_buttons = false; header_bar.pack_start(cancel_button); header_bar.pack_end(ok_button); } private void setup_jid_add_view() { conference_list = new ConferenceList(stream_interactor); conference_list_box = conference_list.get_list_box(); conference_list_box.row_activated.connect(() => { ok_button.clicked(); }); select_fragment = new SelectJidFragment(stream_interactor, conference_list_box, stream_interactor.get_accounts()); select_fragment.add_jid.connect((row) => { AddGroupchatDialog dialog = new AddGroupchatDialog(stream_interactor); dialog.set_transient_for(this); dialog.present(); }); select_fragment.remove_jid.connect((row) => { ConferenceListRow conference_row = row as ConferenceListRow; if (conference_row == null) return; stream_interactor.get_module(MucManager.IDENTITY).remove_bookmark(conference_row.account, conference_row.bookmark); }); Box wrap_box = new Box(Orientation.VERTICAL, 0); wrap_box.append(select_fragment); stack.add_named(wrap_box, "select"); } private void setup_conference_details_view() { details_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; details_fragment.joined.connect(() => this.close()); Box wrap_box = new Box(Orientation.VERTICAL, 0); wrap_box.append(details_fragment); stack.add_named(wrap_box, "details"); } private void set_ok_sensitive_from_select() { ok_button.sensitive = select_fragment.done; } private void set_ok_sensitive_from_details() { ok_button.sensitive = details_fragment.done; } private void on_next_button_clicked() { details_fragment.clear(); ListRow? row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row().get_child() as ListRow : null; ConferenceListRow? conference_row = conference_list_box.get_selected_row() != null ? conference_list_box.get_selected_row().get_child() as ConferenceListRow : null; if (conference_row != null) { details_fragment.account = conference_row.account; details_fragment.jid = conference_row.bookmark.jid.to_string(); details_fragment.nick = conference_row.bookmark.nick; if (conference_row.bookmark.password != null) details_fragment.password = conference_row.bookmark.password; ok_button.grab_focus(); } else if (row != null) { details_fragment.account = row.account; details_fragment.jid = row.jid.to_string(); } show_conference_details_view(); } private void on_cancel() { close(); } private void animate_window_resize(Widget widget) { int curr_height = get_size(Orientation.VERTICAL); var natural_size = Requisition(); widget.get_preferred_size(null, out natural_size); int difference = natural_size.height - curr_height; Timer timer = new Timer(); Timeout.add((int) (stack.transition_duration / 30), () => { ulong microsec; timer.elapsed(out microsec); ulong millisec = microsec / 1000; double partial = double.min(1, (double) millisec / stack.transition_duration); default_height = (int) (curr_height + difference * partial); return millisec < stack.transition_duration; }); } } } dino-0.5.0/main/src/ui/add_conversation/add_contact_dialog.vala0000664000000000000000000000407214776241610023261 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/add_contact_dialog.ui")] protected class AddContactDialog : Gtk.Dialog { public Account? account { get { return account_combobox.selected; } set { account_combobox.selected = value; } } public string jid { get { return jid_entry.text; } set { jid_entry.text = value; } } [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Entry alias_entry; private StreamInteractor stream_interactor; public AddContactDialog(StreamInteractor stream_interactor) { Object(use_header_bar : 1); this.stream_interactor = stream_interactor; account_combobox.initialize(stream_interactor); cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(on_ok_button_clicked); jid_entry.changed.connect(on_jid_entry_changed); } private void on_ok_button_clicked() { string? alias = alias_entry.text == "" ? null : alias_entry.text; try { Jid jid = new Jid(jid_entry.text); stream_interactor.get_module(RosterManager.IDENTITY).add_jid(account, jid, alias); stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(account, jid); close(); } catch (InvalidJidError e) { warning("Tried to add contact with invalid Jid: %s", e.message); } } private void on_jid_entry_changed() { try { Jid parsed_jid = new Jid(jid_entry.text); ok_button. sensitive = parsed_jid != null && parsed_jid.resourcepart == null && stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(account, parsed_jid) == null; } catch (InvalidJidError e) { ok_button.sensitive = false; } } } } dino-0.5.0/main/src/ui/add_conversation/add_groupchat_dialog.vala0000664000000000000000000000503114776241610023616 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/add_groupchat_dialog.ui")] protected class AddGroupchatDialog : Gtk.Dialog { [GtkChild] private unowned Stack accounts_stack; [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Entry alias_entry; [GtkChild] private unowned Entry nick_entry; private StreamInteractor stream_interactor; private bool alias_entry_changed = false; public AddGroupchatDialog(StreamInteractor stream_interactor) { Object(use_header_bar : 1); this.stream_interactor = stream_interactor; ok_button.label = _("Add"); ok_button.add_css_class("suggested-action"); // TODO why doesn't it work in XML accounts_stack.set_visible_child_name("combobox"); account_combobox.initialize(stream_interactor); cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(on_ok_button_clicked); jid_entry.changed.connect(on_jid_key_release); nick_entry.changed.connect(check_ok); } private void on_jid_key_release() { check_ok(); if (!alias_entry_changed) { try { Jid parsed_jid = new Jid(jid_entry.text); alias_entry.text = parsed_jid != null && parsed_jid.localpart != null ? parsed_jid.localpart : jid_entry.text; } catch (InvalidJidError e) { alias_entry.text = jid_entry.text; } } } private void check_ok() { try { Jid parsed_jid = new Jid(jid_entry.text); ok_button.sensitive = parsed_jid != null && parsed_jid.localpart != null && parsed_jid.resourcepart == null; } catch (InvalidJidError e) { ok_button.sensitive = false; } } private void on_ok_button_clicked() { try { Conference conference = new Conference(); conference.jid = new Jid(jid_entry.text); conference.nick = nick_entry.text != "" ? nick_entry.text : null; conference.name = alias_entry.text; stream_interactor.get_module(MucManager.IDENTITY).add_bookmark(account_combobox.selected, conference); close(); } catch (InvalidJidError e) { warning("Ignoring invalid conference Jid: %s", e.message); } } } } dino-0.5.0/main/src/ui/add_conversation/conference_details_fragment.vala0000664000000000000000000002132214776241610025173 0ustar rootrootusing Gdk; using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Xep; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/conference_details_fragment.ui")] protected class ConferenceDetailsFragment : Box { public signal void joined(); public bool done { get; private set; } public Account account { owned get { return account_combobox.selected; } set { accounts_label.label = value.bare_jid.to_string(); account_combobox.selected = value; if (nick == null && value.alias != null) { nick = value.alias; } accounts_stack.set_visible_child_name("label"); } } public string jid { get { return jid_entry.text; } set { jid_label.label = value; jid_entry.text = value; jid_stack.set_visible_child_name("label"); check_if_done(); } } public string? nick { get { return nick_entry.text != "" ? nick_entry.text : null; } set { nick_label.label = value ?? ""; nick_entry.text = value ?? ""; nick_stack.set_visible_child_name("label"); check_if_done(); } } public string? password { get { return password_entry.text == "" ? null : password_entry.text; } set { password_label.label = value; password_entry.text = value; nick_stack.set_visible_child_name("label"); } } public bool fragment_active { get; set; default=true; } [GtkChild] private unowned Stack accounts_stack; [GtkChild] private unowned Button accounts_button; [GtkChild] private unowned Label accounts_label; [GtkChild] private unowned AccountComboBox account_combobox; [GtkChild] private unowned Stack jid_stack; [GtkChild] private unowned Button jid_button; [GtkChild] private unowned Label jid_label; [GtkChild] private unowned Entry jid_entry; [GtkChild] private unowned Stack nick_stack; [GtkChild] private unowned Button nick_button; [GtkChild] private unowned Label nick_label; [GtkChild] private unowned Entry nick_entry; [GtkChild] private unowned Stack password_stack; [GtkChild] private unowned Button password_button; [GtkChild] private unowned Label password_label; [GtkChild] private unowned Label password_text_label; [GtkChild] private unowned Entry password_entry; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Button notification_button; [GtkChild] private unowned Label notification_label; private StreamInteractor stream_interactor; private Button ok_button_; public Button ok_button { get { return ok_button_; } set { if (value != null) { value.clicked.connect(() => { on_ok_button_clicked.begin(); }); ok_button_ = value; } } } public ConferenceDetailsFragment(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; account_combobox.initialize(stream_interactor); accounts_button.clicked.connect(() => { set_active_stack(accounts_stack); }); jid_button.clicked.connect(() => { set_active_stack(jid_stack); }); nick_button.clicked.connect(() => { set_active_stack(nick_stack); }); password_button.clicked.connect(() => { set_active_stack(password_stack); }); account_combobox.changed.connect(() => { accounts_label.label = account_combobox.selected.bare_jid.to_string(); }); accounts_label.label = account_combobox.selected.bare_jid.to_string(); // jid_entry.key_release_event.connect(on_jid_key_release_event); // nick_entry.key_release_event.connect(on_nick_key_release_event); // password_entry.key_release_event.connect(on_password_key_release_event); jid_entry.changed.connect(() => { check_if_done(); }); nick_entry.changed.connect(() => { check_if_done(); }); check_if_done(); notification_button.clicked.connect(() => { notification_revealer.set_reveal_child(false); }); clear(); } public void clear() { jid = ""; nick = ""; password = ""; password_text_label.visible = false; password_stack.visible = false; notification_revealer.set_reveal_child(false); reset_editable(); } public void reset_editable() { jid_stack.set_visible_child_name("entry"); accounts_stack.set_visible_child_name("entry"); nick_stack.set_visible_child_name("entry"); password_stack.set_visible_child_name("entry"); } private async void on_ok_button_clicked() { if (!fragment_active) return; ok_button.label = _("Joining…"); ok_button.sensitive = false; string label_text = ""; try { Jid parsed_jid = new Jid(jid); Muc.JoinResult? join_result = yield stream_interactor.get_module(MucManager.IDENTITY).join(account, parsed_jid, nick, password); ok_button.label = _("Join"); ok_button.sensitive = true; if (join_result == null || join_result.nick != null) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, account, Conversation.Type.GROUPCHAT); Application app = GLib.Application.get_default() as Application; app.controller.select_conversation(conversation); joined(); return; } if (join_result.muc_error != null) { switch (join_result.muc_error) { case Muc.MucEnterError.PASSWORD_REQUIRED: label_text = _("Password required to enter room"); password_text_label.visible = true; password_stack.visible = true; break; case Muc.MucEnterError.BANNED: label_text = _("Banned from joining or creating conference"); break; case Muc.MucEnterError.ROOM_DOESNT_EXIST: label_text = _("Room does not exist"); break; case Muc.MucEnterError.CREATION_RESTRICTED: label_text = _("Not allowed to create room"); break; case Muc.MucEnterError.NOT_IN_MEMBER_LIST: label_text = _("Members-only room"); break; case Muc.MucEnterError.USE_RESERVED_ROOMNICK: case Muc.MucEnterError.NICK_CONFLICT: label_text = _("Choose a different nick"); break; case Muc.MucEnterError.OCCUPANT_LIMIT_REACHED: label_text = _("Too many occupants in room"); break; } } else if (join_result.stanza_error != null) { label_text = _("Could not connect to %s").printf((new Jid(jid)).domainpart); } } catch (InvalidJidError e) { label_text = _("Invalid address"); } notification_label.label = label_text; notification_revealer.set_reveal_child(true); } private void check_if_done() { try { Jid parsed_jid = new Jid(jid); done = parsed_jid.localpart != null && parsed_jid.resourcepart == null && nick != null; } catch (InvalidJidError e) { done = false; } } // private bool on_jid_key_release_event(EventKey event) { // jid_label.label = jid_entry.text; // if (event.keyval == Key.Return) jid_stack.set_visible_child_name("label"); // return false; // } // // private bool on_nick_key_release_event(EventKey event) { // nick_label.label = nick_entry.text; // if (event.keyval == Key.Return) nick_stack.set_visible_child_name("label"); // return false; // } // // private bool on_password_key_release_event(EventKey event) { // string filler = ""; // for (int i = 0; i < password_entry.text.length; i++) filler += password_entry.get_invisible_char().to_string(); // password_label.label = filler; // if (event.keyval == Key.Return) password_stack.set_visible_child_name("label"); // return false; // } private void set_active_stack(Stack stack) { stack.set_visible_child_name("entry"); if (stack != accounts_stack) accounts_stack.set_visible_child_name("label"); if (stack != jid_stack) jid_stack.set_visible_child_name("label"); if (stack != nick_stack) nick_stack.set_visible_child_name("label"); if (stack != password_stack) password_stack.set_visible_child_name("label"); } } } dino-0.5.0/main/src/ui/add_conversation/conference_list.vala0000664000000000000000000001061514776241610022641 0ustar rootrootusing Gee; using Gtk; using Xmpp; using Xmpp.Xep.Bookmarks; using Dino.Entities; namespace Dino.Ui { protected class ConferenceList { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; private ListBox list_box = new ListBox(); private HashMap> lists = new HashMap>(Account.hash_func, Account.equals_func); private HashMap> widgets = new HashMap>(Account.hash_func, Account.equals_func); ulong bookmarks_updated_handler_id = -1; public ConferenceList(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; bookmarks_updated_handler_id = stream_interactor.get_module(MucManager.IDENTITY).bookmarks_updated.connect((account, conferences) => { lists[account] = conferences; refresh_conferences(); }); foreach (Account account in stream_interactor.get_accounts()) { stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks.begin(account, (_, res) => { Set? conferences = stream_interactor.get_module(MucManager.IDENTITY).get_bookmarks.end(res); set_bookmarks(account, conferences); }); } stream_interactor.get_module(MucManager.IDENTITY).conference_added.connect(add_conference); stream_interactor.get_module(MucManager.IDENTITY).conference_removed.connect(remove_conference); } ~ConferenceList() { stream_interactor.get_module(MucManager.IDENTITY).disconnect(bookmarks_updated_handler_id); stream_interactor.get_module(MucManager.IDENTITY).conference_added.disconnect(add_conference); stream_interactor.get_module(MucManager.IDENTITY).conference_removed.disconnect(remove_conference); } private void add_conference(Account account, Conference conference) { if (!widgets.has_key(account)) { widgets[account] = new HashMap(Jid.hash_func, Jid.equals_func); } var widget = new ConferenceListRow(stream_interactor, conference, account); var list_box_row = new ListBoxRow(); list_box_row.set_child(widget); widgets[account][conference.jid] = list_box_row; list_box.append(list_box_row); } private void remove_conference(Account account, Jid jid) { if (widgets.has_key(account) && widgets[account].has_key(jid)) { list_box.remove(widgets[account][jid]); widgets[account].unset(jid); } } public void refresh_conferences() { foreach (Account account in widgets.keys) { var account_widgets_cpy = new HashMap(); account_widgets_cpy.set_all(widgets[account]); foreach (Jid jid in account_widgets_cpy.keys) { list_box.remove(widgets[account][jid]); } } foreach (Account account in lists.keys) { foreach (Conference conference in lists[account]) { add_conference(account, conference); } } } private void set_bookmarks(Account account, Set? conferences) { if (conferences == null) { lists.unset(account); } else { lists[account] = conferences; } refresh_conferences(); } public ListBox get_list_box() { return list_box; } } internal class ConferenceListRow : ListRow { public Conference bookmark; public ConferenceListRow(StreamInteractor stream_interactor, Conference bookmark, Account account) { this.jid = bookmark.jid; this.account = account; this.bookmark = bookmark; status_dot.visible = false; name_label.label = bookmark.name != null && bookmark.name != "" ? bookmark.name : bookmark.jid.to_string(); if (stream_interactor.get_accounts().size > 1) { via_label.label = "via " + account.bare_jid.to_string(); } else if (bookmark.name != null && bookmark.name != bookmark.jid.to_string()) { via_label.label = bookmark.jid.to_string(); } else { via_label.visible = false; } picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(new Conversation(jid, account, Conversation.Type.GROUPCHAT)); } } } dino-0.5.0/main/src/ui/add_conversation/list_row.vala0000664000000000000000000001003314776241610021333 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class ListRow : Widget { public Box outer_box; public AvatarPicture picture; public Label name_label; public Image status_dot; public Label via_label; public StreamInteractor stream_interactor; public Jid jid; public Account account; private ulong[] handler_ids = new ulong[0]; construct { Builder builder = new Builder.from_resource("/im/dino/Dino/add_conversation/list_row.ui"); outer_box = (Box) builder.get_object("outer_box"); picture = (AvatarPicture) builder.get_object("picture"); name_label = (Label) builder.get_object("name_label"); status_dot = (Image) builder.get_object("status_dot"); via_label = (Label) builder.get_object("via_label"); this.layout_manager = new BinLayout(); outer_box.set_parent(this); } private void set_status_dot(StreamInteractor stream_interactor){ status_dot.visible = stream_interactor.connection_manager.get_state(account) == ConnectionManager.ConnectionState.CONNECTED; Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(jid, account); if (full_jids != null) { var statuses = new ArrayList(); foreach (var full_jid in full_jids) { statuses.add(stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, account)); } if (statuses.contains(Xmpp.Presence.Stanza.SHOW_DND)) status_dot.set_from_icon_name("dino-status-dnd"); else if (statuses.contains(Xmpp.Presence.Stanza.SHOW_CHAT)) status_dot.set_from_icon_name("dino-status-chat"); else if (statuses.contains(Xmpp.Presence.Stanza.SHOW_ONLINE)) status_dot.set_from_icon_name("dino-status-online"); else if (statuses.contains(Xmpp.Presence.Stanza.SHOW_AWAY)) status_dot.set_from_icon_name("dino-status-away"); else if (statuses.contains(Xmpp.Presence.Stanza.SHOW_XA)) status_dot.set_from_icon_name("dino-status-away"); else status_dot.set_from_icon_name("dino-status-offline"); } else { status_dot.set_from_icon_name("dino-status-offline"); } } public ListRow.from_jid(StreamInteractor stream_interactor, Jid jid, Account account, bool show_account) { this.stream_interactor = stream_interactor; this.jid = jid; this.account = account; Conversation conv = new Conversation(jid, account, Conversation.Type.CHAT); string display_name = Util.get_conversation_display_name(stream_interactor, conv); if (show_account && stream_interactor.get_accounts().size > 1) { via_label.label = @"via $(account.bare_jid)"; this.has_tooltip = Util.use_tooltips(); set_tooltip_text(Util.string_if_tooltips_active(jid.to_string())); } else if (display_name != jid.bare_jid.to_string()){ via_label.label = jid.bare_jid.to_string(); } else { via_label.visible = false; } name_label.label = display_name; picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(conv); handler_ids += stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect((jid, account) => { if (account.equals(this.account) && jid.equals_bare(this.jid)) { set_status_dot(stream_interactor); } }); handler_ids += stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect((jid, account) => { if (account.equals(this.account) && jid.equals_bare(this.jid)) { set_status_dot(stream_interactor); } }); set_status_dot(stream_interactor); } public override void dispose() { outer_box.unparent(); foreach (var handler_id in handler_ids) { stream_interactor.get_module(PresenceManager.IDENTITY).disconnect(handler_id); } } } } dino-0.5.0/main/src/ui/add_conversation/roster_list.vala0000664000000000000000000000464214776241610022053 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { protected class RosterList { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; private Gee.List accounts; private ulong[] handler_ids = new ulong[0]; private ListBox list_box = new ListBox(); private HashMap> rows = new HashMap>(Account.hash_func, Account.equals_func); public RosterList(StreamInteractor stream_interactor, Gee.List accounts) { this.stream_interactor = stream_interactor; this.accounts = accounts; handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).removed_roster_item.connect( (account, jid) => { if (accounts.contains(account)) { remove_row(account, jid); } }); handler_ids += stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect( (account, jid) => { if (accounts.contains(account)) { update_row(account, jid); } }); list_box.destroy.connect(() => { foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); }); foreach (Account a in accounts) fetch_roster_items(a); } private void remove_row(Account account, Jid jid) { if (rows.has_key(account) && rows[account].has_key(jid)) { list_box.remove(rows[account][jid]); rows[account].unset(jid); } } private void update_row(Account account, Jid jid) { remove_row(account, jid); ListRow row = new ListRow.from_jid(stream_interactor, jid, account, accounts.size > 1); ListBoxRow list_box_row = new ListBoxRow() { child=row }; rows[account][jid] = list_box_row; list_box.append(list_box_row); list_box.invalidate_sort(); list_box.invalidate_filter(); } private void fetch_roster_items(Account account) { rows[account] = new HashMap(Jid.hash_func, Jid.equals_func); foreach (Roster.Item roster_item in stream_interactor.get_module(RosterManager.IDENTITY).get_roster(account)) { update_row(account, roster_item.jid); } } public ListBox get_list_box() { return list_box; } } } dino-0.5.0/main/src/ui/add_conversation/select_contact_dialog.vala0000664000000000000000000000671514776241610024016 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class SelectContactDialog : Gtk.Dialog { public signal void selected(Account account, Jid jid); public Button ok_button; private RosterList roster_list; private ListBox roster_list_box; private SelectJidFragment select_jid_fragment; private StreamInteractor stream_interactor; private Gee.List accounts; public SelectContactDialog(StreamInteractor stream_interactor, Gee.List accounts) { Object(use_header_bar : 1); modal = true; this.default_width = 460; this.default_height = 550; this.stream_interactor = stream_interactor; this.accounts = accounts; setup_view(); setup_headerbar(); } public void set_filter(string str) { select_jid_fragment.set_filter(str); } private void setup_headerbar() { Button cancel_button = new Button(); cancel_button.set_label(_("Cancel")); cancel_button.visible = true; ok_button = new Button(); ok_button.add_css_class("suggested-action"); ok_button.sensitive = false; ok_button.visible = true; HeaderBar header_bar = get_header_bar() as HeaderBar; header_bar.show_title_buttons = false; header_bar.pack_start(cancel_button); header_bar.pack_end(ok_button); cancel_button.clicked.connect(() => { close(); }); ok_button.clicked.connect(() => { ListRow? selected_row = roster_list_box.get_selected_row() != null ? roster_list_box.get_selected_row().get_child() as ListRow : null; if (selected_row != null) selected(selected_row.account, selected_row.jid); close(); }); } private void setup_view() { roster_list = new RosterList(stream_interactor, accounts); roster_list_box = roster_list.get_list_box(); roster_list_box.row_activated.connect(() => { ok_button.clicked(); }); select_jid_fragment = new SelectJidFragment(stream_interactor, roster_list_box, accounts); select_jid_fragment.add_jid.connect((row) => { AddContactDialog add_contact_dialog = new AddContactDialog(stream_interactor); add_contact_dialog.set_transient_for(this); add_contact_dialog.present(); }); select_jid_fragment.remove_jid.connect((row) => { ListRow list_row = roster_list_box.get_selected_row().child as ListRow; stream_interactor.get_module(RosterManager.IDENTITY).remove_jid(list_row.account, list_row.jid); }); select_jid_fragment.notify["done"].connect(() => { ok_button.sensitive = select_jid_fragment.done; }); get_content_area().append(select_jid_fragment); } } public class AddChatDialog : SelectContactDialog { public signal void added(Conversation conversation); public AddChatDialog(StreamInteractor stream_interactor, Gee.List accounts) { base(stream_interactor, accounts); title = _("Start Conversation"); ok_button.label = _("Start"); selected.connect((account, jid) => { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); added(conversation); }); } } } dino-0.5.0/main/src/ui/add_conversation/select_jid_fragment.vala0000664000000000000000000001066414776241610023473 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/add_conversation/select_jid_fragment.ui")] public class SelectJidFragment : Gtk.Box { public signal void add_jid(); public signal void remove_jid(ListRow row); public bool done { get { return list.get_selected_row() != null; } private set {} } [GtkChild] private unowned Entry entry; [GtkChild] private unowned Box box; [GtkChild] private unowned Button add_button; [GtkChild] private unowned Button remove_button; private StreamInteractor stream_interactor; private Gee.List accounts; private ArrayList added_rows = new ArrayList(); private ListBox list; private string[]? filter_values; public SelectJidFragment(StreamInteractor stream_interactor, ListBox list, Gee.List accounts) { this.stream_interactor = stream_interactor; this.list = list; this.accounts = accounts; list.activate_on_single_click = false; list.vexpand = true; box.append(list); list.set_sort_func(sort); list.set_filter_func(filter); list.set_header_func(header); list.row_selected.connect(check_buttons_active); list.row_selected.connect(() => { done = true; }); // just for notifying entry.changed.connect(() => { set_filter(entry.text); }); add_button.clicked.connect(() => { add_jid(); }); remove_button.clicked.connect(() => { var list_row = list.get_selected_row(); if (list_row == null) return; remove_jid(list_row.child as ListRow); }); } public void set_filter(string str) { if (entry.text != str) entry.text = str; foreach (Widget row in added_rows) list.remove(row); added_rows.clear(); filter_values = str == "" ? null : str.split(" "); list.invalidate_filter(); try { Jid parsed_jid = new Jid(str); if (parsed_jid != null && parsed_jid.localpart != null) { foreach (Account account in accounts) { var list_row = new Gtk.ListBoxRow(); list_row.set_child(new AddListRow(stream_interactor, parsed_jid, account)); list.append(list_row); added_rows.add(list_row); } } } catch (InvalidJidError ignored) { // Ignore } } private void check_buttons_active() { ListBoxRow? row = list.get_selected_row(); bool active = row != null && !row.get_type().is_a(typeof(AddListRow)); remove_button.sensitive = active; } private int sort(ListBoxRow row1, ListBoxRow row2) { AddListRow al1 = (row1 as AddListRow); AddListRow al2 = (row2 as AddListRow); if (al1 != null && al2 == null) { return -1; } else if (al2 != null && al1 == null) { return 1; } ListRow? c1 = (row1.child as ListRow); ListRow? c2 = (row2.child as ListRow); if (c1 != null && c2 != null) { return c1.name_label.label.collate(c2.name_label.label); } return 0; } private bool filter(ListBoxRow r) { ListRow? row = (r.child as ListRow); if (row == null) return true; if (filter_values != null) { foreach (string filter in filter_values) { if (!(row.name_label.label.down().contains(filter.down()) || row.jid.to_string().down().contains(filter.down()))) { return false; } } } return true; } private void header(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } private class AddListRow : ListRow { public AddListRow(StreamInteractor stream_interactor, Jid jid, Account account) { this.account = account; this.jid = jid; name_label.label = jid.to_string(); if (stream_interactor.get_accounts().size > 1) { via_label.label = account.bare_jid.to_string(); } else { via_label.visible = false; } picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add("+"); } } } } dino-0.5.0/main/src/ui/application.vala0000664000000000000000000003771614776241610016473 0ustar rootrootusing Gtk; using Dino.Entities; using Dino.Ui; using Xmpp; public class Dino.Ui.Application : Adw.Application, Dino.Application { private const string[] KEY_COMBINATION_QUIT = {"Q", null}; private const string[] KEY_COMBINATION_ADD_CHAT = {"T", null}; private const string[] KEY_COMBINATION_ADD_CONFERENCE = {"G", null}; private const string[] KEY_COMBINATION_LOOP_CONVERSATIONS = {"Tab", null}; private const string[] KEY_COMBINATION_LOOP_CONVERSATIONS_REV = {"Tab", null}; private const string[] KEY_COMBINATION_SHOW_SETTINGS = {"comma", null}; public MainWindow window; public MainWindowController controller; public Database db { get; set; } public Dino.Entities.Settings settings { get; set; } private Config config { get; set; } public StreamInteractor stream_interactor { get; set; } public Plugins.Registry plugin_registry { get; set; default = new Plugins.Registry(); } public SearchPathGenerator? search_path_generator { get; set; } internal static bool print_version = false; private const OptionEntry[] options = { { "version", 0, 0, OptionArg.NONE, ref print_version, "Display version number", null }, { null } }; public Application() throws Error { Object(application_id: "im.dino.Dino", flags: ApplicationFlags.HANDLES_OPEN); init(); Environment.set_application_name("Dino"); Window.set_default_icon_name("im.dino.Dino"); create_actions(); add_main_option_entries(options); startup.connect(() => { if (print_version) { print(@"Dino $(Dino.get_version())\n"); Process.exit(0); } NotificationEvents notification_events = stream_interactor.get_module(NotificationEvents.IDENTITY); get_notifications_dbus.begin((_, res) => { // It might take a bit to get the interface. NotificationEvents will queue any notifications in the meantime. try { DBusNotifications? dbus_notifications = get_notifications_dbus.end(res); if (dbus_notifications != null) { FreeDesktopNotifier free_desktop_notifier = new FreeDesktopNotifier(stream_interactor, dbus_notifications); notification_events.register_notification_provider.begin(free_desktop_notifier); } else { notification_events.register_notification_provider.begin(new GNotificationsNotifier(stream_interactor)); } } catch (Error e) { debug("Failed accessing fdo notification server: %s", e.message); } }); notification_events.notify_content_item.connect((content_item, conversation) => { // Set urgency hint also if (normal) notifications are disabled // Don't set urgency hint in GNOME, produces "Window is active" notification var desktop_env = Environment.get_variable("XDG_CURRENT_DESKTOP"); if (desktop_env == null || !desktop_env.down().contains("gnome")) { if (this.active_window != null) { // this.active_window.urgency_hint = true; } } }); stream_interactor.get_module(FileManager.IDENTITY).add_metadata_provider(new Util.AudioVideoFileMetadataProvider()); }); activate.connect(() => { if (window == null) { controller = new MainWindowController(this, stream_interactor, db); config = new Config(db); window = new MainWindow(this, stream_interactor, db, config); controller.set_window(window); if ((get_flags() & ApplicationFlags.IS_SERVICE) == ApplicationFlags.IS_SERVICE) window.hide_on_close = true; } window.present(); }); } public void handle_uri(string jid, string query, Gee.Map options) { switch (query) { case "join": show_join_muc_dialog(null, jid); break; case "message": Gee.List accounts = stream_interactor.get_accounts(); Jid parsed_jid = null; try { parsed_jid = new Jid(jid); } catch (InvalidJidError ignored) { // Ignored } if (accounts.size == 1 && parsed_jid != null) { Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(parsed_jid, accounts[0], Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); controller.select_conversation(conversation); } else { AddChatDialog dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); dialog.set_filter(jid); dialog.set_transient_for(window); dialog.added.connect((conversation) => { controller.select_conversation(conversation); }); dialog.present(); } break; } } private void create_actions() { SimpleAction preferences_action = new SimpleAction("preferences", null); preferences_action.activate.connect(show_preferences_window); add_action(preferences_action); set_accels_for_action("app.preferences", KEY_COMBINATION_SHOW_SETTINGS); SimpleAction preferences_account_action = new SimpleAction("preferences-account", VariantType.INT32); preferences_account_action.activate.connect((variant) => { Account? account = db.get_account_by_id(variant.get_int32()); if (account == null) return; show_preferences_account_window(account); }); add_action(preferences_account_action); SimpleAction about_action = new SimpleAction("about", null); about_action.activate.connect(show_about_window); add_action(about_action); SimpleAction quit_action = new SimpleAction("quit", null); quit_action.activate.connect(quit); add_action(quit_action); set_accels_for_action("app.quit", KEY_COMBINATION_QUIT); SimpleAction open_conversation_action = new SimpleAction("open-conversation", VariantType.INT32); open_conversation_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation != null) controller.select_conversation(conversation); Util.present_window(window); }); add_action(open_conversation_action); SimpleAction open_conversation_details_action = new SimpleAction("open-conversation-details", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.STRING})); open_conversation_details_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; string stack_value = variant.get_child_value(1).get_string(); var conversation_details = ConversationDetails.setup_dialog(conversation, stream_interactor, window); conversation_details.stack.visible_child_name = stack_value; conversation_details.present(); }); add_action(open_conversation_details_action); SimpleAction deny_subscription_action = new SimpleAction("deny-subscription", VariantType.INT32); deny_subscription_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; stream_interactor.get_module(PresenceManager.IDENTITY).deny_subscription(conversation.account, conversation.counterpart); }); add_action(deny_subscription_action); SimpleAction contacts_action = new SimpleAction("add_chat", null); contacts_action.activate.connect(() => { AddChatDialog add_chat_dialog = new AddChatDialog(stream_interactor, stream_interactor.get_accounts()); add_chat_dialog.set_transient_for(window); add_chat_dialog.added.connect((conversation) => controller.select_conversation(conversation)); add_chat_dialog.present(); }); add_action(contacts_action); set_accels_for_action("app.add_chat", KEY_COMBINATION_ADD_CHAT); SimpleAction conference_action = new SimpleAction("add_conference", null); conference_action.activate.connect(() => { AddConferenceDialog add_conference_dialog = new AddConferenceDialog(stream_interactor); add_conference_dialog.set_transient_for(window); add_conference_dialog.present(); }); add_action(conference_action); set_accels_for_action("app.add_conference", KEY_COMBINATION_ADD_CONFERENCE); SimpleAction accept_muc_invite_action = new SimpleAction("open-muc-join", VariantType.INT32); accept_muc_invite_action.activate.connect((variant) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(variant.get_int32()); if (conversation == null) return; show_join_muc_dialog(conversation.account, conversation.counterpart.to_string()); }); add_action(accept_muc_invite_action); SimpleAction accept_voice_request_action = new SimpleAction("accept-voice-request", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.STRING})); accept_voice_request_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; string nick = variant.get_child_value(1).get_string(); stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, nick, "participant"); }); add_action(accept_voice_request_action); SimpleAction loop_conversations_action = new SimpleAction("loop_conversations", null); loop_conversations_action.activate.connect(() => { window.loop_conversations(false); }); add_action(loop_conversations_action); set_accels_for_action("app.loop_conversations", KEY_COMBINATION_LOOP_CONVERSATIONS); SimpleAction loop_conversations_bw_action = new SimpleAction("loop_conversations_bw", null); loop_conversations_bw_action.activate.connect(() => { window.loop_conversations(true); }); add_action(loop_conversations_bw_action); set_accels_for_action("app.loop_conversations_bw", KEY_COMBINATION_LOOP_CONVERSATIONS_REV); SimpleAction accept_call_action = new SimpleAction("accept-call", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); accept_call_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; int call_id = variant.get_child_value(1).get_int32(); Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(call_id, conversation); CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call]; if (call_state == null) return; call_state.accept(); var call_window = new CallWindow(); call_window.controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.present(); }); add_action(accept_call_action); SimpleAction deny_call_action = new SimpleAction("reject-call", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); deny_call_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null) return; int call_id = variant.get_child_value(1).get_int32(); Call? call = stream_interactor.get_module(CallStore.IDENTITY).get_call_by_id(call_id, conversation); CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call]; if (call_state == null) return; call_state.reject(); }); add_action(deny_call_action); } private void show_preferences_window() { Ui.PreferencesWindow dialog = new Ui.PreferencesWindow() { transient_for = window }; dialog.model.populate(db, stream_interactor); dialog.present(); } private void show_preferences_account_window(Account account) { Ui.PreferencesWindow dialog = new Ui.PreferencesWindow() { transient_for = window }; dialog.model.populate(db, stream_interactor); dialog.accounts_page.account_chosen(account); dialog.present(); } private void show_about_window() { string? version = Dino.get_version().strip().length == 0 ? null : Dino.get_version(); if (version != null && !version.contains("git")) { switch (version.substring(0, 3)) { case "0.2": version = @"$version - Mexican Caribbean Coral Reefs"; break; case "0.3": version = @"$version - Theikenmeer"; break; case "0.4": version = @"$version - Ilulissat"; break; case "0.5": version = @"$version - Alentejo"; break; } } Adw.AboutWindow about_window = new Adw.AboutWindow(); about_window.application_icon = "im.dino.Dino"; about_window.application_name = "Dino"; about_window.issue_url = "https://github.com/dino/dino/issues"; about_window.destroy_with_parent = true; about_window.transient_for = window; about_window.modal = true; about_window.title = _("About Dino"); about_window.version = version; about_window.website = "https://dino.im/"; about_window.copyright = "Copyright © 2016-2025 - Dino Team"; about_window.license_type = License.GPL_3_0; about_window.present(); } private void show_join_muc_dialog(Account? account, string jid) { Dialog dialog = new Dialog.with_buttons(_("Join Channel"), window, Gtk.DialogFlags.MODAL | Gtk.DialogFlags.USE_HEADER_BAR, _("Join"), ResponseType.OK, _("Cancel"), ResponseType.CANCEL); dialog.modal = true; Button ok_button = dialog.get_widget_for_response(ResponseType.OK) as Button; ok_button.add_css_class("suggested-action"); ConferenceDetailsFragment conference_fragment = new ConferenceDetailsFragment(stream_interactor) { ok_button=ok_button }; conference_fragment.jid = jid; if (account != null) { conference_fragment.account = account; } Box content_area = dialog.get_content_area(); content_area.append(conference_fragment); conference_fragment.joined.connect(() => { dialog.destroy(); }); dialog.response.connect((response_id) => { if (response_id == ResponseType.CANCEL) { dialog.destroy(); } }); dialog.present(); } } dino-0.5.0/main/src/ui/call_window/0000775000000000000000000000000014776241610015607 5ustar rootrootdino-0.5.0/main/src/ui/call_window/audio_settings_popover.vala0000664000000000000000000001540114776241610023250 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; public class Dino.Ui.AudioSettingsPopover : Gtk.Popover { public signal void microphone_selected(Plugins.MediaDevice device); public signal void speaker_selected(Plugins.MediaDevice device); public Plugins.MediaDevice? current_microphone_device { get; set; } public Plugins.MediaDevice? current_speaker_device { get; set; } private HashMap row_microphone_device = new HashMap(); private HashMap row_speaker_device = new HashMap(); public AudioSettingsPopover() { Box box = new Box(Orientation.VERTICAL, 15); box.append(create_microphone_box()); box.append(create_speaker_box()); this.set_child(box); } private Widget create_microphone_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("audio", false); Box micro_box = new Box(Orientation.VERTICAL, 10); micro_box.append(new Label("" + _("Microphones") + "") { use_markup=true, xalign=0, can_focus=true /* grab initial focus*/ }); if (devices.size == 0) { micro_box.append(new Label(_("No microphone found."))); } else { ListBox micro_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; micro_list_box.set_header_func(listbox_header_func); Frame micro_frame = new Frame(null); micro_frame.set_child(micro_list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_microphone_device == null || current_microphone_device.id != device.id) { image.opacity = 0; } this.notify["current-microphone-device"].connect(() => { if (current_microphone_device == null || current_microphone_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0); label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); micro_list_box.append(list_box_row); row_microphone_device[list_box_row] = device; } micro_list_box.row_activated.connect((row) => { if (!row_microphone_device.has_key(row)) return; microphone_selected(row_microphone_device[row]); micro_list_box.unselect_row(row); }); micro_box.append(micro_frame); } return micro_box; } private Widget create_speaker_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("audio", true); Box speaker_box = new Box(Orientation.VERTICAL, 10); speaker_box.append(new Label("" + _("Speakers") +"") { use_markup=true, xalign=0 }); if (devices.size == 0) { speaker_box.append(new Label(_("No speaker found."))); } else { ListBox speaker_list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; speaker_list_box.set_header_func(listbox_header_func); speaker_list_box.row_selected.connect((row) => { }); Frame speaker_frame = new Frame(null); speaker_frame.set_child(speaker_list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_speaker_device == null || current_speaker_device.id != device.id) { image.opacity = 0; } this.notify["current-speaker-device"].connect(() => { if (current_speaker_device == null || current_speaker_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); speaker_list_box.append(list_box_row); row_speaker_device[list_box_row] = device; } speaker_list_box.row_activated.connect((row) => { if (!row_speaker_device.has_key(row)) return; speaker_selected(row_speaker_device[row]); speaker_list_box.unselect_row(row); }); speaker_box.append(speaker_frame); } return speaker_box; } private void listbox_header_func(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } }dino-0.5.0/main/src/ui/call_window/call_bottom_bar.vala0000664000000000000000000001622214776241610021602 0ustar rootrootusing Dino.Entities; using Gtk; using Pango; public class Dino.Ui.CallBottomBar : Gtk.Box { public signal void hang_up(); public bool audio_enabled { get; set; } public bool video_enabled { get; set; } public string counterpart_display_name { get; set; } private Button audio_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; private Overlay audio_button_overlay = new Overlay(); private Image audio_image = new Image() { pixel_size=22 }; private MenuButton audio_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; public AudioSettingsPopover? audio_settings_popover; private Button video_button = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; private Overlay video_button_overlay = new Overlay(); private Image video_image = new Image() { pixel_size=22 }; private MenuButton video_settings_button = new MenuButton() { halign=Align.END, valign=Align.END }; public VideoSettingsPopover? video_settings_popover; private Label label = new Label("") { halign=Align.CENTER, valign=Align.CENTER, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true }; private Stack stack = new Stack(); public CallBottomBar() { Object(orientation:Orientation.HORIZONTAL, spacing:0); Box main_buttons = new Box(Orientation.HORIZONTAL, 20) { margin_start=40, margin_end=40, margin_bottom=20, margin_top=20, halign=Align.CENTER, hexpand=true }; audio_button.set_child(audio_image); audio_button.add_css_class("circular"); audio_button.clicked.connect(() => { audio_enabled = !audio_enabled; }); Adw.Bin audio_button_bin = new Adw.Bin(); audio_button_bin.add_css_class("call-button"); audio_button_bin.margin_end = audio_button_bin.margin_bottom = 5; // space for the small settings button audio_button_bin.set_child(audio_button); audio_button_overlay.set_child(audio_button_bin); audio_button_overlay.add_overlay(audio_settings_button); Util.menu_button_set_icon_with_size(audio_settings_button, "go-up-symbolic", 10); audio_settings_button.add_css_class("circular"); audio_settings_button.add_css_class("call-button"); audio_settings_button.add_css_class("call-mediadevice-settings-button"); main_buttons.append(audio_button_overlay); video_button.set_child(video_image); video_button.add_css_class("circular"); video_button.clicked.connect(() => { video_enabled = !video_enabled; }); Adw.Bin video_button_bin = new Adw.Bin(); video_button_bin.add_css_class("call-button"); video_button_bin.margin_end = video_button_bin.margin_bottom = 5; video_button_bin.set_child(video_button); video_button_overlay.set_child(video_button_bin); video_button_overlay.add_overlay(video_settings_button); Util.menu_button_set_icon_with_size(video_settings_button, "go-up-symbolic", 10); video_settings_button.add_css_class("circular"); video_settings_button.add_css_class("call-button"); video_settings_button.add_css_class("call-mediadevice-settings-button"); main_buttons.append(video_button_overlay); Button button_hang = new Button() { height_request=45, width_request=45, halign=Align.START, valign=Align.START }; button_hang.set_child(new Image() { icon_name="dino-phone-hangup-symbolic", pixel_size=22 }); button_hang.add_css_class("circular"); button_hang.add_css_class("destructive-action"); button_hang.clicked.connect(() => hang_up()); Adw.Bin button_hang_bin = new Adw.Bin() { valign=Align.START }; button_hang_bin.add_css_class("call-button"); button_hang_bin.set_child(button_hang); main_buttons.append(button_hang_bin); label.add_css_class("text-no-controls"); stack.add_named(main_buttons, "control-buttons"); stack.add_named(label, "label"); this.append(stack); this.notify["audio-enabled"].connect(on_audio_enabled_changed); this.notify["video-enabled"].connect(on_video_enabled_changed); audio_enabled = true; video_enabled = false; on_audio_enabled_changed(); on_video_enabled_changed(); this.add_css_class("call-bottom-bar"); } public AudioSettingsPopover? show_audio_device_choices(bool show) { audio_settings_button.visible = show; if (audio_settings_popover != null) audio_settings_popover.visible = false; if (!show) return null; audio_settings_popover = new AudioSettingsPopover(); audio_settings_button.popover = audio_settings_popover; audio_settings_popover.microphone_selected.connect(() => { audio_settings_button.popdown(); }); audio_settings_popover.speaker_selected.connect(() => { audio_settings_button.popdown(); }); return audio_settings_popover; } public void show_audio_device_error() { audio_settings_button.set_icon_name("dialog-warning-symbolic"); Util.force_error_color(audio_settings_button); } public VideoSettingsPopover? show_video_device_choices(bool show) { video_settings_button.visible = show; if (video_settings_popover != null) video_settings_popover.visible = false; if (!show) return null; video_settings_popover = new VideoSettingsPopover(); video_settings_button.popover = video_settings_popover; video_settings_popover.camera_selected.connect(() => { video_settings_button.popdown(); }); return video_settings_popover; } public void show_video_device_error() { video_settings_button.set_icon_name("dialog-warning-symbolic"); Util.force_error_color(video_settings_button); } public void on_audio_enabled_changed() { if (audio_enabled) { audio_image.icon_name = "audio-input-microphone-symbolic"; audio_button.add_css_class("white-button"); audio_button.remove_css_class("transparent-white-button"); } else { audio_image.icon_name = "microphone-disabled-symbolic"; audio_button.remove_css_class("white-button"); audio_button.add_css_class("transparent-white-button"); } } public void on_video_enabled_changed() { if (video_enabled) { video_image.icon_name = "dino-video-symbolic"; video_button.add_css_class("white-button"); video_button.remove_css_class("transparent-white-button"); } else { video_image.icon_name = "dino-video-off-symbolic"; video_button.remove_css_class("white-button"); video_button.add_css_class("transparent-white-button"); } } public void show_counterpart_ended(string text) { stack.set_visible_child_name("label"); label.label = Util.unbreak_space_around_non_spacing_mark(text); } public bool is_menu_active() { return (video_settings_button.popover != null && video_settings_button.popover.visible) || (audio_settings_button.popover != null && audio_settings_button.popover.visible); } } dino-0.5.0/main/src/ui/call_window/call_connection_details_window.vala0000664000000000000000000001132314776241610024702 0ustar rootrootusing Gtk; namespace Dino.Ui { public class CallConnectionDetailsWindow : Gtk.Window { public Box box = new Box(Orientation.VERTICAL, 15) { halign=Align.CENTER, valign=Align.CENTER }; private bool video_added = false; private CallContentDetails audio_details = new CallContentDetails("Audio"); private CallContentDetails video_details = new CallContentDetails("Video"); public CallConnectionDetailsWindow() { box.append(audio_details); box.append(video_details); set_child(box); } public void update_content(PeerInfo peer_info) { if (peer_info.audio != null) { audio_details.update_content(peer_info.audio); } if (peer_info.video != null) { add_video_widgets(); video_details.update_content(peer_info.video); } } private void add_video_widgets() { if (video_added) return; video_details.visible = true; video_added = true; } } public class CallContentDetails : Gtk.Grid { public Label rtp_title = new Label("RTP") { xalign=0 }; public Label rtcp_title = new Label("RTCP") { xalign=0 }; public Label target_recv_title = new Label("Target receive bitrate") { xalign=0 }; public Label target_send_title = new Label("Target send bitrate") { xalign=0 }; public Label rtp_ready = new Label("?") { xalign=0 }; public Label rtcp_ready = new Label("?") { xalign=0 }; public Label sent_bps = new Label("?") { use_markup=true, xalign=0 }; public Label recv_bps = new Label("?") { use_markup=true, xalign=0 }; public Label codec = new Label("?") { xalign=0 }; public Label target_receive_bitrate = new Label("n/a") { use_markup=true, xalign=0 }; public Label target_send_bitrate = new Label("n/a") { use_markup=true, xalign=0 }; private PeerContentInfo? prev_info = null; private int row_at = 0; public CallContentDetails(string headline) { attach(new Label("%s".printf(headline)) { use_markup=true, xalign=0 }, 0, row_at++, 1, 1); attach(rtp_title, 0, row_at, 1, 1); attach(rtp_ready, 1, row_at++, 1, 1); attach(rtcp_title, 0, row_at, 1, 1); attach(rtcp_ready, 1, row_at++, 1, 1); put_row("Sent"); attach(sent_bps, 1, row_at++, 1, 1); put_row("Received"); attach(recv_bps, 1, row_at++, 1, 1); put_row("Codec"); attach(codec, 1, row_at++, 1, 1); attach(target_recv_title, 0, row_at, 1, 1); attach(target_receive_bitrate, 1, row_at++, 1, 1); attach(target_send_title, 0, row_at, 1, 1); attach(target_send_bitrate, 1, row_at++, 1, 1); this.column_spacing = 5; } public void update_content(PeerContentInfo info) { if (!info.rtp_ready) { rtp_ready.visible = rtcp_ready.visible = true; rtp_title.visible = rtcp_title.visible = true; rtp_ready.label = info.rtp_ready.to_string(); rtcp_ready.label = info.rtcp_ready.to_string(); } else { rtp_ready.visible = rtcp_ready.visible = false; rtp_title.visible = rtcp_title.visible = false; } if (info.target_send_bytes != -1) { target_receive_bitrate.visible = target_send_bitrate.visible = true; target_recv_title.visible = target_send_title.visible = true; target_receive_bitrate.label = "%u kbps".printf(info.target_receive_bytes); target_send_bitrate.label = "%u kbps".printf(info.target_send_bytes); } else { target_receive_bitrate.visible = target_send_bitrate.visible = false; target_recv_title.visible = target_send_title.visible = false; } codec.label = info.codec + " " + info.clockrate.to_string(); if (prev_info != null) { ulong audio_sent_kbps = (info.bytes_sent - prev_info.bytes_sent) * 8 / 1000; sent_bps.label = "%lu kbps".printf(audio_sent_kbps); ulong audio_recv_kbps = (info.bytes_received - prev_info.bytes_received) * 8 / 1000; recv_bps.label = "%lu kbps".printf(audio_recv_kbps); } prev_info = info; } private void put_row(string label) { attach(new Label(label) { xalign=0 }, 0, row_at, 1, 1); } } } dino-0.5.0/main/src/ui/call_window/call_encryption_button.vala0000664000000000000000000001155714776241610023245 0ustar rootrootusing Dino.Entities; using Gtk; using Pango; public class Dino.Ui.CallEncryptionButtonController : Object { private bool has_been_set = false; public bool controls_active { get; set; default=false; } public MenuButton button; public CallEncryptionButtonController(MenuButton button) { this.button = button; button.opacity = 0; // button.set_popover(popover); button.notify["controls-active"].connect(update_opacity); } public void set_icon(bool encrypted, string? icon_name) { if (encrypted) { button.icon_name = icon_name ?? "changes-prevent-symbolic"; button.remove_css_class("unencrypted"); } else { button.icon_name = icon_name ?? "changes-allow-symbolic"; button.add_css_class("unencrypted"); } has_been_set = true; update_opacity(); } private static bool bytes_equal(uint8[] a1, uint8[] a2) { return a1.length == a2.length && Memory.cmp(a1, a2, a1.length) == 0; } private static bool encryption_equals(Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) { if (audio_encryption == null && video_encryption == null) return true; if (audio_encryption == null || video_encryption == null) return false; if (audio_encryption.encryption_ns != video_encryption.encryption_ns) return false; if (audio_encryption.encryption_name != video_encryption.encryption_name) return false; if (!bytes_equal(audio_encryption.our_key,video_encryption.our_key)) return false; if (!bytes_equal(audio_encryption.peer_key,video_encryption.peer_key)) return false; return true; } public void set_info(string? title, bool show_keys, bool has_audio, Xmpp.Xep.Jingle.ContentEncryption? audio_encryption, bool has_video, Xmpp.Xep.Jingle.ContentEncryption? video_encryption) { Popover popover = new Popover(); button.set_popover(popover); Xmpp.Xep.Jingle.ContentEncryption? single_encryption = null; if (!has_audio || !has_video || encryption_equals(audio_encryption, video_encryption)) { single_encryption = audio_encryption ?? video_encryption; if (single_encryption == null) { popover.set_child(new Label("This call is unencrypted.") ); return; } } if (title != null && !show_keys) { popover.set_child(new Label(title) { use_markup=true } ); return; } Box box = new Box(Orientation.VERTICAL, 10); if (single_encryption != null) { box.append(new Label("%s".printf(title ?? "This call is end-to-end encrypted.")) { use_markup=true, xalign=0 }); box.append(create_media_encryption_grid(single_encryption)); } else { box.append(new Label("%s".printf(title ?? "This call is partially end-to-end encrypted.")) { use_markup=true, xalign=0 }); if (has_audio) { box.append(new Label("Audio") { use_markup=true, xalign=0 }); box.append(create_media_encryption_grid(audio_encryption)); } if (has_video) { box.append(new Label("Video") { use_markup=true, xalign=0 }); box.append(create_media_encryption_grid(video_encryption)); } } popover.set_child(box); } public void update_opacity() { button.opacity = controls_active && has_been_set ? 1 : 0; } private Widget create_media_encryption_grid(Xmpp.Xep.Jingle.ContentEncryption? encryption) { if (encryption == null) { return new Label("This content is unencrypted.") { xalign=0 }; } Grid ret = new Grid() { row_spacing=3, column_spacing=5 }; if (encryption.peer_key.length > 0) { ret.attach(new Label("Peer call key") { xalign=0 }, 1, 2, 1, 1); ret.attach(new Label("" + format_fingerprint(encryption.peer_key) + "") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 2, 1, 1); } if (encryption.our_key.length > 0) { ret.attach(new Label("Your call key") { xalign=0 }, 1, 3, 1, 1); ret.attach(new Label("" + format_fingerprint(encryption.our_key) + "") { use_markup=true, max_width_chars=25, ellipsize=EllipsizeMode.MIDDLE, xalign=0, hexpand=true }, 2, 3, 1, 1); } return ret; } private string format_fingerprint(uint8[] fingerprint) { var sb = new StringBuilder(); for (int i = 0; i < fingerprint.length; i++) { sb.append("%02x".printf(fingerprint[i])); if (i < fingerprint.length - 1) { sb.append(":"); } } return sb.str; } }dino-0.5.0/main/src/ui/call_window/call_window.vala0000664000000000000000000003026314776241610020762 0ustar rootrootusing Gee; using Xmpp; using Dino.Entities; using Gtk; namespace Dino.Ui { public class CallWindow : Gtk.Window { public signal void menu_dump_dot(); public CallWindowController controller; public Overlay overlay = new Overlay(); public Grid grid = new Grid(); public CallBottomBar bottom_bar = new CallBottomBar(); public Revealer bottom_bar_revealer = new Revealer() { valign=Align.END, transition_type=RevealerTransitionType.CROSSFADE, transition_duration=200 }; public HeaderBar header_bar = new HeaderBar() { valign=Align.START, halign=Align.END, show_title_buttons=true, opacity=0.0 }; public Revealer header_bar_revealer = new Revealer() { halign=Align.END, valign=Align.START, transition_type=RevealerTransitionType.SLIDE_LEFT, transition_duration=200, reveal_child=false }; public Box own_video_box = new Box(Orientation.HORIZONTAL, 0) { halign=Align.END, valign=Align.END }; private HashMap participant_widgets = new HashMap(); private ArrayList participants = new ArrayList(); private EventControllerFocus this_focus_events = new EventControllerFocus(); private GestureClick this_gesture_events = new GestureClick() { touch_only=true, propagation_phase=Gtk.PropagationPhase.CAPTURE }; private EventControllerMotion this_motion_events = new EventControllerMotion(); private double latest_motion_x = -1; private double latest_motion_y = -1; private const double MOTION_RELEVANCE_THRESHOLD = 2; private int own_video_width = 150; private int own_video_height = 100; private bool hide_control_elements = false; private uint hide_control_handler = 0; public bool controls_active { get; set; default=true; } construct { header_bar.add_css_class("call-header-bar"); header_bar.title_widget = new Box(Orientation.VERTICAL, 0); // header_bar.spacing = 0; header_bar_revealer.set_child(header_bar); bottom_bar_revealer.set_child(bottom_bar); own_video_box.add_css_class("own-video"); this.add_css_class("dino-call-window"); overlay.set_child(grid); overlay.add_overlay(own_video_box); overlay.add_overlay(bottom_bar_revealer); overlay.add_overlay(header_bar_revealer); overlay.get_child_position.connect(on_get_child_position); set_child(overlay); } public CallWindow() { this.bind_property("controls-active", bottom_bar_revealer, "reveal-child", BindingFlags.SYNC_CREATE); ((Widget) this).add_controller(this_motion_events); this_motion_events.motion.connect((x, y) => { if ((latest_motion_x - x).abs() <= MOTION_RELEVANCE_THRESHOLD && (latest_motion_y - y).abs() <= MOTION_RELEVANCE_THRESHOLD) return; latest_motion_x = x; latest_motion_y = y; reveal_control_elements(); }); ((Widget) this).add_controller(this_focus_events); this_focus_events.enter.connect(reveal_control_elements); this_focus_events.leave.connect(reveal_control_elements); ((Widget) this).add_controller(this_gesture_events); this_gesture_events.pressed.connect_after(reveal_control_elements); this.notify["default-width"].connect(reveal_control_elements); this.notify["default-height"].connect(reveal_control_elements); this.notify["default-width"].connect(reposition_participant_widgets); this.notify["default-height"].connect(reposition_participant_widgets); this.set_titlebar(new OutsideHeaderBar(this.header_bar)); reveal_control_elements(); } public void add_participant(string participant, ParticipantWidget participant_widget) { participant_widget.visible = true; this.bind_property("controls-active", participant_widget, "controls-active", BindingFlags.SYNC_CREATE); this.bind_property("controls-active", participant_widget.encryption_button_controller, "controls-active", BindingFlags.SYNC_CREATE); participants.add(participant); participant_widgets[participant] = participant_widget; grid.attach(participant_widget, 0, 0); reposition_participant_widgets(); } public void remove_participant(string participant) { participant_widgets[participant].set_video(new Adw.Bin()); participants.remove(participant); grid.remove(participant_widgets[participant]); participant_widgets.unset(participant); reposition_participant_widgets(); } public void set_video(string participant, Widget widget) { participant_widgets[participant].set_video(widget); hide_control_elements = true; timeout_hide_control_elements(); } public void set_placeholder(string participant, Conversation? conversation, StreamInteractor stream_interactor) { participant_widgets[participant].set_placeholder(conversation, stream_interactor); hide_control_elements = false; foreach (ParticipantWidget participant_widget in participant_widgets.values) { if (participant_widget.shows_video) { hide_control_elements = true; } } if (!hide_control_elements) { reveal_control_elements(); } } private void reposition_participant_widgets() { int width = get_size(Orientation.HORIZONTAL); int height = get_size(Orientation.VERTICAL); reposition_participant_widgets_rec(participants, width, height, 0, 0, 0, 0); } private void reposition_participant_widgets_rec(ArrayList participants, int width, int height, int margin_top, int margin_right, int margin_bottom, int margin_left) { if (participants.size == 0) return; if (participants.size == 1) { participant_widgets[participants[0]].margin_top = margin_top; participant_widgets[participants[0]].margin_end = margin_right; participant_widgets[participants[0]].margin_bottom = margin_bottom; participant_widgets[participants[0]].margin_start = margin_left; participant_widgets[participants[0]].on_row_changed(margin_top == 0, margin_bottom == 0, margin_left == 0, margin_right == 0); return; } ArrayList first_part = new ArrayList(); ArrayList last_part = new ArrayList(); for (int i = 0; i < participants.size; i++) { if (i < Math.ceil((double)participants.size / (double)2)) { first_part.add(participants[i]); } else { last_part.add(participants[i]); } } if (width > height) { reposition_participant_widgets_rec(first_part, width / 2, height, margin_top, margin_right + width / 2, margin_bottom, margin_left); reposition_participant_widgets_rec(last_part, width / 2, height, margin_top, margin_right, margin_bottom, margin_left + width / 2); } else { reposition_participant_widgets_rec(first_part, width, height / 2, margin_top, margin_right, margin_bottom + height / 2, margin_left); reposition_participant_widgets_rec(last_part, width, height / 2, margin_top + height / 2, margin_right, margin_bottom, margin_left); } } public void set_own_video(Widget? widget_) { unset_own_video(); Widget? own_video = widget_; if (own_video == null) { own_video = new Box(Orientation.HORIZONTAL, 0); } own_video.hexpand = own_video.vexpand = true; own_video.visible = true; own_video_box.append(own_video); } public void set_own_video_ratio(int width, int height) { if (width / height > 150 / 100) { this.own_video_width = 150; this.own_video_height = height * 150 / width; } else { this.own_video_width = width * 100 / height; this.own_video_height = 100; } } public void unset_own_video() { Widget to_remove = own_video_box.get_first_child(); while (to_remove != null) { own_video_box.remove(to_remove); to_remove = own_video_box.get_first_child(); } } public void set_status(string participant_id, string state) { participant_widgets[participant_id].set_status(state); } public void show_counterpart_ended(string who_terminated, string? reason_name, string? reason_text) { hide_control_elements = false; reveal_control_elements(); string text = ""; if (reason_name == Xmpp.Xep.Jingle.ReasonElement.SUCCESS) { text = _("%s ended the call").printf(who_terminated); } else if (reason_name == Xmpp.Xep.Jingle.ReasonElement.DECLINE || reason_name == Xmpp.Xep.Jingle.ReasonElement.BUSY) { text = _("%s declined the call").printf(who_terminated); } else { if (reason_text == null) { text = "The call has been terminated" + " " + (reason_name ?? ""); } else { text = reason_text + " " + (reason_name ?? ""); } } bottom_bar.show_counterpart_ended(text); } private void reveal_control_elements() { if (!bottom_bar_revealer.child_revealed) { controls_active = true; } timeout_hide_control_elements(); } private void timeout_hide_control_elements() { if (hide_control_handler != 0) { Source.remove(hide_control_handler); hide_control_handler = 0; } if (!hide_control_elements) { return; } hide_control_handler = Timeout.add_seconds(3, () => { if (!hide_control_elements) { return false; } if (bottom_bar.is_menu_active()) { return false; } controls_active = false; hide_control_handler = 0; return false; }); } private bool on_get_child_position(Widget widget, out Gdk.Rectangle allocation) { allocation = Gdk.Rectangle(); if (widget == own_video_box) { int width = get_size(Orientation.HORIZONTAL); int height = get_size(Orientation.VERTICAL); allocation.width = own_video_width; allocation.height = own_video_height; allocation.x = width - own_video_width - 20; allocation.y = height - own_video_height - 20; return true; } return false; } } /* Hack to make the CallHeaderBar feel like a HeaderBar (right click menu, double click, ..) although it isn't set as headerbar. * OutsideHeaderBar is set as a headerbar and it doesn't take any space, but claims to take space (which is actually taken by CallHeaderBar). */ public class OutsideHeaderBar : Gtk.Box { HeaderBar header_bar; public OutsideHeaderBar(HeaderBar header_bar) { this.header_bar = header_bar; // size_allocate.connect_after(on_header_bar_size_allocate); // header_bar.size_allocate.connect(on_header_bar_size_allocate); } public void on_header_bar_size_allocate() { Allocation header_bar_alloc; header_bar.get_allocation(out header_bar_alloc); Allocation alloc; get_allocation(out alloc); alloc.height = header_bar_alloc.height; // set_allocation(alloc); } } }dino-0.5.0/main/src/ui/call_window/call_window_controller.vala0000664000000000000000000004650414776241610023232 0ustar rootrootusing Xmpp; using Gee; using Dino.Entities; using Gtk; public class Dino.Ui.CallWindowController : Object { private CallWindow call_window; private Call call; private CallState call_state; private StreamInteractor stream_interactor; private Calls calls; private Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; private Plugins.VideoCallWidget? own_video = null; private HashMap participant_videos = new HashMap(); private HashMap participant_widgets = new HashMap(); private HashMap peer_states = new HashMap(); private HashMap invite_handler_ids = new HashMap(); private int window_height = -1; private int window_width = -1; private bool window_size_changed = false; private ulong[] call_window_handler_ids = new ulong[0]; private ulong[] bottom_bar_handler_ids = new ulong[0]; private uint inhibit_cookie; public CallWindowController(CallWindow call_window, CallState call_state, StreamInteractor stream_interactor) { this.call_window = call_window; this.call = call_state.call; this.call_state = call_state; this.stream_interactor = stream_interactor; this.calls = stream_interactor.get_module(Calls.IDENTITY); this.own_video = call_plugin.create_widget(Plugins.WidgetType.GTK4); call_window.set_default_size(704, 528); // 640x480 * 1.1 this.call_window.bottom_bar.video_enabled = call_state.should_we_send_video(); call_state.terminated.connect((who_terminated, reason_name, reason_text) => { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(who_terminated.bare_jid, call.account, Conversation.Type.CHAT); string display_name = conversation != null ? Util.get_conversation_display_name(stream_interactor, conversation) : who_terminated.bare_jid.to_string(); call_window.show_counterpart_ended(display_name, reason_name, reason_text); Timeout.add_seconds(3, () => { call_window.close(); call_window.destroy(); return false; }); }); call_state.peer_joined.connect((jid, peer_state) => { connect_peer_signals(peer_state); add_new_participant(peer_state.internal_id, peer_state.jid); }); call_state.peer_left.connect((jid, peer_state, reason_name, reason_text) => { remove_participant(peer_state.internal_id); }); foreach (PeerState peer_state in call_state.peers.values) { connect_peer_signals(peer_state); add_new_participant(peer_state.internal_id, peer_state.jid); } // Call window signals bottom_bar_handler_ids += call_window.bottom_bar.hang_up.connect(() => { call_state.end(); call_window.close(); call_window.destroy(); this.dispose(); }); call_window_handler_ids += call_window.close_request.connect(() => { call_state.end(); this.dispose(); return false; }); bottom_bar_handler_ids += call_window.bottom_bar.notify["audio-enabled"].connect(() => { call_state.mute_own_audio(!call_window.bottom_bar.audio_enabled); }); bottom_bar_handler_ids += call_window.bottom_bar.notify["video-enabled"].connect(() => { call_state.mute_own_video(!call_window.bottom_bar.video_enabled); update_own_video(); }); call_window_handler_ids += call_window.notify["default-width"].connect((event) => { if (call_window.default_width == -1) return; int current_width = this.call_window.get_allocated_width(); if (window_width != current_width) { debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Width %i->%i", window_width, current_width); window_size_changed = true; } }); call_window_handler_ids += call_window.notify["default-height"].connect((event) => { if (call_window.default_height == -1) return; int current_height = this.call_window.get_allocated_height(); if (window_height != current_height) { debug("Call window size changed by user. Disabling auto window-to-video size adaptation. Height %i->%i", window_height, current_height); window_size_changed = true; } }); call_window_handler_ids += ((Widget)call_window).realize.connect(() => { capture_window_size(); }); calls.conference_info_received.connect((call, conference_info) => { if (!this.call.equals(call)) return; var participants = new ArrayList(); participants.add_all(participant_videos.keys); foreach (string participant in participants) { remove_participant(participant); } foreach (Jid participant in conference_info.users.keys) { add_new_participant(participant.to_string(), participant); } }); own_video.resolution_changed.connect((width, height) => { if (width == 0 || height == 0) return; call_window.set_own_video_ratio((int)width, (int)height); }); call_window.menu_dump_dot.connect(() => { call_plugin.dump_dot(); }); update_own_video(); update_audio_device_choices(); update_video_device_choices(); var app = GLib.Application.get_default() as Application; inhibit_cookie = app.inhibit(call_window, IDLE | SUSPEND, "Ongoing call"); if (inhibit_cookie == 0) { warning("suspend inhibit request failed or unsupported"); } call_window.close_request.connect(() => { if (inhibit_cookie != 0) { app.uninhibit(inhibit_cookie); } return false; }); } private void invite_button_clicked() { Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(call.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); add_chat_dialog.set_transient_for((Window) call_window.get_root()); add_chat_dialog.title = _("Invite to Call"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { call_state.invite_to_call.begin(jid); }); add_chat_dialog.present(); } private void connect_peer_signals(PeerState peer_state) { string peer_id = peer_state.internal_id; Jid peer_jid = peer_state.jid; peer_states[peer_id] = peer_state; peer_state.connection_ready.connect(() => { call_window.set_status(peer_id, ""); if (participant_widgets.size == 1) { // This is the first peer. // If it can do MUJI, show invite button. call_state.can_convert_into_groupcall.begin((_, res) => { bool can_convert = call_state.can_convert_into_groupcall.end(res); participant_widgets[peer_id].may_show_invite_button = can_convert; }); call_plugin.devices_changed.connect((media, incoming) => { if (media == "audio") update_audio_device_choices(); if (media == "video") update_video_device_choices(); }); update_audio_device_choices(); update_video_device_choices(); } else if (participant_widgets.size > 1) { participant_widgets.values.@foreach((widget) => widget.may_show_invite_button = true); } }); peer_state.counterpart_sends_video_updated.connect((mute) => { if (mute) { Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(peer_jid.bare_jid, call.account, Conversation.Type.CHAT); call_window.set_placeholder(peer_id, conversation, stream_interactor); participant_videos[peer_id].detach(); } else { if (!(participant_videos[peer_id] is Widget)) return; Widget widget = (Widget) participant_videos[peer_id]; call_window.set_video(peer_id, widget); participant_videos[peer_id].display_stream(peer_state.get_video_stream(), peer_jid); } }); peer_state.info_received.connect((session_info) => { if (session_info == Xmpp.Xep.JingleRtp.CallSessionInfo.RINGING) { call_window.set_status(peer_id, "ringing"); } }); peer_state.encryption_updated.connect((state, audio_encryption, video_encryption) => { update_encryption_indicator(participant_widgets[peer_id].encryption_button_controller, peer_states[peer_id].audio_content != null, audio_encryption, peer_states[peer_id].video_content != null, video_encryption); }); } private void update_encryption_indicator(CallEncryptionButtonController encryption_button, bool has_audio, Xep.Jingle.ContentEncryption? audio_encryption, bool has_video, Xep.Jingle.ContentEncryption? video_encryption) { string? title = null; string? icon_name = null; bool show_keys = true; Plugins.Registry registry = Dino.Application.get_default().plugin_registry; if (((has_audio && audio_encryption != null) || (has_video && video_encryption != null)) && (!has_audio || !has_video || (audio_encryption != null && video_encryption != null && audio_encryption.encryption_ns == video_encryption.encryption_ns))) { Plugins.CallEncryptionEntry? audio_encryption_entry = audio_encryption != null ? registry.call_encryption_entries[audio_encryption.encryption_ns] : null; Plugins.CallEncryptionEntry? video_encryption_entry = video_encryption != null ? registry.call_encryption_entries[video_encryption.encryption_ns] : null; Plugins.CallEncryptionWidget? audio_encryption_widgets = audio_encryption_entry != null ? audio_encryption_entry.get_widget(call.account, audio_encryption) : null; Plugins.CallEncryptionWidget? video_encryption_widgets = video_encryption_entry != null ? video_encryption_entry.get_widget(call.account, video_encryption) : null; if (audio_encryption_widgets != null && !has_video) { title = audio_encryption_widgets.get_title(); icon_name = audio_encryption_widgets.get_icon_name(); show_keys = audio_encryption_widgets.show_keys(); } else if (video_encryption_widgets != null && !has_audio) { title = video_encryption_widgets.get_title(); icon_name = video_encryption_widgets.get_icon_name(); show_keys = video_encryption_widgets.show_keys(); } else if (audio_encryption_widgets != null && video_encryption_widgets != null) { if (audio_encryption_widgets.get_title() == video_encryption_widgets.get_title()) title = audio_encryption_widgets.get_title(); if (audio_encryption_widgets.get_icon_name() == video_encryption_widgets.get_icon_name()) icon_name = audio_encryption_widgets.get_icon_name(); if (audio_encryption_widgets.show_keys() == video_encryption_widgets.show_keys()) show_keys = audio_encryption_widgets.show_keys(); } } encryption_button.set_info(title, show_keys, has_audio, audio_encryption, has_video, video_encryption); encryption_button.set_icon((!has_audio || audio_encryption != null) && (!has_video || video_encryption != null), icon_name); } private void add_new_participant(string participant_id, Jid jid) { if (participant_widgets.has_key(participant_id)) { warning("[%s] Attempted to add same participant twice: %s", call.account.bare_jid.to_string(), jid.to_string()); return; } debug("[%s] Call window controller | Add participant: %s", call.account.bare_jid.to_string(), jid.to_string()); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, call.account, Conversation.Type.CHAT); string participant_name = conversation != null ? Util.get_conversation_display_name(stream_interactor, conversation) : jid.bare_jid.to_string(); ParticipantWidget participant_widget = new ParticipantWidget(participant_name); participant_widget.may_show_invite_button = !participant_widgets.is_empty; participant_widget.debug_information_clicked.connect(() => { var conn_details_window = new CallConnectionDetailsWindow() { title=participant_name }; conn_details_window.update_content(peer_states[participant_id].get_info()); uint timeout_handle_id = Timeout.add_seconds(1, () => { conn_details_window.update_content(peer_states[participant_id].get_info()); return true; }); conn_details_window.set_transient_for(call_window); conn_details_window.close_request.connect(() => { Source.remove(timeout_handle_id); return false; }); conn_details_window.present(); this.call_window.close_request.connect(() => { conn_details_window.close(); return false; }); }); invite_handler_ids[participant_id] += participant_widget.invite_button_clicked.connect(() => invite_button_clicked()); participant_widgets[participant_id] = participant_widget; call_window.add_participant(participant_id, participant_widget); participant_videos[participant_id] = call_plugin.create_widget(Plugins.WidgetType.GTK4); participant_videos[participant_id].resolution_changed.connect((width, height) => { if (window_size_changed || participant_widgets.size > 1) return; if (width == 0 || height == 0) return; if (width > height) { call_window.default_width = 704; call_window.default_height = (int) (height * 704 / width); } else { call_window.default_width = (int) (width * 704 / height); call_window.default_height = 704; } capture_window_size(); }); participant_widget.set_placeholder(conversation, stream_interactor); if (call.direction == Call.DIRECTION_INCOMING) { call_window.set_status(participant_id, "establishing"); } else { call_window.set_status(participant_id, "requested"); } } private void remove_participant(string participant_id) { if (peer_states.has_key(participant_id)) debug(@"[%s] Call window controller | Remove participant: %s", call.account.bare_jid.to_string(), peer_states[participant_id].jid.to_string()); participant_videos.unset(participant_id); participant_widgets[participant_id].disconnect(invite_handler_ids[participant_id]); participant_widgets.unset(participant_id); invite_handler_ids.unset(participant_id); peer_states.unset(participant_id); call_window.remove_participant(participant_id); } private void capture_window_size() { Allocation allocation; this.call_window.get_allocation(out allocation); this.window_height = this.call_window.get_allocated_height(); this.window_width = this.call_window.get_allocated_width(); } private void update_audio_device_choices() { if (call_plugin.get_devices("audio", true).size == 0 || call_plugin.get_devices("audio", false).size == 0) { call_window.bottom_bar.show_audio_device_error(); } else if (call_plugin.get_devices("audio", true).size == 1 && call_plugin.get_devices("audio", false).size == 1) { call_window.bottom_bar.show_audio_device_choices(false); return; } AudioSettingsPopover? audio_settings_popover = call_window.bottom_bar.show_audio_device_choices(true); update_current_audio_device(audio_settings_popover); audio_settings_popover.microphone_selected.connect((device) => { call_state.set_audio_device(device); update_current_audio_device(audio_settings_popover); }); audio_settings_popover.speaker_selected.connect((device) => { call_state.set_audio_device(device); update_current_audio_device(audio_settings_popover); }); } private void update_current_audio_device(AudioSettingsPopover audio_settings_popover) { audio_settings_popover.current_microphone_device = call_state.get_microphone_device(); audio_settings_popover.current_speaker_device = call_state.get_speaker_device(); } private void update_video_device_choices() { int device_count = call_plugin.get_devices("video", false).size; if (device_count == 0) { call_window.bottom_bar.show_video_device_error(); } else if (device_count == 1 || call_state.get_video_device() == null) { call_window.bottom_bar.show_video_device_choices(false); return; } VideoSettingsPopover? video_settings_popover = call_window.bottom_bar.show_video_device_choices(true); update_current_video_device(video_settings_popover); video_settings_popover.camera_selected.connect((device) => { call_state.set_video_device(device); update_current_video_device(video_settings_popover); own_video.display_device(device); }); } private void update_current_video_device(VideoSettingsPopover video_settings_popover) { video_settings_popover.current_device = call_state.get_video_device(); } private void update_own_video() { if (this.call_window.bottom_bar.video_enabled) { Gee.List devices = call_plugin.get_devices("video", false); if (!(own_video is Widget) || devices.is_empty) { call_window.set_own_video(null); } else { Widget widget = (Widget) own_video; call_window.set_own_video(widget); own_video.display_device(call_state.get_video_device()); } } else { own_video.detach(); call_window.unset_own_video(); } } public override void dispose() { foreach (ulong handler_id in call_window_handler_ids) call_window.disconnect(handler_id); foreach (ulong handler_id in bottom_bar_handler_ids) call_window.bottom_bar.disconnect(handler_id); var participant_ids = new ArrayList(); participant_ids.add_all(participant_widgets.keys); foreach (string participant_id in participant_ids) { remove_participant(participant_id); } call_window_handler_ids = bottom_bar_handler_ids = new ulong[0]; if (own_video != null) { if (this.call_window.bottom_bar.video_enabled) { own_video.detach(); call_window.unset_own_video(); } own_video = null; } base.dispose(); } } dino-0.5.0/main/src/ui/call_window/participant_widget.vala0000664000000000000000000001401014776241610022331 0ustar rootrootusing Pango; using Gee; using Xmpp; using Dino.Entities; using Gtk; namespace Dino.Ui { public class ParticipantWidget : Box { public Overlay overlay = new Overlay(); public Widget main_widget; public HeaderBar header_bar = new HeaderBar() { valign=Align.START }; public Label title_label = new Label(""); public Label subtitle_label = new Label(""); public Box inner_box = new Box(Orientation.HORIZONTAL, 0) { margin_start=5, margin_top=5, hexpand=true }; public Box title_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER, hexpand=true }; public MenuButton encryption_button = new MenuButton() { opacity=0, has_frame=false, height_request=30, width_request=30, margin_end=5 }; public CallEncryptionButtonController encryption_button_controller; public MenuButton menu_button = new MenuButton() { icon_name="view-more-symbolic", has_frame=false }; public Button invite_button = new Button.from_icon_name("contact-new-symbolic") { has_frame=false }; public bool shows_video = false; public string? participant_name; bool is_highest_row = false; bool is_start_row = false; public bool controls_active { get; set; } public bool may_show_invite_button { get; set; } public signal void debug_information_clicked(); public signal void invite_button_clicked(); class construct { install_action("menu.debuginfo", null, (widget, action_name) => { ((ParticipantWidget) widget).debug_information_clicked(); }); } public ParticipantWidget(string participant_name) { encryption_button_controller = new CallEncryptionButtonController(encryption_button); this.participant_name = participant_name; Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; titles_box.add_css_class("titles"); title_label.attributes = new AttrList(); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); header_bar.set_title_widget(titles_box); title_label.label = participant_name; header_bar.add_css_class("participant-header-bar"); header_bar.pack_start(invite_button); header_bar.pack_start(encryption_button); header_bar.pack_end(menu_button); create_menu(); invite_button.clicked.connect(() => invite_button_clicked()); this.append(overlay); overlay.add_overlay(header_bar); this.notify["controls-active"].connect(reveal_or_hide_controls); this.notify["may-show-invite-button"].connect(reveal_or_hide_controls); } public void on_row_changed(bool is_highest, bool is_lowest, bool is_start, bool is_end) { this.is_highest_row = is_highest; this.is_start_row = is_start; header_bar.show_title_buttons = is_highest_row; if (is_highest_row) { Gtk.Settings? gtk_settings = Gtk.Settings.get_default(); if (gtk_settings != null) { string[] buttons = gtk_settings.gtk_decoration_layout.split(":"); header_bar.decoration_layout = (is_start ? buttons[0] : "") + ":" + (is_end && buttons.length == 2 ? buttons[1] : ""); } } reveal_or_hide_controls(); } public void set_video(Widget widget) { shows_video = true; widget.visible = true; set_participant_widget(widget); } public void set_placeholder(Conversation? conversation, StreamInteractor stream_interactor) { shows_video = false; Box box = new Box(Orientation.HORIZONTAL, 0); box.add_css_class("video-placeholder-box"); AvatarPicture avatar = new AvatarPicture() { hexpand=true, vexpand=true, halign=Align.CENTER, valign=Align.CENTER, height_request=100, width_request=100 }; if (conversation != null) { avatar.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(conversation); } else { avatar.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add("?"); } box.append(avatar); set_participant_widget(box); } private void set_participant_widget(Widget widget) { widget.hexpand = widget.vexpand = true; main_widget = widget; overlay.set_child(main_widget); } private void create_menu() { Menu menu_model = new Menu(); menu_model.append(_("Debug information"), "menu.debuginfo"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); menu_button.popover = popover_menu; } public void set_status(string state) { subtitle_label.visible = true; if (state == "requested") { subtitle_label.label = _("Calling…"); } else if (state == "ringing") { subtitle_label.label = _("Ringing…"); } else if (state == "establishing") { subtitle_label.label = _("Connecting…"); } else { subtitle_label.visible = false; } } public bool is_menu_active() { return false; } private void reveal_or_hide_controls() { header_bar.opacity = controls_active ? 1.0 : 0.0; invite_button.visible = may_show_invite_button && is_highest_row && is_start_row; } public override void dispose() { main_widget = null; overlay = null; base.dispose(); } } } dino-0.5.0/main/src/ui/call_window/video_settings_popover.vala0000664000000000000000000000666014776241610023264 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; public class Dino.Ui.VideoSettingsPopover : Gtk.Popover { public signal void camera_selected(Plugins.MediaDevice device); public Plugins.MediaDevice? current_device { get; set; } private HashMap row_device = new HashMap(); public VideoSettingsPopover() { Box box = new Box(Orientation.VERTICAL, 15); box.append(create_camera_box()); this.set_child(box); } private Widget create_camera_box() { Plugins.VideoCallPlugin call_plugin = Dino.Application.get_default().plugin_registry.video_call_plugin; Gee.List devices = call_plugin.get_devices("video", false); Box camera_box = new Box(Orientation.VERTICAL, 10); camera_box.append(new Label("" + _("Cameras") + "") { use_markup=true, xalign=0, can_focus=true /* grab initial focus*/ }); if (devices.size == 0) { camera_box.append(new Label(_("No camera found."))); } else { ListBox list_box = new ListBox() { activate_on_single_click=true, selection_mode=SelectionMode.SINGLE }; list_box.set_header_func(listbox_header_func); Frame frame = new Frame(null); frame.set_child(list_box); foreach (Plugins.MediaDevice device in devices) { Label display_name_label = new Label(device.display_name) { xalign=0 }; Image image = new Image.from_icon_name("object-select-symbolic"); if (current_device == null || current_device.id != device.id) { image.opacity = 0; } this.notify["current-device"].connect(() => { if (current_device == null || current_device.id != device.id) { image.opacity = 0; } else { image.opacity = 1; } }); Box device_box = new Box(Orientation.HORIZONTAL, 0) { spacing=7 }; device_box.append(image); Box label_box = new Box(Orientation.VERTICAL, 0) { visible = true }; label_box.append(display_name_label); if (device.detail_name != null) { Label detail_name_label = new Label(device.detail_name) { xalign=0 }; detail_name_label.add_css_class("dim-label"); detail_name_label.attributes = new Pango.AttrList(); detail_name_label.attributes.insert(Pango.attr_scale_new(0.8)); label_box.append(detail_name_label); } device_box.append(label_box); ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(device_box); list_box.append(list_box_row); row_device[list_box_row] = device; } list_box.row_activated.connect((row) => { if (!row_device.has_key(row)) return; camera_selected(row_device[row]); list_box.unselect_row(row); }); camera_box.append(frame); } return camera_box; } private void listbox_header_func(ListBoxRow row, ListBoxRow? before_row) { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } } }dino-0.5.0/main/src/ui/chat_input/0000775000000000000000000000000014776241610015443 5ustar rootrootdino-0.5.0/main/src/ui/chat_input/chat_input_controller.vala0000664000000000000000000002532414776241610022717 0ustar rootrootusing Gee; using Gdk; using Gtk; using Xmpp; using Xmpp; using Dino.Entities; namespace Dino.Ui { private const string OPEN_CONVERSATION_DETAILS_URI = "x-dino:open-conversation-details"; public class ChatInputController : Object { public signal void activate_last_message_correction(); public signal void file_picker_selected(); public signal void clipboard_pasted(); public new string? conversation_display_name { get; set; } public string? conversation_topic { get; set; } private Conversation? conversation; private ChatInput.View chat_input; private Label status_description_label; private StreamInteractor stream_interactor; private Plugins.InputFieldStatus input_field_status; private ChatTextViewController chat_text_view_controller; private ContentItem? quoted_content_item = null; public ChatInputController(ChatInput.View chat_input, StreamInteractor stream_interactor) { this.chat_input = chat_input; this.status_description_label = chat_input.chat_input_status; this.stream_interactor = stream_interactor; this.chat_text_view_controller = new ChatTextViewController(chat_input.chat_text_view, stream_interactor); chat_input.init(stream_interactor); reset_input_field_status(); var text_input_key_events = new EventControllerKey() { name = "dino-text-input-controller-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); chat_input.chat_text_view.text_view.add_controller(text_input_key_events); chat_input.chat_text_view.text_view.paste_clipboard.connect(() => clipboard_pasted()); chat_input.chat_text_view.text_view.buffer.changed.connect(on_text_input_changed); chat_text_view_controller.send_text.connect(send_text); chat_input.encryption_widget.encryption_changed.connect(on_encryption_changed); chat_input.file_button.clicked.connect(() => file_picker_selected()); stream_interactor.get_module(MucManager.IDENTITY).received_occupant_role.connect(update_moderated_input_status); stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect(update_moderated_input_status); status_description_label.activate_link.connect((uri) => { if (uri == OPEN_CONVERSATION_DETAILS_URI){ var conversation_details = ConversationDetails.setup_dialog(conversation, stream_interactor, (Window)chat_input.get_root()); conversation_details.present(); } return true; }); SimpleAction quote_action = new SimpleAction("quote", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); quote_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null || !this.conversation.equals(conversation)) return; int content_item_id = variant.get_child_value(1).get_int32(); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, content_item_id); if (content_item == null) return; quoted_content_item = content_item; var quote_model = new Quote.Model.from_content_item(content_item, conversation, stream_interactor) { can_abort = true }; quote_model.aborted.connect(() => { quoted_content_item = null; chat_input.unset_quoted_message(); }); chat_input.set_quoted_message(Quote.get_widget(quote_model)); }); GLib.Application.get_default().add_action(quote_action); } public void set_conversation(Conversation conversation) { reset_input_field_status(); this.quoted_content_item = null; chat_input.unset_quoted_message(); this.conversation = conversation; chat_input.encryption_widget.set_conversation(conversation); chat_input.initialize_for_conversation(conversation); chat_text_view_controller.initialize_for_conversation(conversation); update_moderated_input_status(conversation.account); } public void set_file_upload_active(bool active) { chat_input.set_file_upload_active(active); } private void on_encryption_changed(Encryption encryption) { reset_input_field_status(); if (encryption == Encryption.NONE) return; Application app = GLib.Application.get_default() as Application; var encryption_entry = app.plugin_registry.encryption_list_entries[encryption]; encryption_entry.encryption_activated(conversation, set_input_field_status); } private void set_input_field_status(Plugins.InputFieldStatus? status) { input_field_status = status; chat_input.set_input_state(status.message_type); status_description_label.use_markup = status.contains_markup; status_description_label.label = status.message; chat_input.file_button.sensitive = status.input_state == Plugins.InputFieldStatus.InputState.NORMAL; } private void reset_input_field_status() { set_input_field_status(new Plugins.InputFieldStatus("", Plugins.InputFieldStatus.MessageType.NONE, Plugins.InputFieldStatus.InputState.NORMAL)); } private void send_text() { // Don't do anything if we're in a NO_SEND state. Don't clear the chat input, don't send. if (input_field_status.input_state == Plugins.InputFieldStatus.InputState.NO_SEND) { chat_input.highlight_state_description(); return; } string text = chat_input.chat_text_view.text_view.buffer.text; ContentItem? quoted_content_item_bak = quoted_content_item; var markups = chat_input.chat_text_view.get_markups(); // Reset input state. Has do be done before parsing commands, because those directly return. chat_input.chat_text_view.text_view.buffer.text = ""; chat_input.unset_quoted_message(); quoted_content_item = null; if (text.has_prefix("/")) { string[] token = text.split(" ", 2); switch(token[0]) { case "/me": // Just send as is. break; case "/say": if (token.length == 1) return; text = token[1]; break; case "/kick": stream_interactor.get_module(MucManager.IDENTITY).kick(conversation.account, conversation.counterpart, token[1]); return; case "/affiliate": if (token.length > 1) { string[] user_role = token[1].split(" "); if (user_role.length >= 2) { string nick = string.joinv(" ", user_role[0:user_role.length - 1]).strip(); string role = user_role[user_role.length - 1].strip(); stream_interactor.get_module(MucManager.IDENTITY).change_affiliation(conversation.account, conversation.counterpart, nick, role); } } return; case "/nick": stream_interactor.get_module(MucManager.IDENTITY).change_nick.begin(conversation, token[1]); return; case "/ping": Xmpp.XmppStream? stream = stream_interactor.get_stream(conversation.account); try { stream.get_module(Xmpp.Xep.Ping.Module.IDENTITY).send_ping.begin(stream, conversation.counterpart.with_resource(token[1])); } catch (Xmpp.InvalidJidError e) { warning("Could not ping invalid Jid: %s", e.message); } return; case "/topic": stream_interactor.get_module(MucManager.IDENTITY).change_subject(conversation.account, conversation.counterpart, token[1]); return; default: if (token[0].has_prefix("//")) { text = text.substring(1); } else { string cmd_name = token[0].substring(1); Dino.Application app = GLib.Application.get_default() as Dino.Application; if (app != null && app.plugin_registry.text_commands.has_key(cmd_name)) { string? new_text = app.plugin_registry.text_commands[cmd_name].handle_command(token[1], conversation); if (new_text == null) return; text = (!)new_text; } } break; } } Dino.send_message(conversation, text, quoted_content_item_bak != null ? quoted_content_item_bak.id : 0, null, markups); } private void on_text_input_changed() { if (chat_input.chat_text_view.text_view.buffer.text != "") { stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_entered(conversation); } else { stream_interactor.get_module(ChatInteraction.IDENTITY).on_message_cleared(conversation); } } private void update_moderated_input_status(Account account, Xmpp.Jid? jid = null) { if (conversation != null && conversation.type_ == Conversation.Type.GROUPCHAT){ Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (own_jid == null) return; if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) { string msg_str = _("This conference does not allow you to send messages.") + " " + _("Request permission") + ""; set_input_field_status(new Plugins.InputFieldStatus(msg_str, Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND, true)); } else { reset_input_field_status(); } } } private bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval == Gdk.Key.Up && chat_input.chat_text_view.text_view.buffer.text == "") { activate_last_message_correction(); return true; } else { chat_input.do_focus(); } return false; } } } dino-0.5.0/main/src/ui/chat_input/chat_text_view.vala0000664000000000000000000002314514776241610021332 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class ChatTextViewController : Object { public signal void send_text(); public OccupantsTabCompletor occupants_tab_completor; private ChatTextView widget; public ChatTextViewController(ChatTextView widget, StreamInteractor stream_interactor) { this.widget = widget; occupants_tab_completor = new OccupantsTabCompletor(stream_interactor, widget.text_view); widget.send_text.connect(() => { send_text(); }); } public void initialize_for_conversation(Conversation conversation) { occupants_tab_completor.initialize_for_conversation(conversation); } } public class ChatTextView : Box { public signal void send_text(); public signal void cancel_input(); public ScrolledWindow scrolled_window = new ScrolledWindow() { propagate_natural_height=true, max_content_height=300, hexpand=true }; public TextView text_view = new TextView() { hexpand=true, wrap_mode=Gtk.WrapMode.WORD_CHAR, valign=Align.CENTER, margin_top=7, margin_bottom=7 }; private int vscrollbar_min_height; private uint wait_queue_resize; private SmileyConverter smiley_converter; private TextTag italic_tag; private TextTag bold_tag; private TextTag strikethrough_tag; construct { valign = Align.CENTER; scrolled_window.set_child(text_view); this.append(scrolled_window); var text_input_key_events = new EventControllerKey() { name = "dino-text-input-view-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); text_view.add_controller(text_input_key_events); italic_tag = text_view.buffer.create_tag("italic"); italic_tag.style = Pango.Style.ITALIC; bold_tag = text_view.buffer.create_tag("bold"); bold_tag.weight = Pango.Weight.BOLD; strikethrough_tag = text_view.buffer.create_tag("strikethrough"); strikethrough_tag.strikethrough = true; smiley_converter = new SmileyConverter(text_view); scrolled_window.vadjustment.changed.connect(on_upper_notify); text_view.realize.connect(() => { var minimum_size = Requisition(); scrolled_window.get_preferred_size(out minimum_size, null); vscrollbar_min_height = minimum_size.height; }); } public void set_text(Message message) { // Get a copy of the markup spans, such that we can modify them var markups = new ArrayList(); foreach (var markup in message.get_markups()) { markups.add(new Xep.MessageMarkup.Span() { types=markup.types, start_char=markup.start_char, end_char=markup.end_char }); } text_view.buffer.text = Util.remove_fallbacks_adjust_markups(message.body, message.quoted_item_id > 0, message.get_fallbacks(), markups); foreach (var markup in markups) { foreach (var ty in markup.types) { TextTag tag = null; switch (ty) { case Xep.MessageMarkup.SpanType.EMPHASIS: tag = italic_tag; break; case Xep.MessageMarkup.SpanType.STRONG_EMPHASIS: tag = bold_tag; break; case Xep.MessageMarkup.SpanType.DELETED: tag = strikethrough_tag; break; } TextIter start_selection, end_selection; text_view.buffer.get_iter_at_offset(out start_selection, markup.start_char); text_view.buffer.get_iter_at_offset(out end_selection, markup.end_char); text_view.buffer.apply_tag(tag, start_selection, end_selection); } } } public override void dispose() { base.dispose(); if (wait_queue_resize != 0) { Source.remove(wait_queue_resize); wait_queue_resize = 0; } } private void on_upper_notify() { // hack. otherwise the textview would only show the last row(s) when entering a new row on some systems. scrolled_window.height_request = int.min(scrolled_window.max_content_height, (int) scrolled_window.vadjustment.upper + text_view.margin_top + text_view.margin_bottom); scrolled_window.vadjustment.page_size = double.min(scrolled_window.height_request - (text_view.margin_top + text_view.margin_bottom), scrolled_window.vadjustment.upper); // hack for vscrollbar not requiring space and making textview higher //TODO doesn't resize immediately scrolled_window.get_vscrollbar().visible = (scrolled_window.vadjustment.upper > scrolled_window.max_content_height - 2 * this.vscrollbar_min_height); start_queue_resize_if_needed(); } private void start_queue_resize_if_needed() { if (wait_queue_resize == 0) { wait_queue_resize = Timeout.add(100, queue_resize_if_needed); } } private bool queue_resize_if_needed() { if (scrolled_window.get_height() == scrolled_window.height_request) { wait_queue_resize = 0; return false; } else { queue_resize(); return true; } } private bool on_text_input_key_press(EventControllerKey controller, uint keyval, uint keycode, Gdk.ModifierType state) { // Enter pressed -> Send message (except if it was Shift+Enter) if (keyval in new uint[]{ Key.Return, Key.KP_Enter }) { // Allow the text view to process the event. Needed for IME. if (text_view.im_context_filter_keypress(controller.get_current_event())) { return true; } if ((state & ModifierType.SHIFT_MASK) > 0) { // Let the default handler normally insert a newline if shift was hold return false; } else if (text_view.buffer.text.strip() != "") { send_text(); } return true; } if (keyval == Key.Escape) { cancel_input(); } // Style text section bold (CTRL + b) or italic (CTRL + i) if ((state & ModifierType.CONTROL_MASK) > 0) { if (keyval in new uint[]{ Key.i, Key.b }) { TextIter start_selection, end_selection; text_view.buffer.get_selection_bounds(out start_selection, out end_selection); TextTag tag = null; bool already_formatted = false; var markup_types = get_markup_types_from_iter(start_selection); if (keyval == Key.i) { tag = italic_tag; already_formatted = markup_types.contains(Xep.MessageMarkup.SpanType.EMPHASIS); } else if (keyval == Key.b) { tag = bold_tag; already_formatted = markup_types.contains(Xep.MessageMarkup.SpanType.STRONG_EMPHASIS); } else if (keyval == Key.s) { tag = strikethrough_tag; already_formatted = markup_types.contains(Xep.MessageMarkup.SpanType.DELETED); } if (tag != null) { if (already_formatted) { text_view.buffer.remove_tag(tag, start_selection, end_selection); } else { text_view.buffer.apply_tag(tag, start_selection, end_selection); } } } } return false; } public Gee.List get_markups() { var markups = new HashMap(); markups[Xep.MessageMarkup.SpanType.EMPHASIS] = Xep.MessageMarkup.SpanType.EMPHASIS; markups[Xep.MessageMarkup.SpanType.STRONG_EMPHASIS] = Xep.MessageMarkup.SpanType.STRONG_EMPHASIS; markups[Xep.MessageMarkup.SpanType.DELETED] = Xep.MessageMarkup.SpanType.DELETED; var ended_groups = new ArrayList(); Xep.MessageMarkup.Span current_span = null; TextIter iter; text_view.buffer.get_start_iter(out iter); int i = 0; do { var char_markups = get_markup_types_from_iter(iter); // Not the same set of markups as last character -> end all spans if (current_span != null && (!char_markups.contains_all(current_span.types) || !current_span.types.contains_all(char_markups))) { ended_groups.add(current_span); current_span = null; } if (char_markups.size > 0) { if (current_span == null) { current_span = new Xep.MessageMarkup.Span() { types=char_markups, start_char=i, end_char=i + 1 }; } else { current_span.end_char = i + 1; } } i++; } while (iter.forward_char()); if (current_span != null) { ended_groups.add(current_span); } return ended_groups; } private Gee.List get_markup_types_from_iter(TextIter iter) { var ret = new ArrayList(); foreach (TextTag tag in iter.get_tags()) { if (tag.style == Pango.Style.ITALIC) { ret.add(Xep.MessageMarkup.SpanType.EMPHASIS); } else if (tag.weight == Pango.Weight.BOLD) { ret.add(Xep.MessageMarkup.SpanType.STRONG_EMPHASIS); } else if (tag.strikethrough) { ret.add(Xep.MessageMarkup.SpanType.DELETED); } } return ret; } } } dino-0.5.0/main/src/ui/chat_input/encryption_button.vala0000664000000000000000000001055614776241610022104 0ustar rootrootusing Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class EncryptionButton { public signal void encryption_changed(Encryption encryption); private MenuButton menu_button; private Conversation? conversation; private string? current_icon; private StreamInteractor stream_interactor; private SimpleAction action; ulong conversation_encryption_handler_id = -1; public EncryptionButton(StreamInteractor stream_interactor, MenuButton menu_button) { this.stream_interactor = stream_interactor; this.menu_button = menu_button; // Build menu model including "Unencrypted" and all registered encryption entries Menu menu_model = new Menu(); MenuItem unencrypted_item = new MenuItem(_("Unencrypted"), "enc.encryption"); unencrypted_item.set_action_and_target_value("enc.encryption", new Variant.int32(Encryption.NONE)); menu_model.append_item(unencrypted_item); var encryption_entries = new ArrayList(); Application app = GLib.Application.get_default() as Application; encryption_entries.add_all(app.plugin_registry.encryption_list_entries.values); encryption_entries.sort((a,b) => b.name.collate(a.name)); foreach (var e in encryption_entries) { MenuItem item = new MenuItem(e.name, "enc.encryption"); item.set_action_and_target_value("enc.encryption", new Variant.int32(e.encryption)); menu_model.append_item(item); } // Create action to act on menu selections (stateful => radio buttons) SimpleActionGroup action_group = new SimpleActionGroup(); action = new SimpleAction.stateful("encryption", VariantType.INT32, new Variant.int32(Encryption.NONE)); action.activate.connect((parameter) => { action.set_state(parameter); conversation.encryption = (Encryption) parameter.get_int32(); encryption_changed(conversation.encryption); }); action_group.insert(action); menu_button.insert_action_group("enc", action_group); // Create and set popover menu Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); menu_button.popover = popover_menu; stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, muc_jid) => { if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(muc_jid)) { update_visibility(); } }); } private void update_encryption_menu_state() { action.set_state(new Variant.int32(conversation.encryption)); action.change_state(new Variant.int32(conversation.encryption)); } private void set_icon(string icon) { if (icon != current_icon) { menu_button.set_icon_name(icon); current_icon = icon; } } private void update_encryption_menu_icon() { set_icon(conversation.encryption == Encryption.NONE ? "changes-allow-symbolic" : "changes-prevent-symbolic"); } private void update_visibility() { if (conversation.encryption != Encryption.NONE) { menu_button.visible = true; return; } switch (conversation.type_) { case Conversation.Type.CHAT: menu_button.visible = true; break; case Conversation.Type.GROUPCHAT_PM: menu_button.visible = false; break; case Conversation.Type.GROUPCHAT: menu_button.visible = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart); break; } } public void set_conversation(Conversation conversation) { if (conversation_encryption_handler_id != -1 && this.conversation != null) { this.conversation.disconnect(conversation_encryption_handler_id); } this.conversation = conversation; update_encryption_menu_state(); update_encryption_menu_icon(); update_visibility(); encryption_changed(this.conversation.encryption); conversation_encryption_handler_id = conversation.notify["encryption"].connect(() => { update_encryption_menu_state(); update_encryption_menu_icon(); }); } } }dino-0.5.0/main/src/ui/chat_input/occupants_tab_completer.vala0000664000000000000000000001204114776241610023205 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui { /** * - With given prefix: Complete from occupant list (sorted lexicographically) * - W/o prefix: Complete from received messages (most recent first) * - At the start (with ",") and in the middle of a text * - Backwards tabbing */ public class OccupantsTabCompletor { private StreamInteractor stream_interactor; private Conversation? conversation; private TextView text_input; private Gee.List completions = new ArrayList(); private bool active = false; private int index = -1; public OccupantsTabCompletor(StreamInteractor stream_interactor, TextView text_input) { this.stream_interactor = stream_interactor; this.text_input = text_input; var text_input_key_events = new EventControllerKey(); text_input_key_events.key_pressed.connect(on_text_input_key_press); text_input.add_controller(text_input_key_events); } public void initialize_for_conversation(Conversation conversation) { this.conversation = conversation; } public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (conversation.type_ == Conversation.Type.GROUPCHAT) { if (keyval == Key.Tab || keyval == Key.ISO_Left_Tab) { string text = text_input.buffer.text; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; string word = text.substring(start_index); if (!active) { if (word == "") { completions = generate_completions_from_messages(); } else { completions = generate_completions_from_occupants(word); } if (completions.size > 0) { active = true; index = -1; } } if (keyval != Key.ISO_Group_Shift && active) { text_input.buffer.text = next_completion(keyval == Key.ISO_Left_Tab); return true; } } else if (keyval != Key.Shift_L && active) { active = false; } } return false; } private string next_completion(bool backwards) { string text = text_input.buffer.text; int start_index = int.max(text.last_index_of(" "), text.last_index_of("\n")) + 1; string prev_completion = text.substring(start_index); if (index > -1) { start_index = int.max( text.last_index_of(completions[index]), text.substring(0, text.length - 1).last_index_of("\n") ); prev_completion = text.substring(start_index); } if (backwards) { index = int.max(index, 0) - 1; if (index < 0) index = completions.size - 1; } else { index = (index + 1) % (completions.size); } if (start_index == 0) { return completions[index] + ", "; } else { return text.substring(0, text.length - prev_completion.length) + completions[index] + " "; } } private Gee.List generate_completions_from_messages(string? prefix = null) { Gee.List ret = new ArrayList(); Gee.List content_items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_n_latest(conversation, 10); for (int i = content_items.size - 1; i > 0; i--) { string resourcepart = content_items[i].jid.resourcepart; Jid? own_nick = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (resourcepart != null && resourcepart != "" && (own_nick == null || resourcepart != own_nick.resourcepart) && !ret.contains(resourcepart)) { if (prefix != null && !resourcepart.to_string().down().has_prefix(prefix.down())) continue; ret.add(resourcepart); } } return ret; } private Gee.List generate_completions_from_occupants(string prefix) { // First suggest nicks that have recently writen something Gee.List ret = generate_completions_from_messages(prefix); // Then, suggest other nicks in alphabetical order Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_other_occupants(conversation.counterpart, conversation.account); Gee.List filtered_occupants = new ArrayList(); if (occupants != null) { foreach (Jid jid in occupants) { string resourcepart = jid.resourcepart.to_string(); if (resourcepart.down().has_prefix(prefix.down()) && !ret.contains(resourcepart)) { filtered_occupants.add(resourcepart); } } } filtered_occupants.sort(); ret.add_all(filtered_occupants); return ret; } } } dino-0.5.0/main/src/ui/chat_input/smiley_converter.vala0000664000000000000000000000537214776241610021710 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { class SmileyConverter { private TextView text_input; private static HashMap smiley_translations = new HashMap(); private Regex whitespace_regex = /\s/; static construct { smiley_translations[":)"] = "🙂"; smiley_translations[":D"] = "😀"; smiley_translations[";)"] = "😉"; smiley_translations["O:)"] = "😇"; smiley_translations["O:-)"] = "😇"; smiley_translations["]:>"] = "😈"; smiley_translations[":o"] = "😮"; smiley_translations[":P"] = "😛"; smiley_translations[";P"] = "😜"; smiley_translations[":("] = "🙁"; smiley_translations[":'("] = "😢"; smiley_translations[":/"] = "😕"; smiley_translations["<3"] = "❤️"; smiley_translations[":*"] = "😘️"; smiley_translations[":-*"] = "😘️"; } public SmileyConverter(TextView text_input) { this.text_input = text_input; var text_input_key_events = new EventControllerKey() { name = "dino-smiley-converter-key-events" }; text_input_key_events.key_pressed.connect(on_text_input_key_press); text_input.add_controller(text_input_key_events); } public bool on_text_input_key_press(uint keyval, uint keycode, Gdk.ModifierType state) { if (keyval == Key.space || keyval == Key.Return) { check_convert(); } return false; } private void check_convert() { if (!Dino.Application.get_default().settings.convert_utf8_smileys) return; TextIter? start_iter; text_input.buffer.get_start_iter(out start_iter); TextIter? cursor_iter; text_input.buffer.get_iter_at_mark(out cursor_iter, text_input.buffer.get_insert()); if (start_iter == null || cursor_iter == null) return; string text = text_input.buffer.get_text(start_iter, cursor_iter, true); foreach (string smiley in smiley_translations.keys) { if (text.has_suffix(smiley)) { if (text.length == smiley.length || whitespace_regex.match(text[text.length - smiley.length - 1].to_string())) { TextIter? end_iter; text_input.buffer.get_end_iter(out end_iter); if (end_iter == null) continue; TextIter smiley_start_iter = cursor_iter; smiley_start_iter.backward_chars(smiley.length); text_input.buffer.delete(ref smiley_start_iter, ref cursor_iter); text_input.buffer.insert_text(ref cursor_iter, smiley_translations[smiley], smiley_translations[smiley].length); } } } } } } dino-0.5.0/main/src/ui/chat_input/view.vala0000664000000000000000000000776614776241610017302 0ustar rootrootusing Gdk; using Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ChatInput { [GtkTemplate (ui = "/im/dino/Dino/chat_input.ui")] public class View : Box { public string text { owned get { return chat_text_view.text_view.buffer.text; } set { chat_text_view.text_view.buffer.text = value; } } private StreamInteractor stream_interactor; private Conversation? conversation; private HashMap entry_cache = new HashMap(Conversation.hash_func, Conversation.equals_func); [GtkChild] public unowned Frame frame; [GtkChild] public unowned Box quote_box; [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Button file_button; [GtkChild] public unowned MenuButton emoji_button; [GtkChild] public unowned MenuButton encryption_button; [GtkChild] public unowned Separator file_separator; [GtkChild] public unowned Label chat_input_status; public EncryptionButton encryption_widget; public View init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; encryption_widget = new EncryptionButton(stream_interactor, encryption_button); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length); }); chooser.closed.connect(do_focus); emoji_button.set_popover(chooser); file_button.tooltip_text = Util.string_if_tooltips_active(_("Send a file")); Util.force_css(frame, "* { border-radius: 3px; }"); return this; } public void set_file_upload_active(bool active) { file_button.visible = active; file_separator.visible = active; } public void initialize_for_conversation(Conversation conversation) { if (this.conversation != null) entry_cache[this.conversation] = chat_text_view.text_view.buffer.text; this.conversation = conversation; chat_text_view.text_view.buffer.text = ""; if (entry_cache.has_key(conversation)) { chat_text_view.text_view.buffer.text = entry_cache[conversation]; } do_focus(); } public void set_input_state(Plugins.InputFieldStatus.MessageType message_type) { switch (message_type) { case Plugins.InputFieldStatus.MessageType.NONE: this.remove_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.INFO: this.remove_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.WARNING: this.add_css_class("dino-input-warning"); this.remove_css_class("dino-input-error"); break; case Plugins.InputFieldStatus.MessageType.ERROR: this.remove_css_class("dino-input-warning"); this.add_css_class("dino-input-error"); break; } } public void highlight_state_description() { chat_input_status.add_css_class("input-status-highlight-once"); Timeout.add(500, () => { chat_input_status.remove_css_class("input-status-highlight-once"); return false; }); } public void set_quoted_message(Widget quote_widget) { Widget? quote_box_child = quote_box.get_first_child(); if (quote_box_child != null) quote_box.remove(quote_box_child); quote_box.append(quote_widget); quote_box.visible = true; } public void unset_quoted_message() { Widget? quote_box_child = quote_box.get_first_child(); if (quote_box_child != null) quote_box.remove(quote_box_child); quote_box.visible = false; } public void do_focus() { chat_text_view.text_view.grab_focus(); } } } dino-0.5.0/main/src/ui/contact_details/0000775000000000000000000000000014776241610016445 5ustar rootrootdino-0.5.0/main/src/ui/contact_details/permissions_provider.vala0000664000000000000000000000253114776241610023600 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ContactDetails { public class PermissionsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "permissions"; } } public string tab { get { return "about"; } } private StreamInteractor stream_interactor; public PermissionsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; Xmpp.Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); if (own_jid == null) return; if (stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR){ Button voice_request = new Button.with_label(_("Request")); voice_request.clicked.connect(()=>stream_interactor.get_module(MucManager.IDENTITY).request_voice(conversation.account, conversation.counterpart)); contact_details.add("Permissions", _("Request permission to send messages"), "", voice_request); } } public Object? get_widget(Conversation conversation) { return null; } } } dino-0.5.0/main/src/ui/contact_details/settings_provider.vala0000664000000000000000000000604714776241610023073 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui.ContactDetails { public class SettingsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "chat_settings"; } } public string tab { get { return "about"; } } private StreamInteractor stream_interactor; private string DETAILS_HEADLINE_CHAT = "Settings"; private string DETAILS_HEADLINE_ROOM = "Local Settings"; public SettingsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return; if (!stream_interactor.get().is_public_room(conversation.account, conversation.counterpart)) { string details_headline = conversation.type_ == Conversation.Type.GROUPCHAT ? DETAILS_HEADLINE_ROOM : DETAILS_HEADLINE_CHAT; ComboBoxText combobox_typing = get_combobox(Dino.Application.get_default().settings.send_typing); combobox_typing.active_id = get_setting_id(conversation.send_typing); combobox_typing.changed.connect(() => { conversation.send_typing = get_setting(combobox_typing.active_id); } ); contact_details.add(details_headline, _("Send typing notifications"), "", combobox_typing); } if (conversation.type_ == Conversation.Type.CHAT) { ComboBoxText combobox_marker = get_combobox(Dino.Application.get_default().settings.send_marker); contact_details.add(DETAILS_HEADLINE_CHAT, _("Send read receipts"), "", combobox_marker); combobox_marker.active_id = get_setting_id(conversation.send_marker); combobox_marker.changed.connect(() => { conversation.send_marker = get_setting(combobox_marker.active_id); } ); } } private Conversation.Setting get_setting(string id) { switch (id) { case "default": return Conversation.Setting.DEFAULT; case "on": return Conversation.Setting.ON; case "off": return Conversation.Setting.OFF; } assert_not_reached(); } private string get_setting_id(Conversation.Setting setting) { switch (setting) { case Conversation.Setting.DEFAULT: return "default"; case Conversation.Setting.ON: return "on"; case Conversation.Setting.OFF: return "off"; } assert_not_reached(); } private ComboBoxText get_combobox(bool default_val) { ComboBoxText combobox = new ComboBoxText(); combobox = new ComboBoxText(); string default_setting = default_val ? _("On") : _("Off"); combobox.append("default", _("Default: %s").printf(default_setting) ); combobox.append("on", _("On")); combobox.append("off", _("Off")); return combobox; } public Object? get_widget(Conversation conversation) { return null; } } } dino-0.5.0/main/src/ui/conversation_content_view/0000775000000000000000000000000014776241610020603 5ustar rootrootdino-0.5.0/main/src/ui/conversation_content_view/call_widget.vala0000664000000000000000000002734414776241610023740 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class CallMetaItem : ConversationSummary.ContentMetaItem { private StreamInteractor stream_interactor; public CallMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); this.stream_interactor = stream_interactor; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { CallItem call_item = content_item as CallItem; CallState? call_state = stream_interactor.get_module(Calls.IDENTITY).call_states[call_item.call]; return new CallWidget(stream_interactor, call_item.call, call_state, call_item.conversation); } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } [GtkTemplate (ui = "/im/dino/Dino/call_widget.ui")] public class CallWidget : SizeRequestBox { [GtkChild] public unowned Image image; [GtkChild] public unowned Label title_label; [GtkChild] public unowned Label subtitle_label; [GtkChild] public unowned Revealer incoming_call_revealer; [GtkChild] public unowned Box outer_additional_box; [GtkChild] public unowned Box incoming_call_box; [GtkChild] public unowned Box multiparty_peer_box; [GtkChild] public unowned Button accept_call_button; [GtkChild] public unowned Button reject_call_button; private StreamInteractor stream_interactor; private CallState call_manager; private Call call; private Conversation conversation; public Call.State call_state { get; set; } // needs to be public for binding private uint time_update_handler_id = 0; private ArrayList multiparty_peer_widgets = new ArrayList(); construct { margin_top = 4; size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; } /** @param call_state Null if it's an old call and we can't interact with it anymore */ public CallWidget(StreamInteractor stream_interactor, Call call, CallState? call_state, Conversation conversation) { this.stream_interactor = stream_interactor; this.call_manager = call_state; this.call = call; this.conversation = conversation; // size_allocate.connect((allocation) => { // if (allocation.height > parent.get_allocated_height()) { // Idle.add(() => { parent.queue_resize(); return false; }); // } // }); call.bind_property("state", this, "call-state"); this.notify["call-state"].connect(update_call_state); if (call_manager != null && (call.state == Call.State.ESTABLISHING || call.state == Call.State.IN_PROGRESS)) { call_manager.peer_joined.connect(update_counterparts); } accept_call_button.clicked.connect(() => { call_manager.accept(); var call_window = new CallWindow(); call_window.controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.present(); }); reject_call_button.clicked.connect(call_manager.reject); update_call_state(); } private void update_counterparts() { if (call.state != Call.State.IN_PROGRESS && call.state != Call.State.ENDED) return; if (call.counterparts.size <= 1 && conversation.type_ == Conversation.Type.CHAT) return; foreach (Widget peer_widget in multiparty_peer_widgets) { multiparty_peer_box.remove(peer_widget); } foreach (Jid counterpart in call.counterparts) { AvatarPicture picture = new AvatarPicture() { margin_top=2 }; picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(conversation, counterpart.bare_jid); multiparty_peer_box.append(picture); multiparty_peer_widgets.add(picture); } AvatarPicture picture2 = new AvatarPicture() { margin_top=2 }; picture2.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(conversation, call.account.bare_jid); multiparty_peer_box.append(picture2); multiparty_peer_widgets.add(picture2); outer_additional_box.add_css_class("multiparty-participants"); multiparty_peer_box.visible = true; incoming_call_box.visible = false; incoming_call_revealer.reveal_child = true; } private void on_time_update_timeout() { if (time_update_handler_id != 0) update_call_state(); } private void update_call_state() { incoming_call_revealer.reveal_child = false; incoming_call_revealer.remove_css_class("incoming"); outer_additional_box.remove_css_class("incoming-call-box"); // It doesn't make sense to display MUC calls as missed or declined by the whole MUC. Just display as ended. // TODO: maybe not let them be missed/declined in first place. Call.State relevant_state = call.state; if (conversation.type_ == Conversation.Type.GROUPCHAT && call.direction == Call.DIRECTION_OUTGOING && (relevant_state == Call.State.MISSED || relevant_state == Call.State.DECLINED)) { relevant_state = Call.State.ENDED; } switch (relevant_state) { case Call.State.RINGING: image.set_from_icon_name("dino-phone-ring-symbolic"); if (call.direction == Call.DIRECTION_INCOMING) { bool video = call_manager.should_we_send_video(); title_label.label = video ? _("Incoming video call") : _("Incoming call"); if (call_manager.invited_to_group_call != null) { title_label.label = video ? _("Incoming video group call") : _("Incoming group call"); } if (stream_interactor.get_module(Calls.IDENTITY).can_we_do_calls(call.account)) { subtitle_label.label = "Ring ring…!"; incoming_call_box.visible = true; incoming_call_revealer.reveal_child = true; incoming_call_revealer.add_css_class("incoming"); outer_additional_box.add_css_class("incoming-call-box"); } else { subtitle_label.label = "Dependencies for call support not met"; } } else { title_label.label = _("Calling…"); subtitle_label.label = "Ring ring…?"; } break; case Call.State.ESTABLISHING: case Call.State.IN_PROGRESS: image.set_from_icon_name("dino-phone-in-talk-symbolic"); title_label.label = _("Call started"); string duration = get_duration_string((new DateTime.now_utc()).difference(call.local_time)); subtitle_label.label = _("Started %s ago").printf(duration); time_update_handler_id = Dino.WeakTimeout.add_seconds_once(get_next_time_change() + 1, this, on_time_update_timeout); break; case Call.State.OTHER_DEVICE: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = call.direction == Call.DIRECTION_INCOMING ? _("Incoming call") : _("Outgoing call"); subtitle_label.label = _("You handled this call on another device"); break; case Call.State.ENDED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call ended"); string formated_end = Util.format_time(call.end_time.to_local(), _("%H∶%M"), _("%l∶%M %p")); string duration = get_duration_string(call.end_time.difference(call.local_time)); subtitle_label.label = _("Ended at %s").printf(formated_end) + " · " + _("Lasted %s").printf(duration); break; case Call.State.MISSED: image.set_from_icon_name("dino-phone-missed-symbolic"); title_label.label = _("Call missed"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You missed this call"); } else { string who = Util.get_conversation_display_name(stream_interactor, conversation); subtitle_label.label = _("%s missed this call").printf(who); } break; case Call.State.DECLINED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call declined"); if (call.direction == Call.DIRECTION_INCOMING) { subtitle_label.label = _("You declined this call"); } else { string who = Util.get_conversation_display_name(stream_interactor, conversation); subtitle_label.label = _("%s declined this call").printf(who); } break; case Call.State.FAILED: image.set_from_icon_name("dino-phone-hangup-symbolic"); title_label.label = _("Call failed"); subtitle_label.label = "Call failed to establish"; break; } update_counterparts(); } private string get_duration_string(TimeSpan duration) { DateTime a = new DateTime.now_utc(); DateTime b = new DateTime.now_utc(); a.difference(b); TimeSpan remainder_duration = duration; int hours = (int) Math.floor(remainder_duration / TimeSpan.HOUR); remainder_duration -= hours * TimeSpan.HOUR; int minutes = (int) Math.floor(remainder_duration / TimeSpan.MINUTE); remainder_duration -= minutes * TimeSpan.MINUTE; string ret = ""; if (hours > 0) { ret += n("%i hour", "%i hours", hours).printf(hours); } if (minutes > 0) { if (ret.length > 0) { ret += " "; } ret += n("%i minute", "%i minutes", minutes).printf(minutes); } if (ret.length > 0) { return ret; } return _("a few seconds"); } private int get_next_time_change() { DateTime now = new DateTime.now_local(); DateTime item_time = call.local_time; if (now.get_second() < item_time.get_second()) { return item_time.get_second() - now.get_second(); } else { return 60 - (now.get_second() - item_time.get_second()); } } public override void dispose() { base.dispose(); if (time_update_handler_id != 0) { Source.remove(time_update_handler_id); time_update_handler_id = 0; } if (call_manager != null) { call_manager.peer_joined.disconnect(update_counterparts); } } } } dino-0.5.0/main/src/ui/conversation_content_view/chat_state_populator.vala0000664000000000000000000001256314776241610025703 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { class ChatStatePopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "chat_state"; } } private StreamInteractor? stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private MetaChatStateItem? meta_item; public ChatStatePopulator(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).received_state.connect((conversation, state) => { if (current_conversation != null && current_conversation.equals(conversation)) { update_chat_state(); } }); stream_interactor.get_module(MessageProcessor.IDENTITY).message_sent.connect((message, conversation) => { if (conversation.equals(current_conversation)) { update_chat_state(); } }); } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; this.meta_item = null; update_chat_state(); } public void close(Conversation conversation) { } public void populate_timespan(Conversation conversation, DateTime from, DateTime to) { } private void update_chat_state() { Gee.List? typing_jids = stream_interactor.get_module(CounterpartInteractionManager.IDENTITY).get_typing_jids(current_conversation); if (meta_item != null && typing_jids == null) { // Remove state (stoped typing) item_collection.remove_item(meta_item); meta_item = null; } else if (meta_item != null && typing_jids != null) { // Update state (other people typing in MUC) meta_item.set_new(typing_jids); } else if (typing_jids != null) { // New state (started typing) meta_item = new MetaChatStateItem(stream_interactor, current_conversation, typing_jids); item_collection.insert_item(meta_item); } } } private class MetaChatStateItem : Plugins.MetaConversationItem { public override DateTime time { get; set; default=new DateTime.now_utc().add_years(10); } private StreamInteractor stream_interactor; private Conversation conversation; private Gee.List jids = new ArrayList(); private Label label; private AvatarPicture picture; public MetaChatStateItem(StreamInteractor stream_interactor, Conversation conversation, Gee.List jids) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.jids = jids; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { label = new Label("") { xalign=0, vexpand=true }; label.add_css_class("dim-label"); picture = new AvatarPicture() { margin_top=2, valign=Align.START }; Box image_content_box = new Box(Orientation.HORIZONTAL, 8); image_content_box.append(picture); image_content_box.append(label); update(); return image_content_box; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } public void set_new(Gee.List jids) { this.jids = jids; update(); } private void update() { if (picture == null || label == null) return; Gee.List display_names = new ArrayList(); foreach (Jid jid in jids) { display_names.add(Util.get_participant_display_name(stream_interactor, conversation, jid)); } string new_text = ""; if (jids.size > 3) { new_text = _("%s, %s and %i others are typing…").printf(display_names[0], display_names[1], jids.size - 2); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor) .add_participant(conversation, jids[0]) .add_participant(conversation, jids[1]) .add_participant(conversation, jids[2]) .add("+"); } else if (jids.size == 3) { new_text = _("%s, %s and %s are typing…").printf(display_names[0], display_names[1], display_names[2]); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor) .add_participant(conversation, jids[0]) .add_participant(conversation, jids[1]) .add_participant(conversation, jids[2]); } else if (jids.size == 2) { new_text =_("%s and %s are typing…").printf(display_names[0], display_names[1]); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor) .add_participant(conversation, jids[0]) .add_participant(conversation, jids[1]); } else { new_text = _("%s is typing…").printf(display_names[0]); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor) .add_participant(conversation, jids[0]); } label.label = new_text; } } } dino-0.5.0/main/src/ui/conversation_content_view/content_populator.vala0000664000000000000000000000737414776241610025242 0ustar rootrootusing Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class ContentProvider : ContentItemCollection, Object { private StreamInteractor stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; public ContentProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void init(Plugins.ConversationItemCollection item_collection, Conversation conversation, Plugins.WidgetType type) { if (current_conversation != null) { stream_interactor.get_module(ContentItemStore.IDENTITY).uninit(current_conversation, this); } current_conversation = conversation; this.item_collection = item_collection; stream_interactor.get_module(ContentItemStore.IDENTITY).init(conversation, this); } public void insert_item(ContentItem item) { item_collection.insert_item(create_content_meta_item(item)); } public void remove_item(ContentItem item) { } public Gee.List populate_latest(Conversation conversation, int n) { Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_n_latest(conversation, n); Gee.List ret = new ArrayList(); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public Gee.List populate_before(Conversation conversation, ContentItem before_item, int n) { Gee.List ret = new ArrayList(); Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_before(conversation, before_item, n); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public Gee.List populate_after(Conversation conversation, ContentItem after_item, int n) { Gee.List ret = new ArrayList(); Gee.List items = stream_interactor.get_module(ContentItemStore.IDENTITY).get_after(conversation, after_item, n); foreach (ContentItem item in items) { ret.add(create_content_meta_item(item)); } return ret; } public ContentMetaItem get_content_meta_item(ContentItem content_item) { return create_content_meta_item(content_item); } private ContentMetaItem create_content_meta_item(ContentItem content_item) { if (content_item.type_ == MessageItem.TYPE) { return new MessageMetaItem(content_item, stream_interactor); } else if (content_item.type_ == FileItem.TYPE) { return new FileMetaItem(content_item, stream_interactor); } else if (content_item.type_ == CallItem.TYPE) { return new CallMetaItem(content_item, stream_interactor); } critical("Got unknown content item type %s", content_item.type_); return null; } } public abstract class ContentMetaItem : Plugins.MetaConversationItem { public ContentItem content_item; protected ContentMetaItem(ContentItem content_item) { this.jid = content_item.jid; this.time = content_item.time; this.secondary_sort_indicator = content_item.id; this.encryption = content_item.encryption; this.mark = content_item.mark; content_item.bind_property("mark", this, "mark"); content_item.bind_property("encryption", this, "encryption"); this.can_merge = true; this.requires_avatar = true; this.requires_header = true; this.content_item = content_item; } } } dino-0.5.0/main/src/ui/conversation_content_view/conversation_item_skeleton.vala0000664000000000000000000003033414776241610027107 0ustar rootrootusing Gee; using Gdk; using Gtk; using Markup; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class ConversationItemSkeleton : Plugins.ConversationItemWidgetInterface, Object { public Grid main_grid { get; set; } public Label name_label { get; set; } public Label time_label { get; set; } public AvatarPicture avatar_picture { get; set; } public Image encryption_image { get; set; } public Image received_image { get; set; } private HashMap content_widgets = new HashMap(); private bool show_skeleton_ = false; public bool show_skeleton { get { return show_skeleton_; } set { show_skeleton_ = value && content_meta_item != null && content_meta_item.requires_header && content_meta_item.requires_avatar; } } public StreamInteractor stream_interactor; public Conversation conversation { get; set; } public Plugins.MetaConversationItem item; public bool item_in_edit_mode { get; set; } public Entities.Message.Marked item_mark { get; set; } public ContentMetaItem content_meta_item = null; public Widget? widget = null; private ReactionsController? reactions_controller = null; private uint time_update_timeout = 0; private ulong updated_roster_handler_id = 0; public ConversationItemSkeleton(StreamInteractor stream_interactor, Conversation conversation, Plugins.MetaConversationItem item) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.item = item; this.content_meta_item = item as ContentMetaItem; item.bind_property("in-edit-mode", this, "item-in-edit-mode"); this.notify["item-in-edit-mode"].connect(update_edit_mode); Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_item_widget.ui"); main_grid = (Grid) builder.get_object("main_grid"); main_grid.add_css_class("message-box"); name_label = (Label) builder.get_object("name_label"); time_label = (Label) builder.get_object("time_label"); avatar_picture = (AvatarPicture) builder.get_object("avatar_picture"); encryption_image = (Image) builder.get_object("encrypted_image"); received_image = (Image) builder.get_object("marked_image"); widget = item.get_widget(this, Plugins.WidgetType.GTK4) as Widget; if (widget != null) { widget.valign = Align.END; set_widget(widget, Plugins.WidgetType.GTK4, 2); } if (item.requires_header) { // TODO: For MUC messags, use real jid from message if known avatar_picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(conversation, item.jid); } this.notify["show-skeleton"].connect(update_margin); this.notify["show-skeleton"].connect(set_header); ContentMetaItem? content_meta_item = item as ContentMetaItem; if (content_meta_item != null) { reactions_controller = new ReactionsController(conversation, content_meta_item.content_item, stream_interactor); reactions_controller.box_activated.connect(on_reaction_box_activated); reactions_controller.init(); } update_margin(); } private void set_header() { if (!show_skeleton) return; update_name_label(); // name_label.style_updated.connect(update_name_label); updated_roster_handler_id = stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (this.conversation.account.equals(account) && this.conversation.counterpart.equals(jid)) { update_name_label(); } }); item.notify["encryption"].connect(update_encryption_icon); update_encryption_icon(); if (item.time != null) { update_time(); } item.bind_property("mark", this, "item-mark", BindingFlags.SYNC_CREATE); this.notify["item-mark"].connect_after(update_received_mark); update_received_mark(); } public void set_widget(Object object, Plugins.WidgetType type, int priority) { foreach (var content_widget in content_widgets.values) { content_widget.unparent(); } content_widgets[priority] = (Widget) object; int row_no = 1; for (int i = 0; i < 5; i++) { if (!content_widgets.has_key(i)) continue; main_grid.attach(content_widgets[i], 1, row_no, 4, 1); row_no++; } } private void update_margin() { avatar_picture.visible = show_skeleton; name_label.visible = show_skeleton; time_label.visible = show_skeleton; encryption_image.visible = show_skeleton; received_image.visible = show_skeleton; if (show_skeleton || content_meta_item == null) { main_grid.add_css_class("has-skeleton"); } } private void update_edit_mode() { if (item.in_edit_mode) { main_grid.add_css_class("edit-mode"); } else { main_grid.remove_css_class("edit-mode"); } } private void update_error_mode() { if (item_mark == Message.Marked.ERROR) { main_grid.add_css_class("error"); } else { main_grid.remove_css_class("error"); } } private void update_encryption_icon() { Application app = GLib.Application.get_default() as Application; ContentMetaItem ci = item as ContentMetaItem; if (item.encryption != Encryption.NONE && item.encryption != Encryption.UNKNOWN && ci != null) { string? icon_name = null; var encryption_entry = app.plugin_registry.encryption_list_entries[item.encryption]; if (encryption_entry != null) icon_name = encryption_entry.get_encryption_icon_name(conversation, ci.content_item); encryption_image.icon_name = icon_name ?? "changes-prevent-symbolic"; encryption_image.visible = true; } if (item.encryption == Encryption.NONE) { if (conversation.encryption != Encryption.NONE) { encryption_image.icon_name = "changes-allow-symbolic"; encryption_image.tooltip_text = Util.string_if_tooltips_active(_("Unencrypted")); Util.force_error_color(encryption_image); encryption_image.visible = true; } else if (conversation.encryption == Encryption.NONE) { encryption_image.icon_name = null; encryption_image.visible = false; } } } private void on_reaction_box_activated(Widget widget) { set_widget(widget, Plugins.WidgetType.GTK4, 3); } private void on_time_update_timeout() { if (main_grid.parent != null) update_time(); } private void update_time() { time_label.label = get_relative_time(item.time.to_local()).to_string(); time_update_timeout = Dino.WeakTimeout.add_seconds_once((int) get_next_time_change(item.time), this, on_time_update_timeout); } private void update_name_label() { name_label.label = Util.get_participant_display_name(stream_interactor, conversation, item.jid, true); } private void update_received_mark() { switch (content_meta_item.mark) { case Message.Marked.RECEIVED: received_image.icon_name = "dino-tick-symbolic"; received_image.tooltip_text = Util.string_if_tooltips_active(_("Delivered")); break; case Message.Marked.READ: received_image.icon_name = "dino-double-tick-symbolic"; received_image.tooltip_text = Util.string_if_tooltips_active(_("Read")); break; case Message.Marked.WONTSEND: received_image.icon_name = "dialog-warning-symbolic"; Util.force_error_color(received_image); Util.force_error_color(time_label); string error_text = Util.string_if_tooltips_active(_("Unable to send message")); received_image.tooltip_text = error_text; time_label.tooltip_text = error_text; break; default: received_image.icon_name = null; break; } } public static int get_next_time_change(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan < 10 * TimeSpan.MINUTE) { if (now.get_second() < datetime.get_second()) { return datetime.get_second() - now.get_second(); } else { return 60 - (now.get_second() - datetime.get_second()); } } else { return (23 - now.get_hour()) * 3600 + (59 - now.get_minute()) * 60 + (59 - now.get_second()); } } public static string format_time(DateTime datetime, string format_24h, string format_12h) { string format = Util.is_24h_format() ? format_24h : format_12h; if (!get_charset(null)) { // No UTF-8 support, use simple colon for time instead format = format.replace("∶", ":"); } return datetime.format(format); } public static string get_relative_time(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan > 365 * TimeSpan.DAY) { return format_time(datetime, /* xgettext:no-c-format */ /* Date + time in 24h format (w/o seconds) */ _("%x, %H∶%M"), /* xgettext:no-c-format */ /* Date + time in 12h format (w/o seconds)*/ _("%x, %l∶%M %p")); } else if (timespan > 7 * TimeSpan.DAY) { return format_time(datetime, /* xgettext:no-c-format */ /* Month, day and time in 24h format (w/o seconds) */ _("%b %d, %H∶%M"), /* xgettext:no-c-format */ /* Month, day and time in 12h format (w/o seconds) */ _("%b %d, %l∶%M %p")); } else if (datetime.get_day_of_month() != now.get_day_of_month()) { return format_time(datetime, /* xgettext:no-c-format */ /* Day of week and time in 24h format (w/o seconds) */ _("%a, %H∶%M"), /* xgettext:no-c-format */ /* Day of week and time in 12h format (w/o seconds) */_("%a, %l∶%M %p")); } else if (timespan > 9 * TimeSpan.MINUTE) { return format_time(datetime, /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M"), /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p")); } else if (timespan > TimeSpan.MINUTE) { ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE); /* xgettext:this is the beginning of a sentence. */ return n("%i min ago", "%i mins ago", mins).printf(mins); } else { return _("Just now"); } } public Widget get_widget() { return main_grid; } public override void dispose() { if (time_update_timeout != 0) { Source.remove(time_update_timeout); time_update_timeout = 0; } if (updated_roster_handler_id != 0){ stream_interactor.get_module(RosterManager.IDENTITY).disconnect(updated_roster_handler_id); updated_roster_handler_id = 0; } reactions_controller = null; // Children won't be disposed automatically if (name_label != null) { name_label.unparent(); name_label.dispose(); name_label = null; } if (time_label != null) { time_label.unparent(); time_label.dispose(); time_label = null; } if (avatar_picture != null) { avatar_picture.unparent(); avatar_picture.dispose(); avatar_picture = null; } if (encryption_image != null) { encryption_image.unparent(); encryption_image.dispose(); encryption_image = null; } if (received_image != null) { received_image.unparent(); received_image.dispose(); received_image = null; } base.dispose(); } } } dino-0.5.0/main/src/ui/conversation_content_view/conversation_view.vala0000664000000000000000000006153014776241610025221 0ustar rootrootusing Gee; using Gtk; using Gdk; using Pango; using Dino.Entities; namespace Dino.Ui.ConversationSummary { [GtkTemplate (ui = "/im/dino/Dino/conversation_content_view/view.ui")] public class ConversationView : Widget, Plugins.ConversationItemCollection, Plugins.NotificationCollection { private const int MESSAGE_MENU_BOX_OFFSET = -20; public Conversation? conversation { get; private set; } [GtkChild] public unowned ScrolledWindow scrolled; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Box message_menu_box; [GtkChild] private unowned Box notifications; [GtkChild] private unowned Box main; [GtkChild] private unowned Widget main_wrap_box; private HashMap action_buttons = new HashMap(); private Gee.List? message_actions = null; private StreamInteractor stream_interactor; private Gee.TreeSet content_items = new Gee.TreeSet(compare_content_meta_items); private Gee.TreeSet meta_items = new TreeSet(compare_meta_items); private Gee.HashMap item_item_skeletons = new Gee.HashMap(); private Gee.HashMap widgets = new Gee.HashMap(); private Gee.List widget_order = new Gee.ArrayList(); private ContentProvider content_populator; private SubscriptionNotitication subscription_notification; private double? was_value; private double? was_upper; private double? was_page_size; private Mutex reloading_mutex = Mutex(); private bool firstLoad = true; private bool at_current_content = true; private bool reload_messages = true; Widget currently_highlighted = null; ContentMetaItem? current_meta_item = null; double last_y = -1; construct { this.layout_manager = new BinLayout(); main_wrap_box.layout_manager = new BinLayout(); // Setup all message menu buttons var correction_button = new Button() { name="correction" }; correction_button.clicked.connect((button) => { on_action_button_clicked(button, null); }); action_buttons["correction"] = correction_button; message_menu_box.append(correction_button); var reply_button = new Button() { name="reply" }; reply_button.clicked.connect((button) => { on_action_button_clicked(button, null); }); action_buttons["reply"] = reply_button; message_menu_box.append(reply_button); var reaction_button = new MenuButton() { name="reaction" }; EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { on_action_button_clicked(reaction_button, new GLib.Variant.string(emoji)); }); reaction_button.popover = chooser; action_buttons["reaction"] = reaction_button; message_menu_box.append(reaction_button); } public ConversationView init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; scrolled.vadjustment.notify["upper"].connect_after(on_upper_notify); scrolled.vadjustment.notify["page-size"].connect(on_upper_notify); scrolled.vadjustment.notify["value"].connect(on_value_notify); content_populator = new ContentProvider(stream_interactor); subscription_notification = new SubscriptionNotitication(stream_interactor); add_meta_notification.connect(on_add_meta_notification); remove_meta_notification.connect(on_remove_meta_notification); Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_conversation_addition_populator(new ChatStatePopulator(stream_interactor)); app.plugin_registry.register_conversation_addition_populator(new DateSeparatorPopulator(stream_interactor)); app.plugin_registry.register_conversation_addition_populator(new UnreadIndicatorPopulator(stream_interactor)); // Rather than connecting to the leave event of the main_event_box directly, // we connect to the parent event box that also wraps the overlaying message_menu_box. // This eliminates the unwanted leave events emitted on the main_event_box when hovering // the overlaying menu buttons. EventControllerMotion main_wrap_motion_events = new EventControllerMotion(); main_wrap_box.add_controller(main_wrap_motion_events); main_wrap_motion_events.leave.connect(on_leave_notify_event); // The buttons of the overlaying message_menu_box may partially overlap the adjacent // conversation items. We connect to the main_event_box directly to avoid emitting // the pointer motion events as long as the pointer is above the message menu. // This ensures that the currently highlighted item remains unchanged when the pointer // reaches the overlapping part of a button. EventControllerMotion main_motion_events = new EventControllerMotion(); main.add_controller(main_motion_events); main_motion_events.motion.connect(update_highlight); // Process touch events and capture phase to allow highlighting a message without cursor GestureClick click_controller = new GestureClick(); click_controller.touch_only = true; click_controller.propagation_phase = Gtk.PropagationPhase.CAPTURE; main_wrap_box.add_controller(click_controller); click_controller.pressed.connect_after((n, x, y) => { update_highlight(x, y); }); return this; } public void activate_last_message_correction() { Gee.BidirIterator iter = content_items.bidir_iterator(); iter.last(); for (int i = 0; i < 10 && content_items.size > i; i++) { Plugins.MetaConversationItem item = iter.get(); MessageMetaItem message_item = item as MessageMetaItem; if (message_item != null) { if ((conversation.type_ == Conversation.Type.CHAT && message_item.jid.equals_bare(conversation.account.bare_jid)) || (conversation.type_ == Conversation.Type.GROUPCHAT && message_item.jid.equals(stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account)))) { message_item.in_edit_mode = true; break; } } iter.previous(); } } private bool is_highlight_fixed() { foreach (Widget widget in action_buttons.values) { MenuButton? menu_button = widget as MenuButton; if (menu_button != null && menu_button.popover.visible) return true; ToggleButton? toggle_button = widget as ToggleButton; if (toggle_button != null && toggle_button.active) return true; } return false; } private void on_leave_notify_event() { if (is_highlight_fixed()) return; if (currently_highlighted != null) { currently_highlighted.remove_css_class("highlight"); currently_highlighted = null; } message_menu_box.visible = false; } private void update_highlight(double x, double y) { if (is_highlight_fixed()) return; if (currently_highlighted != null && (last_y - y).abs() <= 2) { return; } last_y = y; // Get widget under pointer int h = 0; Widget? w = null; foreach (Plugins.MetaConversationItem item in meta_items) { Widget widget = widgets[item]; h += widget.get_allocated_height() + widget.margin_top + widget.margin_bottom; if (h >= y) { w = widget; break; } }; if (currently_highlighted != null) currently_highlighted.remove_css_class("highlight"); currently_highlighted = null; current_meta_item = null; if (w == null) { update_message_menu(); return; } // Get widget coordinates in main double widget_x, widget_y; w.translate_coordinates(main, 0, 0, out widget_x, out widget_y); // Get MessageItem foreach (Plugins.MetaConversationItem item in item_item_skeletons.keys) { if (item_item_skeletons[item].get_widget() == w) { current_meta_item = item as ContentMetaItem; } } update_message_menu(); if (current_meta_item != null) { // Highlight widget currently_highlighted = w; currently_highlighted.add_css_class("highlight"); // Move message menu message_menu_box.margin_top = (int)(widget_y + MESSAGE_MENU_BOX_OFFSET); } } private void update_message_menu() { if (current_meta_item == null) { message_menu_box.visible = false; return; } var current_message_actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); message_actions = current_meta_item.get_item_actions(Plugins.WidgetType.GTK4); if (message_actions != null) { message_menu_box.visible = true; foreach (Widget widget in action_buttons.values) { widget.visible = false; } // Configure as many buttons as we need with the actions for the current meta item foreach (var message_action in current_message_actions) { Widget button_widget = action_buttons[message_action.name]; button_widget.visible = true; if (message_action.name == "reaction") { MenuButton button = (MenuButton) button_widget; button.sensitive = message_action.sensitive; button.icon_name = message_action.icon_name; button.tooltip_text = Util.string_if_tooltips_active(message_action.tooltip); } else if (message_action.callback != null) { Button button = (Button) button_widget; button.sensitive = message_action.sensitive; button.icon_name = message_action.icon_name; button.tooltip_text = Util.string_if_tooltips_active(message_action.tooltip); } } } else { message_menu_box.visible = false; } } public void initialize_for_conversation(Conversation? conversation) { // Workaround for rendering issues if (firstLoad) { main.visible = false; Idle.add(() => { main.visible=true; return false; }); firstLoad = false; } if (conversation == this.conversation && at_current_content) { // Just make sure we are scrolled down if (scrolled.vadjustment.value != scrolled.vadjustment.upper) { scroll_animation(scrolled.vadjustment.upper).play(); } return; } clear(); initialize_for_conversation_(conversation); display_latest(); at_current_content = true; // Scroll to end scrolled.vadjustment.value = scrolled.vadjustment.upper; } private void scroll_and_highlight_item(Plugins.MetaConversationItem target, uint duration = 500) { Widget widget = null; int h = 0; foreach (Plugins.MetaConversationItem item in meta_items) { widget = widgets[item]; if (target == item) { break; } h += widget.get_allocated_height(); } if (widget != widgets[target]) { warning("Target item widget not reached"); return; } double target_height = h - scrolled.vadjustment.page_size * 1/3; Adw.Animation animation = scroll_animation(target_height); animation.done.connect(() => { widget.remove_css_class("highlight-once"); widget.add_css_class("highlight-once"); Timeout.add(5000, () => { widget.remove_css_class("highlight-once"); return false; }); }); animation.play(); } private Adw.Animation scroll_animation(double target) { return new Adw.TimedAnimation(scrolled, scrolled.vadjustment.value, target, 500, new Adw.PropertyAnimationTarget(scrolled.vadjustment, "value") ); } public void initialize_around_message(Conversation conversation, ContentItem content_item) { if (conversation == this.conversation) { ContentMetaItem? matching_item = content_items.first_match(it => it.content_item.id == content_item.id); if (matching_item != null) { scroll_and_highlight_item(matching_item); return; } } clear(); initialize_for_conversation_(conversation); Gee.List before_items = content_populator.populate_before(conversation, content_item, 40); foreach (ContentMetaItem item in before_items) { do_insert_item(item); } ContentMetaItem meta_item = content_populator.get_content_meta_item(content_item); insert_new(meta_item); content_items.add(meta_item); meta_items.add(meta_item); Gee.List after_items = content_populator.populate_after(conversation, content_item, 40); foreach (ContentMetaItem item in after_items) { do_insert_item(item); } if (after_items.size == 40) { at_current_content = false; } // Compute where to jump to for centered message, jump, highlight. reload_messages = false; Timeout.add(700, () => { scroll_and_highlight_item(meta_item, 300); reload_messages = true; return false; }); } private void initialize_for_conversation_(Conversation? conversation) { if (this.conversation == conversation) { debug("Re-initialized for %s", conversation.counterpart.bare_jid.to_string()); } // Deinitialize old conversation Dino.Application app = Dino.Application.get_default(); if (this.conversation != null) { foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.close(conversation); } foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { populator.close(conversation); } } // Clear data structures clear_notifications(); this.conversation = conversation; // Init for new conversation foreach (Plugins.ConversationItemPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.init(conversation, this, Plugins.WidgetType.GTK4); } content_populator.init(this, conversation, Plugins.WidgetType.GTK4); subscription_notification.init(conversation, this); } private void display_latest() { Gee.List items = content_populator.populate_latest(conversation, 40); foreach (ContentMetaItem item in items) { do_insert_item(item); } Application app = GLib.Application.get_default() as Application; foreach (Plugins.NotificationPopulator populator in app.plugin_registry.notification_populators) { populator.init(conversation, this, Plugins.WidgetType.GTK4); } Idle.add(() => { on_value_notify(); return false; }); } public void insert_item(Plugins.MetaConversationItem item) { if (meta_items.size > 0) { bool after_last = meta_items.last().time.compare(item.time) <= 0; bool within_range = meta_items.last().time.compare(item.time) > 0 && meta_items.first().time.compare(item.time) < 0; bool accept = within_range || (at_current_content && after_last); if (!accept) { return; } } do_insert_item(item); } public void do_insert_item(Plugins.MetaConversationItem item) { lock (meta_items) { insert_new(item); if (item is ContentMetaItem) { content_items.add((ContentMetaItem)item); } meta_items.add(item); } inserted_item(item); } private void remove_item(Plugins.MetaConversationItem item) { ConversationItemSkeleton? skeleton = item_item_skeletons[item]; if (skeleton != null) { main.remove(skeleton.get_widget()); widgets.unset(item); widget_order.remove(skeleton.get_widget()); item_item_skeletons.unset(item); if (item is ContentMetaItem) { content_items.remove((ContentMetaItem)item); } meta_items.remove(item); skeleton.dispose(); } removed_item(item); } public void on_add_meta_notification(Plugins.MetaConversationNotification notification) { Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { add_notification(widget); } } public void on_remove_meta_notification(Plugins.MetaConversationNotification notification){ Widget? widget = (Widget) notification.get_widget(Plugins.WidgetType.GTK4); if (widget != null) { remove_notification(widget); } } public void add_notification(Widget widget) { notifications.append(widget); Timeout.add(20, () => { notification_revealer.transition_duration = 200; notification_revealer.reveal_child = true; return false; }); } public void remove_notification(Widget widget) { notification_revealer.reveal_child = false; notifications.remove(widget); } private Widget insert_new(Plugins.MetaConversationItem item) { Plugins.MetaConversationItem? lower_item = meta_items.lower(item); // Fill datastructure ConversationItemSkeleton item_skeleton = new ConversationItemSkeleton(stream_interactor, conversation, item); item_item_skeletons[item] = item_skeleton; int index = lower_item != null ? widget_order.index_of(item_item_skeletons[lower_item].get_widget()) + 1 : 0; widget_order.insert(index, item_skeleton.get_widget()); // Insert widget widgets[item] = item_skeleton.get_widget(); widgets[item].insert_after(main, item_item_skeletons.has_key(lower_item) ? item_item_skeletons[lower_item].get_widget() : null); if (lower_item != null) { if (can_merge(item, lower_item)) { item_skeleton.show_skeleton = false; } else { item_skeleton.show_skeleton = true; } } else { item_skeleton.show_skeleton = true; } Plugins.MetaConversationItem? upper_item = meta_items.higher(item); if (upper_item != null) { if (!can_merge(upper_item, item)) { ConversationItemSkeleton upper_skeleton = item_item_skeletons[upper_item]; upper_skeleton.show_skeleton = true; } } // If an item from the past was added, add everything between that item and the (post-)first present item if (index == 0) { Dino.Application app = Dino.Application.get_default(); if (widget_order.size == 1) { foreach (Plugins.ConversationAdditionPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.populate_timespan(conversation, item.time, new DateTime.now_utc()); } } else { foreach (Plugins.ConversationAdditionPopulator populator in app.plugin_registry.conversation_addition_populators) { populator.populate_timespan(conversation, item.time, meta_items.higher(item).time); } } } return item_skeleton.get_widget(); } private bool can_merge(Plugins.MetaConversationItem upper_item /*more recent, displayed below*/, Plugins.MetaConversationItem lower_item /*less recent, displayed above*/) { return upper_item.time != null && lower_item.time != null && upper_item.time.difference(lower_item.time) < TimeSpan.MINUTE && upper_item.jid != null && lower_item.jid != null && upper_item.jid.equals(lower_item.jid) && upper_item.encryption == lower_item.encryption && (upper_item.mark == Message.Marked.WONTSEND) == (lower_item.mark == Message.Marked.WONTSEND); } private void on_action_button_clicked(Widget widget, GLib.Variant? variant = null) { foreach (var action in message_actions) { if (action.name != widget.name) continue; action.callback(variant); } } private void on_upper_notify() { if (was_upper == null || scrolled.vadjustment.value > was_upper - was_page_size - 1) { // scrolled down or content smaller than page size if (at_current_content) { Idle.add(() => { // If we do this directly without Idle.add, scrolling down doesn't work properly scrolled.vadjustment.value = scrolled.vadjustment.upper - scrolled.vadjustment.page_size; // scroll down return false; }); } } else if (scrolled.vadjustment.value < scrolled.vadjustment.upper - scrolled.vadjustment.page_size - 1) { scrolled.vadjustment.value = scrolled.vadjustment.upper - was_upper + scrolled.vadjustment.value; // stay at same content } was_upper = scrolled.vadjustment.upper; was_page_size = scrolled.vadjustment.page_size; was_value = scrolled.vadjustment.value; reloading_mutex.trylock(); reloading_mutex.unlock(); } private void on_value_notify() { if (scrolled.vadjustment.value < 400) { load_earlier_messages(); } else if (scrolled.vadjustment.upper - (scrolled.vadjustment.value + scrolled.vadjustment.page_size) < 400) { load_later_messages(); } } private void load_earlier_messages() { was_value = scrolled.vadjustment.value; if (!reloading_mutex.trylock()) return; if (content_items.size > 0) { Gee.List items = content_populator.populate_before(conversation, ((ContentMetaItem) content_items.first()).content_item, 20); foreach (ContentMetaItem item in items) { do_insert_item(item); } } else { reloading_mutex.unlock(); } } private void load_later_messages() { if (!reloading_mutex.trylock()) return; if (content_items.size > 0 && !at_current_content) { Gee.List items = content_populator.populate_after(conversation, ((ContentMetaItem) content_items.last()).content_item, 20); if (items.size == 0) { at_current_content = true; } foreach (ContentMetaItem item in items) { do_insert_item(item); } } else { reloading_mutex.unlock(); } } private static int compare_content_meta_items(ContentMetaItem a, ContentMetaItem b) { return compare_meta_items(a, b); } private static int compare_meta_items(Plugins.MetaConversationItem a, Plugins.MetaConversationItem b) { int cmp1 = a.time.compare(b.time); if (cmp1 != 0) return cmp1; return a.secondary_sort_indicator - b.secondary_sort_indicator; } private void clear() { was_upper = null; was_page_size = null; foreach (var item in content_items) { item.dispose(); } content_items.clear(); meta_items.clear(); widget_order.clear(); foreach (var skeleton in item_item_skeletons.values) { skeleton.dispose(); } item_item_skeletons.clear(); foreach (Widget widget in widgets.values) { widget.unparent(); widget.dispose(); } widgets.clear(); Widget? notification = notifications.get_first_child(); while (notification != null) { notifications.remove(notification); notification = notifications.get_first_child(); } } private void clear_notifications() { // notifications.@foreach((widget) => { notifications.remove(widget); }); notification_revealer.transition_duration = 0; notification_revealer.set_reveal_child(false); } } } dino-0.5.0/main/src/ui/conversation_content_view/date_separator_populator.vala0000664000000000000000000000461014776241610026553 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { class DateSeparatorPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "date_separator"; } } private StreamInteractor stream_interactor; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private Gee.TreeSet insert_times; public DateSeparatorPopulator(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; item_collection.inserted_item.connect(on_inserted_item); this.insert_times = new TreeSet((a, b) => { return a.compare(b); }); } public void close(Conversation conversation) { item_collection.inserted_item.disconnect(on_inserted_item); } public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } private void on_inserted_item(Plugins.MetaConversationItem item) { if (!(item is ContentMetaItem)) return; DateTime time = item.time.to_local(); DateTime msg_date = new DateTime.local(time.get_year(), time.get_month(), time.get_day_of_month(), 0, 0, 0); if (!insert_times.contains(msg_date)) { if (insert_times.lower(msg_date) != null) { item_collection.insert_item(new MetaDateItem(msg_date.to_utc())); } else if (insert_times.size > 0) { item_collection.insert_item(new MetaDateItem(insert_times.first().to_utc())); } insert_times.add(msg_date); } } } public class MetaDateItem : Plugins.MetaConversationItem { public override DateTime time { get; set; } public MetaDateItem(DateTime date) { this.time = date; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new DateSeparator() { model = new ViewModel.CompatDateSeparatorModel(time) }; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } } dino-0.5.0/main/src/ui/conversation_content_view/file_default_widget.vala0000664000000000000000000001471214776241610025443 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/file_default_widget.ui")] public class FileDefaultWidget : Box { public signal void clicked(); [GtkChild] public unowned Stack image_stack; [GtkChild] public unowned Label name_label; [GtkChild] public unowned Label mime_label; [GtkChild] public unowned Image content_type_image; [GtkChild] public unowned Spinner spinner; [GtkChild] public unowned MenuButton file_menu; private FileTransfer.State state; public FileDefaultWidget() { EventControllerMotion this_motion_events = new EventControllerMotion(); this.add_controller(this_motion_events); this_motion_events.enter.connect(on_pointer_entered_event); this_motion_events.leave.connect(on_pointer_left_event); GestureClick gesture_click_controller = new GestureClick(); gesture_click_controller.set_button(1); // listen for left clicks this.add_controller(gesture_click_controller); gesture_click_controller.pressed.connect((n_press, x, y) => { // Check whether the click was inside the file menu. Otherwise, open the file. double x_button, y_button; this.translate_coordinates(file_menu, x, y, out x_button, out y_button); if (file_menu.contains(x_button, y_button)) return; this.clicked(); }); } public void update_file_info(string? mime_type, FileTransfer.State state, bool direction, int64 size, int64 transferred_bytes) { this.state = state; spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap content_type_image.icon_name = get_file_icon_name(mime_type); string? mime_description = mime_type != null ? ContentType.get_description(mime_type) : null; switch (state) { case FileTransfer.State.COMPLETE: mime_label.label = mime_description; image_stack.set_visible_child_name("content_type_image"); // Create a menu Menu menu_model = new Menu(); menu_model.append(_("Open"), "file.open"); menu_model.append(_("Save as…"), "file.save_as"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.IN_PROGRESS: if (direction == FileTransfer.DIRECTION_RECEIVED) { if (size > 0) { int64 progress = transferred_bytes * 100 / size; mime_label.label = _("Downloading %s… (%u%%)").printf(get_size_string(size), progress); } else { mime_label.label = _("Downloading %s…").printf(get_size_string(size)); } } else { int64 progress = transferred_bytes * 100 / size; mime_label.label = _("Uploading %s… (%u%%)").printf(get_size_string(size), progress); } spinner.start(); image_stack.set_visible_child_name("spinner"); // Create a menu Menu menu_model = new Menu(); menu_model.append(_("Cancel"), "file.cancel_download"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); file_menu.popover = popover_menu; popover_menu.closed.connect(on_pointer_left); break; case FileTransfer.State.NOT_STARTED: if (mime_description != null) { mime_label.label = _("%s offered: %s").printf(mime_description, get_size_string(size)); } else if (size != -1) { mime_label.label = _("File offered: %s").printf(get_size_string(size)); } else { mime_label.label = _("File offered"); } image_stack.set_visible_child_name("content_type_image"); break; case FileTransfer.State.FAILED: mime_label.use_markup = true; mime_label.label = "" + _("File transfer failed") + ""; image_stack.set_visible_child_name("content_type_image"); break; } } private void on_pointer_entered_event() { this.set_cursor_from_name("pointer"); content_type_image.opacity = 0.7; if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("download_image"); } if (state == FileTransfer.State.COMPLETE || state == FileTransfer.State.IN_PROGRESS) { file_menu.opacity = 1; } } private void on_pointer_left_event() { if (file_menu.popover != null && file_menu.popover.visible) return; this.set_cursor(null); on_pointer_left(); } private void on_pointer_left() { content_type_image.opacity = 0.5; if (state == FileTransfer.State.NOT_STARTED) { image_stack.set_visible_child_name("content_type_image"); } file_menu.opacity = 0; } private static string get_file_icon_name(string? mime_type) { if (mime_type == null) return "dino-file-symbolic"; string generic_icon_name = ContentType.get_generic_icon_name(mime_type) ?? ""; switch (generic_icon_name) { case "audio-x-generic": return "dino-file-music-symbolic"; case "image-x-generic": return "dino-file-image-symbolic"; case "text-x-generic": return "dino-file-document-symbolic"; case "text-x-generic-template": return "dino-file-document-symbolic"; case "video-x-generic": return "dino-file-video-symbolic"; case "x-office-document": return "dino-file-document-symbolic"; case "x-office-spreadsheet": return "dino-file-table-symbolic"; default: return "dino-file-symbolic"; } } public static string get_size_string(int64 size) { if (size < 1024) { return @"$(size) B"; } else if (size < 1000 * 1000) { return @"$(size / 1000) kB"; } else if (size < 1000 * 1000 * 1000) { return @"$(size / 1000 / 1000) MB"; } else { return @"$(size / 1000 / 1000 / 1000) GB"; } } } } dino-0.5.0/main/src/ui/conversation_content_view/file_image_widget.vala0000664000000000000000000002574414776241610025110 0ustar rootrootusing Gee; using Gdk; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class FileImageWidget : Widget { enum State { EMPTY, PREVIEW, IMAGE } private State state = State.EMPTY; private Stack stack = new Stack() { transition_duration=600, transition_type=StackTransitionType.CROSSFADE, hhomogeneous = false, vhomogeneous = false, interpolate_size = true }; private Overlay overlay = new Overlay(); private bool show_image_overlay_toolbar = false; private Gtk.Box image_overlay_toolbar = new Gtk.Box(Orientation.VERTICAL, 0) { halign=Align.END, valign=Align.START, margin_top=10, margin_start=10, margin_end=10, margin_bottom=10, vexpand=false, visible=false }; private Label file_size_label = new Label(null) { halign=Align.START, valign=Align.END, margin_bottom=4, margin_start=4, visible=false }; private FileTransfer file_transfer; private FileTransmissionProgress transmission_progress = new FileTransmissionProgress() { halign=Align.CENTER, valign=Align.CENTER, visible=false }; construct { layout_manager = new BinLayout(); } public FileImageWidget(int MAX_WIDTH=600, int MAX_HEIGHT=300) { this.halign = Align.START; this.add_css_class("file-image-widget"); // Setup menu button overlay MenuButton button = new MenuButton(); button.icon_name = "view-more"; Menu menu_model = new Menu(); menu_model.append(_("Open"), "file.open"); menu_model.append(_("Save as…"), "file.save_as"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; image_overlay_toolbar.append(button); image_overlay_toolbar.add_css_class("card"); image_overlay_toolbar.add_css_class("toolbar"); image_overlay_toolbar.add_css_class("overlay-toolbar"); image_overlay_toolbar.set_cursor_from_name("default"); file_size_label.add_css_class("file-details"); overlay.set_child(stack); overlay.set_measure_overlay(stack, true); overlay.add_overlay(file_size_label); overlay.add_overlay(transmission_progress); overlay.add_overlay(image_overlay_toolbar); overlay.set_clip_overlay(image_overlay_toolbar, true); overlay.insert_after(this, null); GestureClick gesture_click_controller = new GestureClick(); gesture_click_controller.button = 1; // listen for left clicks gesture_click_controller.released.connect(on_image_clicked); stack.add_controller(gesture_click_controller); EventControllerMotion this_motion_events = new EventControllerMotion(); this.add_controller(this_motion_events); this_motion_events.enter.connect((controller, x, y) => { (controller.widget as FileImageWidget).on_motion_event_enter(); }); attach_on_motion_event_leave(this_motion_events, button); } private static void attach_on_motion_event_leave(EventControllerMotion this_motion_events, MenuButton button) { this_motion_events.leave.connect((controller) => { if (button.popover != null && button.popover.visible) return; (controller.widget as FileImageWidget).image_overlay_toolbar.visible = false; (controller.widget as FileImageWidget).file_size_label.visible = false; }); } private void on_motion_event_enter() { image_overlay_toolbar.visible = show_image_overlay_toolbar; file_size_label.visible = file_transfer != null && file_transfer.direction == FileTransfer.DIRECTION_RECEIVED && file_transfer.state == FileTransfer.State.NOT_STARTED && !file_transfer.sfs_sources.is_empty; } public async void set_file_transfer(FileTransfer file_transfer) { this.file_transfer = file_transfer; this.file_transfer.bind_property("size", file_size_label, "label", BindingFlags.SYNC_CREATE, file_size_label_transform); this.file_transfer.bind_property("size", transmission_progress, "file-size", BindingFlags.SYNC_CREATE); this.file_transfer.bind_property("transferred-bytes", transmission_progress, "transferred-size"); file_transfer.notify["state"].connect(refresh_state); file_transfer.sources_changed.connect(refresh_state); refresh_state(); } private static bool file_size_label_transform(Binding binding, Value from_value, ref Value to_value) { to_value = FileDefaultWidget.get_size_string((int64) from_value); return true; } private void refresh_state() { if ((state == EMPTY || state == PREVIEW) && file_transfer.path != null) { load_from_file.begin(file_transfer.get_file(), file_transfer.file_name); show_image_overlay_toolbar = true; this.set_cursor_from_name("zoom-in"); state = IMAGE; } else if (state == EMPTY && file_transfer.thumbnails.size > 0) { load_from_thumbnail.begin(file_transfer); transmission_progress.visible = true; show_image_overlay_toolbar = false; state = PREVIEW; } if (file_transfer.state == IN_PROGRESS || file_transfer.state == NOT_STARTED || file_transfer.state == FAILED) { transmission_progress.visible = true; show_image_overlay_toolbar = false; } else if (transmission_progress.visible) { Timeout.add(250, () => { transmission_progress.transferred_size = transmission_progress.file_size; transmission_progress.visible = false; show_image_overlay_toolbar = true; return false; }); } if (file_transfer.direction == FileTransfer.DIRECTION_RECEIVED) { if (file_transfer.state == IN_PROGRESS) { transmission_progress.state = DOWNLOADING; } else if (file_transfer.sfs_sources.is_empty) { transmission_progress.state = UNKNOWN_SOURCE; } else if (file_transfer.state == NOT_STARTED) { transmission_progress.state = DOWNLOAD_NOT_STARTED; } else if (file_transfer.state == FAILED) { transmission_progress.state = DOWNLOAD_NOT_STARTED_FAILED_BEFORE; } } else if (file_transfer.direction == FileTransfer.DIRECTION_SENT) { if (file_transfer.state == IN_PROGRESS) { transmission_progress.state = UPLOADING; } else if (file_transfer.state == FAILED) { transmission_progress.state = UPLOAD_FAILED; } } } public async void load_from_file(File file, string file_name) throws GLib.Error { FixedRatioPicture image = new FixedRatioPicture() { min_width=100, min_height=100, max_width=600, max_height=300 }; // Work-around because Gtk.Picture does not apply the orientation itself Gdk.Pixbuf? pixbuf = new Pixbuf.from_file(file.get_path()); pixbuf = pixbuf.apply_embedded_orientation(); image.paintable = Texture.for_pixbuf(pixbuf); stack.add_child(image); stack.set_visible_child(image); } public async void load_from_thumbnail(FileTransfer file_transfer) throws GLib.Error { this.file_transfer = file_transfer; Gdk.Pixbuf? pixbuf = null; foreach (Xep.JingleContentThumbnails.Thumbnail thumbnail in file_transfer.thumbnails) { pixbuf = parse_thumbnail(thumbnail); if (pixbuf != null) { break; } } if (pixbuf == null) { warning("Can't load thumbnails of file %s", file_transfer.file_name); throw new Error(-1, 0, "Error loading preview image"); } // TODO: should this be executed? If yes, before or after scaling pixbuf = pixbuf.apply_embedded_orientation(); if (file_transfer.width > 0 && file_transfer.height > 0) { pixbuf = pixbuf.scale_simple(file_transfer.width, file_transfer.height, InterpType.BILINEAR); } else { warning("Preview: Not scaling image, width: %d, height: %d\n", file_transfer.width, file_transfer.height); } if (pixbuf == null) { warning("Can't scale thumbnail %s", file_transfer.file_name); throw new Error(-1, 0, "Error scaling preview image"); } FixedRatioPicture image = new FixedRatioPicture() { min_width=100, min_height=100, max_width=600, max_height=300 }; image.paintable = Texture.for_pixbuf(pixbuf); stack.add_child(image); stack.set_visible_child(image); } public void on_image_clicked(GestureClick gesture_click_controller, int n_press, double x, double y) { if (this.file_transfer.state != COMPLETE) return; switch (gesture_click_controller.get_device().source) { case Gdk.InputSource.TOUCHSCREEN: case Gdk.InputSource.PEN: if (n_press == 1) { image_overlay_toolbar.visible = !image_overlay_toolbar.visible; } else if (n_press == 2) { this.activate_action("file.open", null); image_overlay_toolbar.visible = false; } break; default: this.activate_action("file.open", null); image_overlay_toolbar.visible = false; break; } } public static Pixbuf? parse_thumbnail(Xep.JingleContentThumbnails.Thumbnail thumbnail) { string[] splits = thumbnail.uri.split(":", 2); if (splits.length != 2) { warning("Thumbnail parsing error: ':' not found"); return null; } if (splits[0] != "data") { warning("Unsupported thumbnail: unimplemented uri type\n"); return null; } splits = splits[1].split(";", 2); if (splits.length != 2) { warning("Thumbnail parsing error: ';' not found"); return null; } if (splits[0] != "image/png") { warning("Unsupported thumbnail: unsupported mime-type\n"); return null; } splits = splits[1].split(",", 2); if (splits.length != 2) { warning("Thumbnail parsing error: ',' not found"); return null; } if (splits[0] != "base64") { warning("Unsupported thumbnail: data is not base64 encoded\n"); return null; } uint8[] data = Base64.decode(splits[1]); MemoryInputStream input_stream = new MemoryInputStream.from_data(data); Pixbuf pixbuf = new Pixbuf.from_stream(input_stream); return pixbuf; } public static bool can_display(FileTransfer file_transfer) { return file_transfer.mime_type != null && Dino.Util.is_pixbuf_supported_mime_type(file_transfer.mime_type) && (file_transfer.state == FileTransfer.State.COMPLETE || file_transfer.thumbnails.size > 0); } public override void dispose() { if (overlay != null && overlay.parent != null) overlay.unparent(); base.dispose(); } } } dino-0.5.0/main/src/ui/conversation_content_view/file_transmission_progress.c0000664000000000000000000000146514776241610026431 0ustar rootroot#include GskPath *dino_ui_file_transmission_progress_create_progress_arc(GskPath *circle_path, gfloat percentage) { GskPathMeasure *measure = gsk_path_measure_new(circle_path); gfloat length = gsk_path_measure_get_length(measure); GskPathPoint start_point, end_point; g_return_val_if_fail(gsk_path_measure_get_point(measure, length * 0.75, &start_point), NULL); percentage += 0.75f; if (percentage > 1) percentage -= 1.0f; g_return_val_if_fail(gsk_path_measure_get_point(measure, length * percentage, &end_point), NULL); GskPathBuilder *builder = gsk_path_builder_new(); gsk_path_builder_add_segment(builder, circle_path, &start_point, &end_point); GskPath *arc_path = gsk_path_builder_free_to_path(builder); gsk_path_measure_unref(measure); return arc_path; }dino-0.5.0/main/src/ui/conversation_content_view/file_transmission_progress.vala0000664000000000000000000001223714776241610027131 0ustar rootrootusing Gee; using Gdk; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class FileTransmissionProgress : Adw.Bin { public enum State { UNKNOWN_SOURCE, DOWNLOAD_NOT_STARTED, DOWNLOAD_NOT_STARTED_FAILED_BEFORE, DOWNLOADING, UPLOADING, UPLOAD_FAILED } public int64 file_size { get; set; } public int64 transferred_size { get; set; } public State state { get; set; } private const int LINE_WIDTH = 4; private Button button = new Button(); private Adw.TimedAnimation progress_animation; construct { add_css_class("circular-osd"); button.add_css_class("circular"); button.margin_start = button.margin_end = button.margin_top = button.margin_bottom = LINE_WIDTH; this.set_child(button); this.button.clicked.connect(on_button_clicked); this.notify["transferred-size"].connect(update_progress); this.notify["state"].connect(on_state_changed); on_state_changed(); setup_animation(); } private void setup_animation() { progress_animation = new Adw.TimedAnimation(this, 0.0, 0.0, 250, new Adw.CallbackAnimationTarget(queue_draw)) { easing = Adw.Easing.LINEAR }; progress_animation.done.connect(update_progress); } private void on_state_changed() { sensitive = state != UNKNOWN_SOURCE && state != UPLOAD_FAILED; switch (this.state) { case UNKNOWN_SOURCE: case DOWNLOAD_NOT_STARTED: button.icon_name = "document-save-symbolic"; break; case DOWNLOADING: case UPLOADING: button.icon_name = "small-x-symbolic"; break; case DOWNLOAD_NOT_STARTED_FAILED_BEFORE: case UPLOAD_FAILED: button.icon_name = "dialog-warning-symbolic"; break; } } private void update_progress() { if (file_size == 0 || progress_animation == null) return; // For encrypted files, transferred size > file size. For PGP, the whole message is encrypted at once, making a better solution difficult. double next_value = ((double)transferred_size / (double)file_size).clamp(0, 1); if (progress_animation != null && progress_animation.value_to != next_value) { progress_animation.value_from = progress_animation.value; progress_animation.value_to = next_value; progress_animation.reset(); progress_animation.play(); } } #if GTK_4_14 private static extern Gsk.Path create_progress_arc(Gsk.Path circle, float percentage); #endif public override void snapshot(Gtk.Snapshot snapshot) { base.snapshot(snapshot); if ((state == State.DOWNLOADING || state == State.UPLOADING) && progress_animation.value > 0.01) { float radius = float.max(int.min(get_width(), get_height()) / 2, 1); float line_width = (float) LINE_WIDTH; snapshot.translate({get_width() / 2, get_height() / 2}); #if GTK_4_14 var fg_color = get_color(); get_style_context().lookup_color("accent_color", out fg_color); var builder = new Gsk.PathBuilder(); builder.add_circle({0, 0}, radius - line_width / 2); var circle_path = builder.to_path(); var stroke = new Gsk.Stroke(line_width); stroke.set_line_cap(Gsk.LineCap.ROUND); var arc_path = create_progress_arc(circle_path, (float) progress_animation.value); snapshot.append_stroke(arc_path, stroke, fg_color); #else var fg_color = get_style_context().get_color(); get_style_context().lookup_color("accent_color", out fg_color); var context = snapshot.append_cairo({{-radius, -radius}, {radius*2, radius*2}}); context.set_line_cap(Cairo.LineCap.ROUND); context.set_line_width(line_width); context.set_source_rgba(fg_color.red, fg_color.green, fg_color.blue, fg_color.alpha); context.arc(0, 0, radius - line_width / 2, -0.5 * Math.PI, (progress_animation.value - 0.25) * 2 * Math.PI); context.stroke(); #endif } } private void on_button_clicked() { switch (this.state) { case UNKNOWN_SOURCE: case UPLOAD_FAILED: break; case DOWNLOAD_NOT_STARTED_FAILED_BEFORE: case DOWNLOAD_NOT_STARTED: this.activate_action("file.download", null); break; case DOWNLOADING: case UPLOADING: this.activate_action("file.cancel", null); break; } } public override void dispose() { progress_animation = null; base.dispose(); } } } dino-0.5.0/main/src/ui/conversation_content_view/file_widget.vala0000664000000000000000000002132214776241610023732 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Dino.Entities; namespace Dino.Ui { public class FileMetaItem : ConversationSummary.ContentMetaItem { private StreamInteractor stream_interactor; private FileItem file_item; private FileTransfer file_transfer; public FileMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); this.stream_interactor = stream_interactor; this.file_item = content_item as FileItem; this.file_transfer = file_item.file_transfer; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { FileWidget widget = new FileWidget(file_transfer); FileWidgetController widget_controller = new FileWidgetController(widget, file_transfer, stream_interactor); return widget; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { if ((file_transfer.provider != FileManager.HTTP_PROVIDER_ID && file_transfer.provider != FileManager.SFS_PROVIDER_ID) || file_transfer.info == null) return null; Gee.List actions = new ArrayList(); if (stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(file_item.conversation, content_item) != null) { actions.add(get_reply_action(content_item, file_item.conversation, stream_interactor)); actions.add(get_reaction_action(content_item, file_item.conversation, stream_interactor)); } return actions; } } public class FileWidget : SizeRequestBin { enum State { IMAGE, DEFAULT } private FileTransfer file_transfer; public FileTransfer.State file_transfer_state { get; set; } public string file_transfer_mime_type { get; set; } private State? state = null; private FileDefaultWidgetController default_widget_controller; private Widget? content = null; public signal void open_file(); public signal void save_file_as(); public signal void start_download(); public signal void cancel_download(); class construct { install_action("file.open", null, (widget, action_name) => { ((FileWidget) widget).open_file(); }); install_action("file.save_as", null, (widget, action_name) => { ((FileWidget) widget).save_file_as(); }); install_action("file.download", null, (widget, action_name) => { ((FileWidget) widget).start_download(); }); install_action("file.cancel", null, (widget, action_name) => { ((FileWidget) widget).cancel_download(); }); } construct { margin_top = 4; size_request_mode = SizeRequestMode.HEIGHT_FOR_WIDTH; } public FileWidget(FileTransfer file_transfer) { this.file_transfer = file_transfer; update_widget.begin(); // size_allocate.connect((allocation) => { // if (allocation.height > parent.get_allocated_height()) { // Idle.add(() => { parent.queue_resize(); return false; }); // } // }); file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); this.notify["file-transfer-state"].connect(update_widget); this.notify["file-transfer-mime-type"].connect(update_widget); } private async void update_widget() { bool show_image = FileImageWidget.can_display(file_transfer); if (show_image && state != State.IMAGE) { var content_bak = content; FileImageWidget file_image_widget = null; try { file_image_widget = new FileImageWidget(); yield file_image_widget.set_file_transfer(file_transfer); // If the widget changed in the meanwhile, stop if (content != content_bak) return; if (content != null) content.unparent(); content = file_image_widget; state = State.IMAGE; content.insert_after(this, null); return; } catch (Error e) { } } if (!show_image && state != State.DEFAULT) { if (content != null) content.unparent(); FileDefaultWidget default_file_widget = new FileDefaultWidget(); default_widget_controller = new FileDefaultWidgetController(default_file_widget); default_widget_controller.set_file_transfer(file_transfer); content = default_file_widget; this.state = State.DEFAULT; content.insert_after(this, null); } } public override void dispose() { if (default_widget_controller != null) default_widget_controller.dispose(); default_widget_controller = null; if (content != null) { content.unparent(); content.dispose(); content = null; } base.dispose(); } } public class FileWidgetController : Object { private weak Widget widget; private FileTransfer file_transfer; private StreamInteractor? stream_interactor; public FileWidgetController(FileWidget widget, FileTransfer file_transfer, StreamInteractor? stream_interactor = null) { this.widget = widget; this.ref(); this.widget.weak_ref(() => { this.widget = null; this.unref(); }); this.file_transfer = file_transfer; this.stream_interactor = stream_interactor; widget.open_file.connect(open_file); widget.save_file_as.connect(save_file); widget.start_download.connect(start_download); widget.cancel_download.connect(cancel_download); } private void open_file() { try{ AppInfo.launch_default_for_uri(file_transfer.get_file().get_uri(), null); } catch (Error err) { warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message); } } private void save_file() { var save_dialog = new FileChooserNative(_("Save as…"), widget.get_root() as Gtk.Window, FileChooserAction.SAVE, null, null); save_dialog.set_modal(true); save_dialog.set_current_name(file_transfer.file_name); save_dialog.response.connect(() => { try{ GLib.File.new_for_uri(file_transfer.get_file().get_uri()).copy(save_dialog.get_file(), GLib.FileCopyFlags.OVERWRITE, null); } catch (Error err) { warning("Failed copy file %s - %s", file_transfer.get_file().get_uri(), err.message); } }); save_dialog.show(); } private void start_download() { if (stream_interactor != null) { stream_interactor.get_module(FileManager.IDENTITY).download_file.begin(file_transfer); } } private void cancel_download() { file_transfer.cancellable.cancel(); } } public class FileDefaultWidgetController : Object { private FileDefaultWidget widget; private FileTransfer? file_transfer; public string file_transfer_state { get; set; } public string file_transfer_mime_type { get; set; } public int64 file_transfer_transferred_bytes { get; set; } private FileTransfer.State state; public FileDefaultWidgetController(FileDefaultWidget widget) { this.widget = widget; widget.clicked.connect(on_clicked); this.notify["file-transfer-state"].connect(update_file_info); this.notify["file-transfer-mime-type"].connect(update_file_info); this.notify["file-transfer-transferred-bytes"].connect(update_file_info); } public void set_file_transfer(FileTransfer file_transfer) { this.file_transfer = file_transfer; widget.name_label.label = file_transfer.file_name; file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); file_transfer.bind_property("transferred-bytes", this, "file-transfer-transferred-bytes"); update_file_info(); } private void update_file_info() { state = file_transfer.state; widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.direction, file_transfer.size, file_transfer.transferred_bytes); } private void on_clicked() { switch (state) { case FileTransfer.State.COMPLETE: widget.activate_action("file.open", null); break; case FileTransfer.State.NOT_STARTED: widget.activate_action("file.download", null); break; default: // Clicking doesn't do anything in FAILED and IN_PROGRESS states break; } } } } dino-0.5.0/main/src/ui/conversation_content_view/item_actions.vala0000664000000000000000000000460614776241610024134 0ustar rootrootusing Dino.Entities; using Gtk; namespace Dino.Ui { public Plugins.MessageAction get_reaction_action(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { Plugins.MessageAction action = new Plugins.MessageAction(); action.name = "reaction"; action.icon_name = "dino-emoticon-add-symbolic"; action.tooltip = _("Add reaction"); action.callback = (variant) => { string emoji = variant.get_string(); stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); }; // Disable the button if reaction aren't possible. bool supports_reactions = stream_interactor.get_module(Reactions.IDENTITY).conversation_supports_reactions(conversation); string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (!supports_reactions) { action.tooltip = _("This conversation does not support reactions."); action.sensitive = false; } else if (message_id == null) { action.tooltip = "This message does not support reactions."; action.sensitive = false; } return action; } public Plugins.MessageAction get_reply_action(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { Plugins.MessageAction action = new Plugins.MessageAction(); action.name = "reply"; action.icon_name = "mail-reply-sender-symbolic"; action.tooltip = _("Reply"); action.callback = () => { GLib.Application.get_default().activate_action("quote", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(conversation.id), new GLib.Variant.int32(content_item.id) })); }; // Disable the button if replies aren't possible. string? message_id = stream_interactor.get_module(ContentItemStore.IDENTITY).get_message_id_for_content_item(conversation, content_item); if (message_id == null) { action.sensitive = false; if (conversation.type_.is_muc_semantic()) { action.tooltip = _("This conversation does not support replies."); } else { action.tooltip = "This message does not support replies."; } } return action; } }dino-0.5.0/main/src/ui/conversation_content_view/message_widget.vala0000664000000000000000000003500314776241610024440 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Xmpp; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class MessageMetaItem : ContentMetaItem { enum AdditionalInfo { NONE, PENDING, DELIVERY_FAILED } private StreamInteractor stream_interactor; private MessageItem message_item; public Message.Marked marked { get; set; } public Plugins.ConversationItemWidgetInterface outer = null; MessageItemEditMode? edit_mode = null; ChatTextViewController? controller = null; AdditionalInfo additional_info = AdditionalInfo.NONE; ulong realize_id = -1; ulong marked_notify_handler_id = -1; uint pending_timeout_id = -1; public Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true, vexpand=true }; public MessageMetaItem(ContentItem content_item, StreamInteractor stream_interactor) { base(content_item); message_item = content_item as MessageItem; this.stream_interactor = stream_interactor; stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect(on_received_correction); label.activate_link.connect(on_label_activate_link); Message message = ((MessageItem) content_item).message; if (message.direction == Message.DIRECTION_SENT && !(message.marked in Message.MARKED_RECEIVED)) { var binding = message.bind_property("marked", this, "marked"); marked_notify_handler_id = this.notify["marked"].connect(() => { // Currently "pending", but not anymore if (additional_info == AdditionalInfo.PENDING && message.marked != Message.Marked.SENDING && message.marked != Message.Marked.UNSENT) { update_label(); } // Currently "error", but not anymore if (additional_info == AdditionalInfo.DELIVERY_FAILED && message.marked != Message.Marked.ERROR) { update_label(); } // Currently not error, but should be if (additional_info != AdditionalInfo.DELIVERY_FAILED && message.marked == Message.Marked.ERROR) { update_label(); } // Nothing bad can happen anymore if (message.marked in Message.MARKED_RECEIVED) { binding.unbind(); this.disconnect(marked_notify_handler_id); marked_notify_handler_id = -1; } }); } update_label(); } private void generate_markup_text(ContentItem item, Label label) { MessageItem message_item = item as MessageItem; Conversation conversation = message_item.conversation; Message message = message_item.message; // Get a copy of the markup spans, such that we can modify them var markups = new ArrayList(); foreach (var markup in message.get_markups()) { markups.add(new Xep.MessageMarkup.Span() { types=markup.types, start_char=markup.start_char, end_char=markup.end_char }); } string markup_text = message.body; var attrs = new AttrList(); label.set_attributes(attrs); if (markup_text == null) return; // TODO remove // Only process messages up to a certain size if (markup_text.length > 10000) { markup_text = markup_text.substring(0, 10000) + " [" + _("Message too long") + "]"; } bool theme_dependent = false; markup_text = Util.remove_fallbacks_adjust_markups(markup_text, message.quoted_item_id > 0, message.get_fallbacks(), markups); var bold_attr = Pango.attr_weight_new(Pango.Weight.BOLD); var italic_attr = Pango.attr_style_new(Pango.Style.ITALIC); var strikethrough_attr = Pango.attr_strikethrough_new(true); // Prefix message with name instead of /me if (markup_text.has_prefix("/me ")) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, message.from); markup_text = display_name + " " + markup_text.substring(4); foreach (Xep.MessageMarkup.Span span in markups) { int length = display_name.char_count() - 4 + 1; span.start_char += length; span.end_char += length; } bold_attr.end_index = display_name.length; italic_attr.end_index = display_name.length; attrs.insert(bold_attr.copy()); attrs.insert(italic_attr.copy()); } foreach (var markup in markups) { foreach (var ty in markup.types) { Attribute attr = null; switch (ty) { case Xep.MessageMarkup.SpanType.EMPHASIS: attr = Pango.attr_style_new(Pango.Style.ITALIC); break; case Xep.MessageMarkup.SpanType.STRONG_EMPHASIS: attr = Pango.attr_weight_new(Pango.Weight.BOLD); break; case Xep.MessageMarkup.SpanType.DELETED: attr = Pango.attr_strikethrough_new(true); break; } attr.start_index = markup_text.index_of_nth_char(markup.start_char); attr.end_index = markup_text.index_of_nth_char(markup.end_char); attrs.insert(attr.copy()); } } // Work around pango bug markup_text = Util.unbreak_space_around_non_spacing_mark((owned) markup_text); if (conversation.type_ == Conversation.Type.GROUPCHAT) { markup_text = Util.parse_add_markup_theme(markup_text, conversation.nickname, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } else { markup_text = Util.parse_add_markup_theme(markup_text, null, true, true, true, Util.is_dark_theme(this.label), ref theme_dependent); } int only_emoji_count = Util.get_only_emoji_count(markup_text); if (only_emoji_count != -1) { string size_str = only_emoji_count < 5 ? "xx-large" : "large"; markup_text = @"" + markup_text + ""; } string dim_color = Util.is_dark_theme(this.label) ? "#BDBDBD" : "#707070"; if (message.edit_to != null) { markup_text += @" (%s)".printf(_("edited")); theme_dependent = true; } // Append message status info additional_info = AdditionalInfo.NONE; if (message.direction == Message.DIRECTION_SENT && (message.marked == Message.Marked.SENDING || message.marked == Message.Marked.UNSENT)) { // Append "pending..." iff message has not been sent yet if (message.time.compare(new DateTime.now_utc().add_seconds(-10)) < 0) { markup_text += @" %s".printf(_("pending…")); theme_dependent = true; additional_info = AdditionalInfo.PENDING; } else { int time_diff = (- (int) message.time.difference(new DateTime.now_utc()) / 1000); if (pending_timeout_id != -1) Source.remove(pending_timeout_id); pending_timeout_id = Timeout.add(10000 - time_diff, () => { update_label(); pending_timeout_id = -1; return false; }); } } else if (message.direction == Message.DIRECTION_SENT && message.marked == Message.Marked.ERROR) { // Append "delivery failed" if there was a server error string error_color = Util.rgba_to_hex(Util.get_label_pango_color(label, "@error_color")); markup_text += " %s".printf(error_color, _("delivery failed")); theme_dependent = true; additional_info = AdditionalInfo.DELIVERY_FAILED; } if (theme_dependent && realize_id == -1) { realize_id = label.realize.connect(update_label); } else if (!theme_dependent && realize_id != -1) { label.disconnect(realize_id); } label.label = markup_text; } public void update_label() { generate_markup_text(content_item, label); } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { this.outer = outer; this.notify["in-edit-mode"].connect(on_in_edit_mode_changed); outer.set_widget(label, Plugins.WidgetType.GTK4, 2); if (message_item.message.quoted_item_id > 0) { var quoted_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(message_item.conversation, message_item.message.quoted_item_id); if (quoted_content_item != null) { var quote_model = new Quote.Model.from_content_item(quoted_content_item, message_item.conversation, stream_interactor); quote_model.jump_to.connect(() => { GLib.Application.get_default().activate_action("jump-to-conversation-message", new GLib.Variant.tuple(new GLib.Variant[] { new GLib.Variant.int32(message_item.conversation.id), new GLib.Variant.int32(quoted_content_item.id) })); }); var quote_widget = Quote.get_widget(quote_model); outer.set_widget(quote_widget, Plugins.WidgetType.GTK4, 1); } } return label; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { if (in_edit_mode) return null; Gee.List actions = new ArrayList(); bool correction_allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); if (correction_allowed) { Plugins.MessageAction action1 = new Plugins.MessageAction(); action1.name = "correction"; action1.icon_name = "document-edit-symbolic"; action1.tooltip = _("Edit message"); action1.callback = () => { this.in_edit_mode = true; }; actions.add(action1); } actions.add(get_reply_action(content_item, message_item.conversation, stream_interactor)); actions.add(get_reaction_action(content_item, message_item.conversation, stream_interactor)); return actions; } private void on_in_edit_mode_changed() { if (in_edit_mode == false) return; bool allowed = stream_interactor.get_module(MessageCorrection.IDENTITY).is_own_correction_allowed(message_item.conversation, message_item.message); if (allowed) { MessageItem message_item = content_item as MessageItem; Message message = message_item.message; edit_mode = new MessageItemEditMode(); controller = new ChatTextViewController(edit_mode.chat_text_view, stream_interactor); Conversation conversation = message_item.conversation; controller.initialize_for_conversation(conversation); edit_mode.cancelled.connect(() => { in_edit_mode = false; outer.set_widget(label, Plugins.WidgetType.GTK4, 2); }); edit_mode.send.connect(() => { string text = edit_mode.chat_text_view.text_view.buffer.text; var markups = edit_mode.chat_text_view.get_markups(); Dino.send_message(message_item.conversation, text, message_item.message.quoted_item_id, message_item.message, markups); in_edit_mode = false; outer.set_widget(label, Plugins.WidgetType.GTK4, 2); }); edit_mode.chat_text_view.set_text(message); outer.set_widget(edit_mode, Plugins.WidgetType.GTK4, 2); edit_mode.chat_text_view.text_view.grab_focus(); } else { this.in_edit_mode = false; } } private void on_received_correction(ContentItem content_item) { if (this.content_item.id == content_item.id) { this.content_item = content_item; message_item = content_item as MessageItem; update_label(); } } public static bool on_label_activate_link(string uri) { // Always handle xmpp URIs with Dino if (!uri.has_prefix("xmpp:")) return false; File file = File.new_for_uri(uri); Dino.Application.get_default().open(new File[]{file}, ""); return true; } public override void dispose() { stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.disconnect(on_received_correction); this.notify["in-edit-mode"].disconnect(on_in_edit_mode_changed); if (marked_notify_handler_id != -1) { this.disconnect(marked_notify_handler_id); } if (realize_id != -1) { label.disconnect(realize_id); } if (pending_timeout_id != -1) { Source.remove(pending_timeout_id); } if (label != null) { label.unparent(); label.dispose(); label = null; } base.dispose(); } } [GtkTemplate (ui = "/im/dino/Dino/message_item_widget_edit_mode.ui")] public class MessageItemEditMode : Box { public signal void cancelled(); public signal void send(); [GtkChild] public unowned MenuButton emoji_button; [GtkChild] public unowned ChatTextView chat_text_view; [GtkChild] public unowned Button cancel_button; [GtkChild] public unowned Button send_button; [GtkChild] public unowned Frame frame; construct { Util.force_css(frame, "* { border-radius: 3px; padding: 0px 7px; }"); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { chat_text_view.text_view.buffer.insert_at_cursor(emoji, emoji.data.length); }); emoji_button.set_popover(chooser); chat_text_view.text_view.buffer.changed.connect_after(on_text_view_changed); cancel_button.clicked.connect(() => cancelled()); send_button.clicked.connect(() => send()); chat_text_view.cancel_input.connect(() => cancelled()); chat_text_view.send_text.connect(() => send()); } private void on_text_view_changed() { send_button.sensitive = chat_text_view.text_view.buffer.text != ""; } } } dino-0.5.0/main/src/ui/conversation_content_view/quote_widget.vala0000664000000000000000000000713514776241610024156 0ustar rootrootusing Dino.Ui.ConversationSummary; using Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui.Quote { public class Model : Object { public signal void aborted(); public signal void jump_to(); public string display_name { get; set; } public string message { get; set; } public string display_time { get; set; } public DateTime message_time { get; set; } public StreamInteractor stream_interactor { get; set; } public Conversation conversation { get; set; } public Jid author_jid { get; set; } public bool can_abort { get; set; default=false; } private uint display_time_timeout; public Model.from_content_item(ContentItem content_item, Conversation conversation, StreamInteractor stream_interactor) { this.display_name = Util.get_participant_display_name(stream_interactor, conversation, content_item.jid, true); if (content_item.type_ == MessageItem.TYPE) { var message = ((MessageItem) content_item).message; this.message = Dino.message_body_without_reply_fallback(message); } else if (content_item.type_ == FileItem.TYPE) { var file_transfer = ((FileItem) content_item).file_transfer; this.message = _("File") + ": " + file_transfer.file_name; } this.message_time = content_item.time; update_display_time(); this.stream_interactor = stream_interactor; this.conversation = conversation; this.author_jid = content_item.jid; } private void on_display_time_timeout() { if (display_time_timeout != 0) update_display_time(); } private void update_display_time() { this.display_time = ConversationItemSkeleton.get_relative_time(message_time.to_local()); display_time_timeout = Dino.WeakTimeout.add_seconds_once((int) ConversationItemSkeleton.get_next_time_change(message_time), this, on_display_time_timeout); } public override void dispose() { base.dispose(); if (display_time_timeout != 0) { Source.remove(display_time_timeout); display_time_timeout = 0; } } } public Widget get_widget(Model model) { Builder builder = new Builder.from_resource("/im/dino/Dino/quote.ui"); AvatarPicture avatar = (AvatarPicture) builder.get_object("avatar"); Label author = (Label) builder.get_object("author"); Label time = (Label) builder.get_object("time"); Label message = (Label) builder.get_object("message"); Button abort_button = (Button) builder.get_object("abort-button"); avatar.model = new ViewModel.CompatAvatarPictureModel(model.stream_interactor).add_participant(model.conversation, model.author_jid); model.bind_property("display-name", author, "label", BindingFlags.SYNC_CREATE); model.bind_property("display-time", time, "label", BindingFlags.SYNC_CREATE); model.bind_property("message", message, "label", BindingFlags.SYNC_CREATE); model.bind_property("can-abort", abort_button, "visible", BindingFlags.SYNC_CREATE); abort_button.clicked.connect(() => { model.aborted(); }); Widget outer = builder.get_object("outer") as Widget; GestureClick gesture_click_controller = new GestureClick(); outer.add_controller(gesture_click_controller); gesture_click_controller.pressed.connect(() => { model.jump_to(); }); return outer; } } dino-0.5.0/main/src/ui/conversation_content_view/reactions_widget.vala0000664000000000000000000001570314776241610025010 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { public class ReactionsController : Object { public signal void box_activated(Widget widget); private Conversation conversation; private Account account; private ContentItem content_item; private StreamInteractor stream_interactor; private HashMap> reactions = new HashMap>(); private ReactionsWidget? widget = null; public ReactionsController(Conversation conversation, ContentItem content_item, StreamInteractor stream_interactor) { this.conversation = conversation; this.account = conversation.account; this.content_item = content_item; this.stream_interactor = stream_interactor; } public void init() { Gee.List reactions = stream_interactor.get_module(Reactions.IDENTITY).get_item_reactions(conversation, content_item); foreach (ReactionUsers reaction_users in reactions) { foreach (Jid jid in reaction_users.jids) { reaction_added(reaction_users.reaction, jid); } } stream_interactor.get_module(Reactions.IDENTITY).reaction_added.connect((account, content_item_id, jid, reaction) => { if (this.content_item.id == content_item_id) { reaction_added(reaction, jid); } }); stream_interactor.get_module(Reactions.IDENTITY).reaction_removed.connect((account, content_item_id, jid, reaction) => { if (this.content_item.id == content_item_id) { reaction_removed(reaction, jid); } }); } private void initialize_widget() { widget = new ReactionsWidget(); widget.emoji_picked.connect((emoji) => { stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); }); widget.emoji_clicked.connect((emoji) => { if (account.bare_jid in reactions[emoji]) { stream_interactor.get_module(Reactions.IDENTITY).remove_reaction(conversation, content_item, emoji); } else { stream_interactor.get_module(Reactions.IDENTITY).add_reaction(conversation, content_item, emoji); } }); box_activated(widget); } public void reaction_added(string reaction, Jid jid) { if (widget == null) { initialize_widget(); } if (!reactions.has_key(reaction)) { reactions[reaction] = new ArrayList(Jid.equals_func); } if (jid.equals_bare(account.bare_jid) && reactions[reaction].contains(jid)) { return; } reactions[reaction].add(jid); if (reactions[reaction].size == 0) return; widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); } public void reaction_removed(string reaction, Jid jid) { if (!reactions.has_key(reaction)) return; reactions[reaction].remove(jid); if (reactions[reaction].size > 0) { widget.update_reaction(reaction, reactions[reaction].size, reactions[reaction].contains(account.bare_jid), update_tooltip(reaction)); } else { widget.remove_reaction(reaction); reactions.unset(reaction); } if (reactions.size == 0) { widget.unparent(); widget = null; } } private Gee.List update_tooltip(string reaction) { var name_list = new ArrayList(); if (reactions[reaction].size > 0) { if (account.bare_jid in reactions[reaction]) { name_list.add(_("You")); } foreach (Jid jid in reactions[reaction]) { if (jid.equals(account.bare_jid)) continue; name_list.add(Util.get_participant_display_name(stream_interactor, conversation, jid)); } } return name_list; } } public class ReactionsWidget : Grid { public signal void emoji_picked(string emoji); public signal void emoji_clicked(string emoji); private HashMap reaction_counts = new HashMap(); private HashMap reaction_buttons = new HashMap(); private MenuButton add_button; public ReactionsWidget() { this.row_spacing = this.column_spacing = 5; this.margin_top = 2; this.add_css_class("reaction-grid"); add_button = new MenuButton() { tooltip_text= _("Add reaction") }; add_button.add_css_class("pill"); Util.menu_button_set_icon_with_size(add_button, "dino-emoticon-add-symbolic", 14); EmojiChooser chooser = new EmojiChooser(); chooser.emoji_picked.connect((emoji) => { emoji_picked(emoji); }); add_button.set_popover(chooser); } public void update_reaction(string reaction, int count, bool own, Gee.List names) { if (!reaction_buttons.has_key(reaction)) { Label reaction_label = new Label("" + reaction + "") { use_markup=true }; Label count_label = new Label("") { use_markup=true }; Button button = new Button(); button.add_css_class("pill"); Box reaction_box = new Box(Orientation.HORIZONTAL, 4) { halign=Align.CENTER }; reaction_box.append(reaction_label); reaction_box.append(count_label); button.set_child(reaction_box); reaction_counts[reaction] = count_label; reaction_buttons[reaction] = button; this.attach(button, (reaction_buttons.size - 1) % 10, (reaction_buttons.size - 1) / 10, 1, 1); if (add_button.get_parent() != null) this.remove(add_button); this.attach(add_button, reaction_buttons.size % 10, reaction_buttons.size / 10, 1, 1); button.clicked.connect(() => { emoji_clicked(reaction); }); } reaction_counts[reaction].label = "" + count.to_string() + ""; if (own) { reaction_buttons[reaction].add_css_class("own-reaction"); } else { reaction_buttons[reaction].remove_css_class("own-reaction"); } // Build tooltip StringBuilder tooltip_builder = new StringBuilder (); for (int i = 0; i < names.size - 1; i++) { tooltip_builder.append(names[i]); if (i < names.size - 2) tooltip_builder.append(", "); } if (names.size > 1) { tooltip_builder.append(" and "); } tooltip_builder.append(names[names.size - 1]); tooltip_builder.append(" reacted with " + reaction); reaction_buttons[reaction].set_tooltip_text(tooltip_builder.str); } public void remove_reaction(string reaction) { reaction_buttons[reaction].unparent(); } } }dino-0.5.0/main/src/ui/conversation_content_view/subscription_notification.vala0000664000000000000000000001063514776241610026747 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui.ConversationSummary { public class SubscriptionNotitication : Object { private StreamInteractor stream_interactor; private Conversation conversation; private ConversationView conversation_view; public SubscriptionNotitication(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(PresenceManager.IDENTITY).received_subscription_request.connect((jid, account) => { Conversation relevant_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(jid, account, Conversation.Type.CHAT); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(relevant_conversation); if (conversation != null && account.equals(conversation.account) && jid.equals(conversation.counterpart)) { show_pending_subscription_request(); } }); } public void init(Conversation conversation, ConversationView conversation_view) { this.conversation = conversation; this.conversation_view = conversation_view; if (conversation.type_ != Conversation.Type.CHAT) return; if (stream_interactor.get_module(PresenceManager.IDENTITY).exists_subscription_request(conversation.account, conversation.counterpart)) { // Show a notification of a pending subscription request show_pending_subscription_request(); } else if (!conversation.counterpart.equals_bare(conversation.account.bare_jid)) { // Show a suggestion to request subscription if: We don't have subscription yet and didn't yet request it // Don't show this notification for chats with ourselves var roster_item = stream_interactor.get_module(RosterManager.IDENTITY).get_roster_item(conversation.account, conversation.counterpart); if (roster_item == null || (roster_item.subscription == Xmpp.Roster.Item.SUBSCRIPTION_NONE || roster_item.subscription == Xmpp.Roster.Item.SUBSCRIPTION_FROM) && !roster_item.subscription_requested) { show_no_subscription(roster_item != null); } } } private void show_no_subscription(bool already_in_roster) { Box box = new Box(Orientation.HORIZONTAL, 5); Button accept_button = new Button.with_label(_("Send request")); GLib.Application app = GLib.Application.get_default(); accept_button.clicked.connect(() => { if (!already_in_roster) { stream_interactor.get_module(RosterManager.IDENTITY).add_jid(conversation.account, conversation.counterpart, null); } stream_interactor.get_module(PresenceManager.IDENTITY).request_subscription(conversation.account, conversation.counterpart); app.activate_action("accept-subscription", conversation.id); ((Dino.Ui.Application) app).window.conversation_view.chat_input.chat_text_view.text_view.grab_focus(); conversation_view.remove_notification(box); }); box.append(new Label(_("You do not receive status updates from this contact yet.")) { margin_end=10 }); box.append(accept_button); conversation_view.add_notification(box); } private void show_pending_subscription_request() { Box box = new Box(Orientation.HORIZONTAL, 5); Button accept_button = new Button.with_label(_("Accept")); Button deny_button = new Button.with_label(_("Deny")); GLib.Application app = GLib.Application.get_default(); accept_button.clicked.connect(() => { app.activate_action("accept-subscription", conversation.id); ((Dino.Ui.Application) app).window.conversation_view.chat_input.chat_text_view.text_view.grab_focus(); conversation_view.remove_notification(box); }); deny_button.clicked.connect(() => { app.activate_action("deny-subscription", conversation.id); ((Dino.Ui.Application) app).window.conversation_view.chat_input.chat_text_view.text_view.grab_focus(); conversation_view.remove_notification(box); }); box.append(new Label(_("This contact would like to add you to their contact list")) { margin_end=10 }); box.append(accept_button); box.append(deny_button); conversation_view.add_notification(box); } } } dino-0.5.0/main/src/ui/conversation_content_view/unread_indicator_populator.vala0000664000000000000000000000700114776241610027065 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.ConversationSummary { class UnreadIndicatorPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "unread_indicator"; } } private StreamInteractor stream_interactor; private Conversation? current_conversation; private UnreadIndicatorItem? unread_indicator = null; Plugins.ConversationItemCollection item_collection = null; public UnreadIndicatorPopulator(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; stream_interactor.get_module(ChatInteraction.IDENTITY).focused_out.connect(() => { update_unread_indicator(); }); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(() => { if (!stream_interactor.get_module(ChatInteraction.IDENTITY).is_active_focus(current_conversation)) { update_unread_indicator(); } }); } private void update_unread_indicator() { if (current_conversation == null) return; ContentItem? read_up_to_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(current_conversation, current_conversation.read_up_to_item); int current_num_unread = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(current_conversation); if (current_num_unread == 0 && unread_indicator != null) { item_collection.remove_item(unread_indicator); unread_indicator = null; } if (read_up_to_item != null && current_num_unread > 0) { if (unread_indicator != null) { item_collection.remove_item(unread_indicator); } unread_indicator = new UnreadIndicatorItem(read_up_to_item); item_collection.insert_item(unread_indicator); } } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; update_unread_indicator(); } public void close(Conversation conversation) { } public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } } private class UnreadIndicatorItem : Plugins.MetaConversationItem { public UnreadIndicatorItem(ContentItem after_item) { this.time = after_item.time; this.secondary_sort_indicator = int.MAX; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType type) { Box box = new Box(Orientation.HORIZONTAL, 10) { hexpand=true }; box.get_style_context().add_class("dino-unread-line"); Separator sep = new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }; box.append(sep); Label label = new Label(_("New")) { halign=Align.END, hexpand=false }; label.attributes = new Pango.AttrList(); label.attributes.insert(Pango.attr_weight_new(Pango.Weight.BOLD)); box.append(label); return box; } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } } dino-0.5.0/main/src/ui/conversation_details.vala0000664000000000000000000002633314776241610020400 0ustar rootrootusing Dino; using Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; namespace Dino.Ui.ConversationDetails { public void populate_dialog(Model.ConversationDetails model, Conversation conversation, StreamInteractor stream_interactor) { model.conversation = conversation; model.display_name = stream_interactor.get_module(ContactModels.IDENTITY).get_display_name_model(conversation); model.blocked = stream_interactor.get_module(BlockingManager.IDENTITY).is_blocked(model.conversation.account, model.conversation.counterpart); model.domain_blocked = stream_interactor.get_module(BlockingManager.IDENTITY).is_blocked(model.conversation.account, model.conversation.counterpart.domain_jid); if (conversation.type_ == Conversation.Type.GROUPCHAT) { stream_interactor.get_module(MucManager.IDENTITY).get_config_form.begin(conversation.account, conversation.counterpart, (_, res) => { model.data_form = stream_interactor.get_module(MucManager.IDENTITY).get_config_form.end(res); if (model.data_form == null) return; model.data_form_bak = model.data_form.stanza_node.to_string(); }); } } public void bind_dialog(Model.ConversationDetails model, ViewModel.ConversationDetails view_model, StreamInteractor stream_interactor) { view_model.avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(model.conversation); view_model.show_blocked = model.conversation.type_ == Conversation.Type.CHAT && stream_interactor.get_module(BlockingManager.IDENTITY).is_supported(model.conversation.account); view_model.members_sorted.set_model(model.members); view_model.members.set_map_func((item) => { var conference_member = (Ui.Model.ConferenceMember) item; Jid? nick_jid = stream_interactor.get_module(MucManager.IDENTITY).get_occupant_jid(model.conversation.account, model.conversation.counterpart, conference_member.jid); return new Ui.ViewModel.ConferenceMemberListRow() { avatar = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(model.conversation, conference_member.jid), name = nick_jid != null ? nick_jid.resourcepart : conference_member.jid.localpart, jid = conference_member.jid.to_string(), affiliation = conference_member.affiliation }; }); if (model.domain_blocked) { view_model.blocked = DOMAIN; } else if (model.blocked) { view_model.blocked = USER; } else { view_model.blocked = UNBLOCK; } model.display_name.bind_property("display-name", view_model, "name", BindingFlags.SYNC_CREATE); model.conversation.bind_property("notify-setting", view_model, "notification", BindingFlags.SYNC_CREATE, (_, from, ref to) => { switch (model.conversation.get_notification_setting(stream_interactor)) { case ON: to = ViewModel.ConversationDetails.NotificationSetting.ON; break; case OFF: to = ViewModel.ConversationDetails.NotificationSetting.OFF; break; case HIGHLIGHT: to = ViewModel.ConversationDetails.NotificationSetting.HIGHLIGHT; break; case DEFAULT: // A "default" setting should have been resolved to the actual default value assert_not_reached(); } return true; }); model.conversation.bind_property("notify-setting", view_model, "notification-is-default", BindingFlags.SYNC_CREATE, (_, from, ref to) => { var notify_setting = (Conversation.NotifySetting) from; to = notify_setting == Conversation.NotifySetting.DEFAULT; return true; }); model.conversation.bind_property("pinned", view_model, "pinned", BindingFlags.SYNC_CREATE, (_, from, ref to) => { var from_int = (int) from; to = from_int > 0; return true; }); model.conversation.bind_property("type-", view_model, "notification-options", BindingFlags.SYNC_CREATE, (_, from, ref to) => { var ty = (Conversation.Type) from; to = ty == Conversation.Type.GROUPCHAT ? ViewModel.ConversationDetails.NotificationOptions.ON_HIGHLIGHT_OFF : ViewModel.ConversationDetails.NotificationOptions.ON_OFF; return true; }); model.bind_property("data-form", view_model, "room-configuration-rows", BindingFlags.SYNC_CREATE, (_, from, ref to) => { var data_form = (DataForms.DataForm) from; if (data_form == null) return true; var list_store = new GLib.ListStore(typeof(ViewModel.PreferencesRow.Any)); foreach (var field in data_form.fields) { var field_view_model = Util.get_data_form_field_view_model(field); if (field_view_model != null) { list_store.append(field_view_model); } } to = list_store; return true; }); view_model.pin_changed.connect(() => { model.conversation.pinned = model.conversation.pinned == 1 ? 0 : 1; }); view_model.block_changed.connect((action) => { switch (action) { case USER: stream_interactor.get_module(BlockingManager.IDENTITY).block(model.conversation.account, model.conversation.counterpart); stream_interactor.get_module(BlockingManager.IDENTITY).unblock(model.conversation.account, model.conversation.counterpart.domain_jid); break; case DOMAIN: stream_interactor.get_module(BlockingManager.IDENTITY).block(model.conversation.account, model.conversation.counterpart.domain_jid); break; case UNBLOCK: stream_interactor.get_module(BlockingManager.IDENTITY).unblock(model.conversation.account, model.conversation.counterpart); stream_interactor.get_module(BlockingManager.IDENTITY).unblock(model.conversation.account, model.conversation.counterpart.domain_jid); break; } view_model.blocked = action; }); view_model.notification_changed.connect((setting) => { switch (setting) { case ON: model.conversation.notify_setting = ON; break; case OFF: model.conversation.notify_setting = OFF; break; case HIGHLIGHT: model.conversation.notify_setting = HIGHLIGHT; break; case DEFAULT: model.conversation.notify_setting = DEFAULT; break; } }); view_model.notification_flipped.connect(() => { model.conversation.notify_setting = view_model.notification == ON ? Conversation.NotifySetting.OFF : Conversation.NotifySetting.ON; }); } public Dialog setup_dialog(Conversation conversation, StreamInteractor stream_interactor, Window parent) { var dialog = new Dialog() { transient_for = parent }; var model = new Model.ConversationDetails(); model.populate(stream_interactor, conversation); // populate_dialog(model, conversation, stream_interactor); bind_dialog(model, dialog.model, stream_interactor); dialog.model.about_rows.append(new ViewModel.PreferencesRow.Text() { title = _("XMPP Address"), text = conversation.counterpart.to_string() }); if (model.conversation.type_ == Conversation.Type.CHAT) { var about_row = new ViewModel.PreferencesRow.Entry() { title = _("Display name"), text = dialog.model.name }; about_row.changed.connect(() => { if (about_row.text != Util.get_conversation_display_name(stream_interactor, conversation)) { stream_interactor.get_module(RosterManager.IDENTITY).set_jid_handle(conversation.account, conversation.counterpart, about_row.text); } }); dialog.model.about_rows.append(about_row); } if (model.conversation.type_ == Conversation.Type.GROUPCHAT) { var topic = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account); if (topic != null && topic != "") { dialog.model.about_rows.append(new ViewModel.PreferencesRow.Text() { title = _("Topic"), text = Util.parse_add_markup(topic, null, true, true) }); } } dialog.close_request.connect(() => { // Only send the config form if something was changed if (model.data_form_bak != null && model.data_form_bak != model.data_form.stanza_node.to_string()) { stream_interactor.get_module(MucManager.IDENTITY).set_config_form.begin(conversation.account, conversation.counterpart, model.data_form); } return false; }); Plugins.ContactDetails contact_details = new Plugins.ContactDetails(); contact_details.add.connect((c, l, d, wo) => { add_entry(c, l, d, wo, dialog); }); Application app = GLib.Application.get_default() as Application; app.plugin_registry.register_contact_details_entry(new ContactDetails.SettingsProvider(stream_interactor)); app.plugin_registry.register_contact_details_entry(new ContactDetails.PermissionsProvider(stream_interactor)); foreach (Plugins.ContactDetailsProvider provider in app.plugin_registry.contact_details_entries) { var preferences_group = (Adw.PreferencesGroup) provider.get_widget(conversation); if (preferences_group != null) { dialog.add_encryption_tab_element((Adw.PreferencesGroup) provider.get_widget(conversation)); } provider.populate(conversation, contact_details, Plugins.WidgetType.GTK4); } return dialog; } private void add_entry(string category, string label, string? description, Object wo, Dialog dialog) { if (!(wo is Widget)) return; Widget widget = (Widget) wo; if (widget.get_type().is_a(typeof(Entry))) { Util.EntryLabelHybrid hybrid = new Util.EntryLabelHybrid.wrap(widget as Entry) { xalign=1 }; widget = hybrid; } else if (widget.get_type().is_a(typeof(ComboBoxText))) { Util.ComboBoxTextLabelHybrid hybrid = new Util.ComboBoxTextLabelHybrid.wrap(widget as ComboBoxText) { xalign=1 }; widget = hybrid; } var view_model = new ViewModel.PreferencesRow.WidgetDeprecated() { title = label, widget = widget }; switch (category) { case "Permissions": case "Local Settings": case "Settings": dialog.model.settings_rows.append(view_model); break; default: break; } } } dino-0.5.0/main/src/ui/conversation_list_titlebar.vala0000664000000000000000000000206614776241610021611 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { public static Adw.HeaderBar get_conversation_list_titlebar() { Builder builder = new Builder.from_resource("/im/dino/Dino/conversation_list_titlebar.ui"); MenuButton add_button = (MenuButton) builder.get_object("add_button"); MenuButton menu_button = (MenuButton) builder.get_object("menu_button"); create_add_menu(add_button, menu_button); return (Adw.HeaderBar) builder.get_object("header_bar"); } private static void create_add_menu(MenuButton add_button, MenuButton menu_button) { add_button.tooltip_text = Util.string_if_tooltips_active(_("Start Conversation")); Builder add_builder = new Builder.from_resource("/im/dino/Dino/menu_add.ui"); MenuModel add_menu_model = add_builder.get_object("menu_add") as MenuModel; add_button.set_menu_model(add_menu_model); Builder menu_builder = new Builder.from_resource("/im/dino/Dino/menu_app.ui"); MenuModel menu_menu_model = menu_builder.get_object("menu_app") as MenuModel; menu_button.set_menu_model(menu_menu_model); } } dino-0.5.0/main/src/ui/conversation_selector/0000775000000000000000000000000014776241610017717 5ustar rootrootdino-0.5.0/main/src/ui/conversation_selector/conversation_selector.vala0000664000000000000000000001366614776241610025212 0ustar rootrootusing Gdk; using Gee; using Gtk; using Xmpp; using Dino.Entities; namespace Dino.Ui { public class ConversationSelector : Widget { public signal void conversation_selected(Conversation conversation); ListBox list_box = new ListBox() { hexpand=true }; private StreamInteractor stream_interactor; private HashMap rows = new HashMap(Conversation.hash_func, Conversation.equals_func); public ConversationSelector init(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; list_box.set_parent(this); this.layout_manager = new BinLayout(); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(add_conversation); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(remove_conversation); stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect(on_content_item_received); Timeout.add_seconds(60, () => { foreach (ConversationSelectorRow row in rows.values) row.update(); return true; }); foreach (Conversation conversation in stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations()) { add_conversation(conversation); } return this; } construct { list_box.set_sort_func(sort); realize.connect(() => { ListBoxRow? first_row = list_box.get_row_at_index(0); if (first_row != null) { list_box.select_row(first_row); row_activated(first_row); } }); list_box.row_activated.connect(row_activated); } public void row_activated(ListBoxRow r) { ConversationSelectorRow? row = r as ConversationSelectorRow; if (row != null) { conversation_selected(row.conversation); } } public void on_conversation_selected(Conversation conversation) { if (!rows.has_key(conversation)) { add_conversation(conversation); } list_box.select_row(rows[conversation]); } private void on_content_item_received(ContentItem item, Conversation conversation) { if (rows.has_key(conversation)) { list_box.invalidate_sort(); } } private void add_conversation(Conversation conversation) { ConversationSelectorRow row; if (!rows.has_key(conversation)) { conversation.notify["pinned"].connect(list_box.invalidate_sort); row = new ConversationSelectorRow(stream_interactor, conversation); rows[conversation] = row; list_box.append(row); row.main_revealer.set_reveal_child(true); // Set up drag motion behaviour (select conversation after timeout) DropControllerMotion drop_motion_controller = new DropControllerMotion(); uint drag_timeout = 0; drop_motion_controller.motion.connect((x, y) => { if (drag_timeout != 0) return; drag_timeout = Timeout.add(200, () => { conversation_selected(conversation); drag_timeout = 0; return false; }); }); drop_motion_controller.leave.connect(() => { if (drag_timeout != 0) { Source.remove(drag_timeout); drag_timeout = 0; } }); row.add_controller(drop_motion_controller); } list_box.invalidate_sort(); } private void select_fallback_conversation(Conversation conversation) { if (list_box.get_selected_row() == rows[conversation]) { int index = rows[conversation].get_index(); ListBoxRow? next_select_row = list_box.get_row_at_index(index + 1); if (next_select_row == null) { next_select_row = list_box.get_row_at_index(index - 1); } if (next_select_row != null) { list_box.select_row(next_select_row); row_activated(next_select_row); } } } private async void remove_conversation(Conversation conversation) { select_fallback_conversation(conversation); if (rows.has_key(conversation)) { conversation.notify["pinned"].disconnect(list_box.invalidate_sort); ConversationSelectorRow conversation_row; rows.unset(conversation, out conversation_row); yield conversation_row.colapse(); list_box.remove(conversation_row); } } public void loop_conversations(bool backwards) { int index = list_box.get_selected_row().get_index(); int new_index = ((index + (backwards ? -1 : 1)) + rows.size) % rows.size; ListBoxRow? next_select_row = list_box.get_row_at_index(new_index); if (next_select_row != null) { list_box.select_row(next_select_row); row_activated(next_select_row); } } private int sort(ListBoxRow row1, ListBoxRow row2) { ConversationSelectorRow cr1 = row1 as ConversationSelectorRow; ConversationSelectorRow cr2 = row2 as ConversationSelectorRow; if (cr1 != null && cr2 != null) { Conversation c1 = cr1.conversation; Conversation c2 = cr2.conversation; int pin_comp = c2.pinned - c1.pinned; if (pin_comp != 0) return pin_comp; if (c1.last_active == null) return -1; if (c2.last_active == null) return 1; int comp = c2.last_active.compare(c1.last_active); if (comp == 0) { return Util.get_conversation_display_name(stream_interactor, c1) .collate(Util.get_conversation_display_name(stream_interactor, c2)); } else { return comp; } } return 0; } } } dino-0.5.0/main/src/ui/conversation_selector/conversation_selector_row.vala0000664000000000000000000003745314776241610026101 0ustar rootrootusing Gee; using Gdk; using Gtk; using Pango; using Dino; using Dino.Entities; using Xmpp; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_row.ui")] public class ConversationSelectorRow : ListBoxRow { [GtkChild] protected unowned AvatarPicture picture; [GtkChild] protected unowned Label name_label; [GtkChild] protected unowned Label time_label; [GtkChild] protected unowned Label nick_label; [GtkChild] protected unowned Label message_label; [GtkChild] protected unowned Label unread_count_label; [GtkChild] protected unowned Image pinned_image; [GtkChild] public unowned Revealer main_revealer; public Conversation conversation { get; private set; } protected const int AVATAR_SIZE = 40; protected ContentItem? last_content_item; protected int num_unread = 0; protected StreamInteractor stream_interactor; construct { name_label.attributes = new AttrList(); } public ConversationSelectorRow(StreamInteractor stream_interactor, Conversation conversation) { this.conversation = conversation; this.stream_interactor = stream_interactor; switch (conversation.type_) { case Conversation.Type.CHAT: stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (conversation.account.equals(account) && conversation.counterpart.equals(jid)) { update_name_label(); } }); break; case Conversation.Type.GROUPCHAT: stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_name_label(); update_read(true); // bubble color might have changed } }); stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { update_name_label(); } }); break; case Conversation.Type.GROUPCHAT_PM: break; } // Set tooltip switch (conversation.type_) { case Conversation.Type.CHAT: has_tooltip = Util.use_tooltips(); query_tooltip.connect ((x, y, keyboard_tooltip, tooltip) => { tooltip.set_custom(Util.widget_if_tooltips_active(generate_tooltip())); return true; }); break; case Conversation.Type.GROUPCHAT: has_tooltip = Util.use_tooltips(); set_tooltip_text(Util.string_if_tooltips_active(conversation.counterpart.bare_jid.to_string())); break; case Conversation.Type.GROUPCHAT_PM: break; } stream_interactor.get_module(ContentItemStore.IDENTITY).new_item.connect((item, c) => { if (conversation.equals(c)) { content_item_received(item); } }); stream_interactor.get_module(MessageCorrection.IDENTITY).received_correction.connect((item) => { if (last_content_item != null && last_content_item.id == item.id) { content_item_received(item); } }); last_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(conversation); conversation.notify["read-up-to-item"].connect(() => update_read()); conversation.notify["pinned"].connect(() => { update_pinned_icon(); }); update_name_label(); update_pinned_icon(); content_item_received(); } public void update() { update_time_label(); } public void content_item_received(ContentItem? ci = null) { last_content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_latest(conversation) ?? ci; update_message_label(); update_time_label(); update_read(); } public async void colapse() { main_revealer.set_transition_type(RevealerTransitionType.SLIDE_UP); main_revealer.set_reveal_child(false); // Animations can be diabled (=> child_revealed immediately false). Wait for completion in case they're enabled. if (main_revealer.child_revealed) { main_revealer.notify["child-revealed"].connect(() => { Idle.add(colapse.callback); }); yield; } } protected void update_name_label() { name_label.label = Util.get_conversation_display_name(stream_interactor, conversation); } private void update_pinned_icon() { pinned_image.visible = conversation.pinned != 0; } protected void update_time_label(DateTime? new_time = null) { if (last_content_item != null) { time_label.visible = true; time_label.label = get_relative_time(last_content_item.time.to_local()); } } protected void update_message_label() { if (last_content_item != null) { switch (last_content_item.type_) { case MessageItem.TYPE: MessageItem message_item = last_content_item as MessageItem; Message last_message = message_item.message; string body = Dino.message_body_without_reply_fallback(last_message); bool me_command = body.has_prefix("/me "); /* If we have a /me command, we always show the display * name, and we don't set me_is_me on * get_participant_display_name, since that will return * "Me" (internationalized), whereas /me commands expect to * be in the third person. We also omit the colon in this * case, and strip off the /me prefix itself. */ if (conversation.type_ == Conversation.Type.GROUPCHAT || me_command) { nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, last_message.from, !me_command); } else if (last_message.direction == Message.DIRECTION_SENT) { nick_label.label = _("Me"); } else { nick_label.label = ""; } if (me_command) { /* Don't slice off the space after /me */ body = body.slice("/me".length, body.length); } else if (nick_label.label.length > 0) { /* TODO: Is this valid for RTL languages? */ nick_label.label += ": "; } change_label_attribute(message_label, attr_style_new(Pango.Style.NORMAL)); message_label.label = Util.summarize_whitespaces_to_space(body); break; case FileItem.TYPE: FileItem file_item = last_content_item as FileItem; FileTransfer transfer = file_item.file_transfer; if (conversation.type_ == Conversation.Type.GROUPCHAT) { // TODO properly display nick for oneself nick_label.label = Util.get_participant_display_name(stream_interactor, conversation, file_item.file_transfer.from, true) + ": "; } else { nick_label.label = transfer.direction == Message.DIRECTION_SENT ? _("Me") + ": " : ""; } bool file_is_image = transfer.mime_type != null && transfer.mime_type.has_prefix("image"); change_label_attribute(message_label, attr_style_new(Pango.Style.ITALIC)); if (transfer.direction == Message.DIRECTION_SENT) { message_label.label = (file_is_image ? _("Image sent") : _("File sent") ); } else { message_label.label = (file_is_image ? _("Image received") : _("File received") ); } break; case CallItem.TYPE: CallItem call_item = (CallItem) last_content_item; Call call = call_item.call; nick_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Me") + ": " : ""; change_label_attribute(message_label, attr_style_new(Pango.Style.ITALIC)); message_label.label = call.direction == Call.DIRECTION_OUTGOING ? _("Outgoing call") : _("Incoming call"); break; } nick_label.visible = true; message_label.visible = true; } } private static void change_label_attribute(Label label, owned Attribute attribute) { AttrList copy = label.attributes.copy(); copy.change((owned) attribute); label.attributes = copy; } private bool update_read_pending = false; private bool update_read_pending_force = false; protected void update_read(bool force_update = false) { if (force_update) update_read_pending_force = true; if (update_read_pending) return; update_read_pending = true; Idle.add(() => { update_read_pending = false; update_read_pending_force = false; update_read_idle(update_read_pending_force); return Source.REMOVE; }, Priority.LOW); } private void update_read_idle(bool force_update = false) { int current_num_unread = stream_interactor.get_module(ChatInteraction.IDENTITY).get_num_unread(conversation); if (num_unread == current_num_unread && !force_update) return; num_unread = current_num_unread; if (num_unread == 0) { unread_count_label.visible = false; change_label_attribute(name_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(time_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(nick_label, attr_weight_new(Weight.NORMAL)); change_label_attribute(message_label, attr_weight_new(Weight.NORMAL)); } else { unread_count_label.label = num_unread.to_string(); unread_count_label.visible = true; if (conversation.get_notification_setting(stream_interactor) == Conversation.NotifySetting.ON) { unread_count_label.add_css_class("unread-count-notify"); unread_count_label.remove_css_class("unread-count"); } else { unread_count_label.add_css_class("unread-count"); unread_count_label.remove_css_class("unread-count-notify"); } change_label_attribute(name_label, attr_weight_new(Weight.BOLD)); change_label_attribute(time_label, attr_weight_new(Weight.BOLD)); change_label_attribute(nick_label, attr_weight_new(Weight.BOLD)); change_label_attribute(message_label, attr_weight_new(Weight.BOLD)); } } private static Regex dino_resource_regex = /^dino\.[a-f0-9]{8}$/; private Widget generate_tooltip() { Grid grid = new Grid() { row_spacing=5, column_homogeneous=false, column_spacing=5, margin_start=7, margin_end=7, margin_top=7, margin_bottom=7 }; Label label = new Label(conversation.counterpart.to_string()) { valign=Align.START, xalign=0 }; label.attributes = new AttrList(); label.attributes.insert(attr_weight_new(Weight.BOLD)); grid.attach(label, 0, 0, 2, 1); Gee.List? full_jids = stream_interactor.get_module(PresenceManager.IDENTITY).get_full_jids(conversation.counterpart, conversation.account); if (full_jids == null) return grid; for (int i = 0; i < full_jids.size; i++) { Jid full_jid = full_jids[i]; string? show = stream_interactor.get_module(PresenceManager.IDENTITY).get_last_show(full_jid, conversation.account); if (show == null) continue; int i_cache = i; stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.begin(conversation.account, full_jid, (_, res) => { Xep.ServiceDiscovery.Identity? identity = stream_interactor.get_module(EntityInfo.IDENTITY).get_identity.end(res); Image image = new Image() { hexpand=false, valign=Align.CENTER }; if (identity != null && (identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_PHONE || identity.type_ == Xep.ServiceDiscovery.Identity.TYPE_TABLET)) { image.set_from_icon_name("dino-device-phone-symbolic"); } else { image.set_from_icon_name("dino-device-desktop-symbolic"); } if (show == Presence.Stanza.SHOW_AWAY) { Util.force_color(image, "#FF9800"); } else if (show == Presence.Stanza.SHOW_XA || show == Presence.Stanza.SHOW_DND) { Util.force_color(image, "#FF5722"); } else { Util.force_color(image, "#4CAF50"); } string? status = null; if (show == Presence.Stanza.SHOW_AWAY) { status = "away"; } else if (show == Presence.Stanza.SHOW_XA) { status = "not available"; } else if (show == Presence.Stanza.SHOW_DND) { status = "do not disturb"; } var sb = new StringBuilder(); if (identity != null && identity.name != null) { sb.append(identity.name); } else if (full_jid.resourcepart != null && dino_resource_regex.match(full_jid.resourcepart)) { sb.append("Dino"); } else if (full_jid.resourcepart != null) { sb.append(full_jid.resourcepart); } else { return; } if (status != null) { sb.append(" (").append(status).append(")"); } Label resource = new Label(sb.str) { use_markup=true, hexpand=true, xalign=0 }; grid.attach(image, 0, i_cache + 1, 1, 1); grid.attach(resource, 1, i_cache + 1, 1, 1); }); } return grid; } private static string get_relative_time(DateTime datetime) { DateTime now = new DateTime.now_local(); TimeSpan timespan = now.difference(datetime); if (timespan > 365 * TimeSpan.DAY) { return datetime.get_year().to_string(); } else if (timespan > 7 * TimeSpan.DAY) { // Day and month // xgettext:no-c-format return datetime.format(_("%b %d")); } else if (timespan > 2 * TimeSpan.DAY) { return datetime.format("%a"); } else if (datetime.get_day_of_month() != now.get_day_of_month()) { return _("Yesterday"); } else if (timespan > 9 * TimeSpan.MINUTE) { return datetime.format(Util.is_24h_format() ? /* xgettext:no-c-format */ /* Time in 24h format (w/o seconds) */ _("%H∶%M") : /* xgettext:no-c-format */ /* Time in 12h format (w/o seconds) */ _("%l∶%M %p")); } else if (timespan > 1 * TimeSpan.MINUTE) { ulong mins = (ulong) (timespan.abs() / TimeSpan.MINUTE); return n("%i min ago", "%i mins ago", mins).printf(mins); } else { return _("Just now"); } } } } dino-0.5.0/main/src/ui/conversation_titlebar/0000775000000000000000000000000014776241610017705 5ustar rootrootdino-0.5.0/main/src/ui/conversation_titlebar/call_entry.vala0000664000000000000000000001031414776241610022705 0ustar rootrootusing Xmpp; using Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class CallTitlebarEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "call"; } } public double order { get { return 4; } } private MenuButton button = new MenuButton() { tooltip_text=_("Start call") }; private StreamInteractor stream_interactor; private Conversation conversation; public CallTitlebarEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; button.set_icon_name("dino-phone-symbolic"); Menu menu_model = new Menu(); menu_model.append(_("Audio call"), "call.audio"); menu_model.append(_("Video call"), "call.video"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; SimpleActionGroup action_group = new SimpleActionGroup(); SimpleAction audio_call_action = new SimpleAction("audio", null); audio_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, false, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); action_group.insert(audio_call_action); SimpleAction video_call_action = new SimpleAction("video", null); video_call_action.activate.connect((parameter) => { stream_interactor.get_module(Calls.IDENTITY).initiate_call.begin(conversation, true, (_, res) => { CallState call_state = stream_interactor.get_module(Calls.IDENTITY).initiate_call.end(res); open_call_window(call_state); }); }); action_group.insert(video_call_action); button.insert_action_group("call", action_group); stream_interactor.get_module(Calls.IDENTITY).call_incoming.connect((call, state,conversation) => { update_button_state(); }); stream_interactor.get_module(Calls.IDENTITY).call_terminated.connect((call) => { update_button_state(); }); stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect((jid, account) => { if (this.conversation == null) return; if (this.conversation.counterpart.equals_bare(jid) && this.conversation.account.equals(account)) { update_visibility.begin(); } }); stream_interactor.connection_manager.connection_state_changed.connect((account, state) => { update_visibility.begin(); }); } private void open_call_window(CallState call_state) { var call_window = new CallWindow(); var call_controller = new CallWindowController(call_window, call_state, stream_interactor); call_window.controller = call_controller; call_window.present(); update_button_state(); } public new void set_conversation(Conversation conversation) { this.conversation = conversation; update_visibility.begin(); update_button_state(); } private void update_button_state() { button.sensitive = !stream_interactor.get_module(Calls.IDENTITY).is_call_in_progress(); } private async void update_visibility() { if (conversation == null) { button.visible = false; return; } Conversation conv_bak = conversation; bool can_do_calls = yield stream_interactor.get_module(Calls.IDENTITY).can_conversation_do_calls(conversation); if (conv_bak != conversation) return; button.visible = can_do_calls; } public new void unset_conversation() { } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.5.0/main/src/ui/conversation_titlebar/conversation_titlebar.vala0000664000000000000000000000412414776241610025153 0ustar rootrootusing Gtk; using Gee; using Pango; using Dino.Entities; namespace Dino.Ui { public class ConversationTitlebar : Object { public signal void back_pressed(); public new string? title { get { return title_label.label; } set { title_label.label = value; } } public new string? subtitle { get { return subtitle_label.label; } set { subtitle_label.label = value; subtitle_label.visible = (value != null); } } public bool back_button_visible { get { return back_revealer.reveal_child; } set { back_revealer.reveal_child = value; } } public Adw.HeaderBar header_bar = new Adw.HeaderBar(); private Label title_label = new Label("") { ellipsize=EllipsizeMode.END }; private Label subtitle_label = new Label("") { use_markup=true, ellipsize=EllipsizeMode.END, visible=false }; private Revealer back_revealer; public ConversationTitlebar() { Box titles_box = new Box(Orientation.VERTICAL, 0) { valign=Align.CENTER }; title_label.attributes = new AttrList(); title_label.attributes.insert(Pango.attr_weight_new(Weight.BOLD)); titles_box.append(title_label); subtitle_label.attributes = new AttrList(); subtitle_label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); subtitle_label.add_css_class("dim-label"); titles_box.append(subtitle_label); back_revealer = new Revealer() { visible = true, transition_type = RevealerTransitionType.SLIDE_RIGHT, transition_duration = 200, can_focus = false, reveal_child = false }; Button back_button = new Button.from_icon_name("go-previous-symbolic") { visible = true, valign = Align.CENTER, use_underline = true }; back_button.get_style_context().add_class("image-button"); back_button.clicked.connect(() => back_pressed()); back_revealer.set_child(back_button); header_bar.pack_start(back_revealer); header_bar.set_title_widget(titles_box); } public void insert_button(Widget button) { header_bar.pack_end(button); } public Widget get_widget() { return header_bar; } } } dino-0.5.0/main/src/ui/conversation_titlebar/menu_entry.vala0000664000000000000000000000427114776241610022743 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { class MenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "menu"; } } public double order { get { return 0; } } StreamInteractor stream_interactor; private Conversation? conversation; MenuButton button = new MenuButton() { icon_name="view-more-symbolic" }; public MenuEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; Menu menu_model = new Menu(); menu_model.append(_("Conversation Details"), "conversation.details"); menu_model.append(_("Close Conversation"), "conversation.close"); Gtk.PopoverMenu popover_menu = new Gtk.PopoverMenu.from_model(menu_model); button.popover = popover_menu; SimpleActionGroup action_group = new SimpleActionGroup(); SimpleAction details_action = new SimpleAction("details", null); details_action.activate.connect((parameter) => { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("about")}); GLib.Application.get_default().activate_action("open-conversation-details", variant); }); action_group.insert(details_action); SimpleAction close_action = new SimpleAction("close", null); close_action.activate.connect((parameter) => { stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); }); action_group.insert(close_action); button.insert_action_group("conversation", action_group); } public new void set_conversation(Conversation conversation) { button.sensitive = true; this.conversation = conversation; } public new void unset_conversation() { button.sensitive = false; } private void open_conversation_details() { var conversation_details = ConversationDetails.setup_dialog(conversation, stream_interactor, (Window)button.get_root()); conversation_details.present(); } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.5.0/main/src/ui/conversation_titlebar/occupants_entry.vala0000664000000000000000000000243714776241610024000 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Ui { class OccupantsEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "occupants"; } } public double order { get { return 3; } } StreamInteractor stream_interactor; private Conversation? conversation; private MenuButton button = new MenuButton() { icon_name="system-users-symbolic", tooltip_text=Util.string_if_tooltips_active(_("Members")), visible=false }; private OccupantMenu.View menu = null; public OccupantsEntry(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public new void set_conversation(Conversation conversation) { this.conversation = conversation; if (conversation.type_ == Conversation.Type.GROUPCHAT) { button.visible = true; OccupantMenu.View new_menu = new OccupantMenu.View(stream_interactor, conversation); button.set_popover(new_menu); menu = new_menu; } else { button.visible = false; } } public new void unset_conversation() { button.visible = false; } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.5.0/main/src/ui/conversation_titlebar/search_entry.vala0000664000000000000000000000131714776241610023242 0ustar rootrootusing Gtk; using Gee; using Dino.Entities; namespace Dino.Ui { public class SearchMenuEntry : Plugins.ConversationTitlebarEntry, Object { public string id { get { return "search"; } } public double order { get { return 1; } } public ToggleButton button = new ToggleButton() { tooltip_text=Util.string_if_tooltips_active(_("Search messages")) }; public SearchMenuEntry() { button.set_icon_name("system-search-symbolic"); } public new void set_conversation(Conversation conversation) { } public new void unset_conversation() { } public Object? get_widget(Plugins.WidgetType type) { if (type != Plugins.WidgetType.GTK4) return null; return button; } } } dino-0.5.0/main/src/ui/conversation_view.vala0000664000000000000000000000324214776241610017717 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { [GtkTemplate (ui = "/im/dino/Dino/conversation_view.ui")] public class ConversationView : Widget { // [GtkChild] public unowned ScrolledWindow conversation_scrolled; [GtkChild] public unowned Overlay overlay; [GtkChild] public unowned Revealer goto_end_revealer; [GtkChild] public unowned Button goto_end_button; [GtkChild] public unowned ChatInput.View chat_input; [GtkChild] public unowned ConversationSummary.ConversationView conversation_frame; [GtkChild] public unowned Revealer white_revealer; public ListView list_view = new ListView(null, null); public bool at_current_content = true; construct { this.layout_manager = new BinLayout(); white_revealer.notify["child-revealed"].connect_after(on_child_revealed_changed); // conversation_scrolled.set_child(list_view); // list_view.set_factory(get_item_factory()); } public void add_overlay_dialog(Widget widget) { Revealer revealer = new Revealer() { transition_type=RevealerTransitionType.CROSSFADE , transition_duration= 100 }; revealer.set_child(widget); overlay.add_overlay(revealer); revealer.reveal_child = true; white_revealer.visible = true; white_revealer.reveal_child = true; widget.destroy.connect(() => { overlay.remove_overlay(revealer); white_revealer.reveal_child = false; chat_input.do_focus(); }); } private void on_child_revealed_changed() { if (!white_revealer.child_revealed) { white_revealer.visible = false; } } } } dino-0.5.0/main/src/ui/conversation_view_controller.vala0000664000000000000000000003023614776241610022165 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class ConversationViewController : Object { public new string? conversation_display_name { get; set; } public string? conversation_topic { get; set; } private Application app; private ConversationView view; private Widget? overlay_dialog; private ConversationTitlebar titlebar; public SearchMenuEntry search_menu_entry = new SearchMenuEntry(); public ListView list_view = new ListView(null, null); private DropTarget drop_event_controller = new DropTarget(typeof(File), DragAction.COPY ); private ChatInputController chat_input_controller; private StreamInteractor stream_interactor; private Conversation? conversation; public ConversationViewController(ConversationView view, ConversationTitlebar titlebar, StreamInteractor stream_interactor) { this.view = view; this.titlebar = titlebar; this.stream_interactor = stream_interactor; this.app = GLib.Application.get_default() as Application; this.chat_input_controller = new ChatInputController(view.chat_input, stream_interactor); chat_input_controller.activate_last_message_correction.connect(view.conversation_frame.activate_last_message_correction); chat_input_controller.file_picker_selected.connect(open_file_picker); chat_input_controller.clipboard_pasted.connect(on_clipboard_paste); view.conversation_frame.init(stream_interactor); // drag 'n drop file upload drop_event_controller.on_drop.connect(this.on_drag_data_received); // forward key presses var key_controller = new EventControllerKey() { name = "dino-forward-to-input-key-events-1" }; key_controller.key_pressed.connect(forward_key_press_to_chat_input); view.conversation_frame.add_controller(key_controller); var key_controller2 = new EventControllerKey() { name = "dino-forward-to-input-key-events-2" }; key_controller2.key_pressed.connect(forward_key_press_to_chat_input); view.chat_input.add_controller(key_controller2); var key_controller3 = new EventControllerKey() { name = "dino-forward-to-input-key-events-3" }; key_controller3.key_pressed.connect(forward_key_press_to_chat_input); titlebar.get_widget().add_controller(key_controller3); // goto-end floating button var vadjustment = view.conversation_frame.scrolled.vadjustment; vadjustment.notify["value"].connect(() => { bool button_active = vadjustment.value < vadjustment.upper - vadjustment.page_size; view.goto_end_revealer.reveal_child = button_active; view.goto_end_revealer.visible = button_active; }); view.goto_end_button.clicked.connect(() => { view.conversation_frame.initialize_for_conversation(conversation); }); // Update conversation display name & topic this.bind_property("conversation-display-name", titlebar, "title"); this.bind_property("conversation-topic", titlebar, "subtitle"); stream_interactor.get_module(MucManager.IDENTITY).room_info_updated.connect((account, jid) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_conversation_display_name(); } }); stream_interactor.get_module(MucManager.IDENTITY).private_room_occupant_updated.connect((account, room, occupant) => { if (conversation != null && conversation.counterpart.equals_bare(room.bare_jid) && conversation.account.equals(account)) { update_conversation_display_name(); } }); stream_interactor.get_module(MucManager.IDENTITY).subject_set.connect((account, jid, subject) => { if (conversation != null && conversation.counterpart.equals_bare(jid) && conversation.account.equals(account)) { update_conversation_topic(subject); } }); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect((account, jid, roster_item) => { if (conversation != null && conversation.account.equals(account) && conversation.counterpart.equals(jid)) { update_conversation_display_name(); } }); stream_interactor.get_module(FileManager.IDENTITY).upload_available.connect(update_file_upload_status); // Headerbar plugins app.plugin_registry.register_contact_titlebar_entry(new MenuEntry(stream_interactor)); app.plugin_registry.register_contact_titlebar_entry(search_menu_entry); app.plugin_registry.register_contact_titlebar_entry(new OccupantsEntry(stream_interactor)); app.plugin_registry.register_contact_titlebar_entry(new CallTitlebarEntry(stream_interactor)); foreach(var entry in app.plugin_registry.conversation_titlebar_entries) { Widget? button = entry.get_widget(Plugins.WidgetType.GTK4) as Widget; if (button == null) { continue; } titlebar.insert_button(button); } Shortcut shortcut = new Shortcut(new KeyvalTrigger(Key.U, ModifierType.CONTROL_MASK), new CallbackAction(() => { if (conversation == null) return false; stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.begin(conversation, (_, res) => { if (stream_interactor.get_module(FileManager.IDENTITY).is_upload_available.end(res)) { open_file_picker(); } }); return false; })); ((Gtk.Window)view.get_root()).add_shortcut(shortcut); } public void select_conversation(Conversation? conversation, bool default_initialize_conversation) { if (this.conversation != null) { conversation.notify["encryption"].disconnect(update_file_upload_status); } if (overlay_dialog != null) { overlay_dialog.destroy(); } this.conversation = conversation; // Set list model onto list view // Dino.Application app = GLib.Application.get_default() as Dino.Application; // var map_list_model = get_conversation_content_model(new ContentItemMetaModel(app.db, conversation, stream_interactor), stream_interactor); // NoSelection selection_model = new NoSelection(map_list_model); // view.list_view.set_model(selection_model); // view.at_current_content = true; conversation.notify["encryption"].connect(update_file_upload_status); chat_input_controller.set_conversation(conversation); update_conversation_display_name(); update_conversation_topic(); foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { e.set_conversation(conversation); } if (default_initialize_conversation) { view.conversation_frame.initialize_for_conversation(conversation); } update_file_upload_status.begin(); } public void unset_conversation() { conversation_display_name = null; conversation_topic = null; } private async void update_file_upload_status() { if (conversation == null) return; bool upload_available = yield stream_interactor.get_module(FileManager.IDENTITY).is_upload_available(conversation); chat_input_controller.set_file_upload_active(upload_available); if (upload_available && overlay_dialog == null) { if (drop_event_controller.widget == null) { view.add_controller(drop_event_controller); } } else { if (drop_event_controller.widget != null) { view.remove_controller(drop_event_controller); } } } private void update_conversation_display_name() { conversation_display_name = Util.get_conversation_display_name(stream_interactor, conversation); } private void update_conversation_topic(string? subtitle = null) { if (subtitle != null) { string summarized_topic = Util.summarize_whitespaces_to_space(subtitle); conversation_topic = Util.parse_add_markup(summarized_topic, null, true, true); } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { string? subject = stream_interactor.get_module(MucManager.IDENTITY).get_groupchat_subject(conversation.counterpart, conversation.account); if (subject != null) { string summarized_topic = Util.summarize_whitespaces_to_space(subject); conversation_topic = Util.parse_add_markup(summarized_topic, null, true, true); } else { conversation_topic = null; } } else { conversation_topic = null; } } private async void on_clipboard_paste() { try { Clipboard clipboard = view.get_clipboard(); Gdk.Texture? texture = yield clipboard.read_texture_async(null); // TODO critical var file_name = Path.build_filename(FileManager.get_storage_dir(), Xmpp.random_uuid() + ".png"); texture.save_to_png(file_name); open_send_file_overlay(File.new_for_path(file_name)); } catch (IOError.NOT_SUPPORTED e) { // Format not supported, ignore } } private bool on_drag_data_received(DropTarget target, Value val, double x, double y) { if (val.type() == typeof(File)) { open_send_file_overlay((File)val); return true; } return false; } private void open_file_picker() { FileChooserNative chooser = new FileChooserNative(_("Select file"), view.get_root() as Gtk.Window, FileChooserAction.OPEN, _("Select"), _("Cancel")); chooser.response.connect((response) => { if (response == ResponseType.ACCEPT) { open_send_file_overlay(File.new_for_path(chooser.get_file().get_path())); } }); chooser.show(); } private void open_send_file_overlay(File file) { FileInfo file_info; try { file_info = file.query_info("*", FileQueryInfoFlags.NONE); } catch (Error e) { return; } FileSendOverlay overlay = new FileSendOverlay(file, file_info); overlay.send_file.connect(() => send_file(file)); stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.begin(conversation, (_, res) => { HashMap limits = stream_interactor.get_module(FileManager.IDENTITY).get_file_size_limits.end(res); bool something_works = false; foreach (var limit in limits.values) { if (limit >= file_info.get_size()) { something_works = true; } } if (!something_works && limits.has_key(0)) { if (!something_works && file_info.get_size() > limits[0]) { overlay.set_file_too_large(); } } }); overlay.close.connect(() => { // We don't want drag'n'drop to be active while the overlay is active overlay_dialog = null; update_file_upload_status.begin(); }); view.add_overlay_dialog(overlay.get_widget()); overlay_dialog = overlay.get_widget(); update_file_upload_status.begin(); } private void send_file(File file) { stream_interactor.get_module(FileManager.IDENTITY).send_file.begin(file, conversation); } private bool forward_key_press_to_chat_input(EventControllerKey key_controller, uint keyval, uint keycode, Gdk.ModifierType state) { if (view.get_root().get_focus() is TextView) { return false; } // Don't forward / change focus on Control / Alt if (keyval == Gdk.Key.Control_L || keyval == Gdk.Key.Control_R || keyval == Gdk.Key.Alt_L || keyval == Gdk.Key.Alt_R) { return false; } // Don't forward / change focus on Control + ... if ((state & ModifierType.CONTROL_MASK) > 0) { return false; } return key_controller.forward(view.chat_input.chat_text_view.text_view); } } } dino-0.5.0/main/src/ui/file_send_overlay.vala0000664000000000000000000000563214776241610017651 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class FileSendOverlay { public signal void close(); public signal void send_file(); public Box main_box; public Button close_button; public Button send_button; public SizingBin file_widget_insert; public Label info_label; private bool can_send = true; public FileSendOverlay(File file, FileInfo file_info) { Builder builder = new Builder.from_resource("/im/dino/Dino/file_send_overlay.ui"); main_box = (Box) builder.get_object("main_box"); close_button = (Button) builder.get_object("close_button"); send_button = (Button) builder.get_object("send_button"); file_widget_insert = (SizingBin) builder.get_object("file_widget_insert"); info_label = (Label) builder.get_object("info_label"); close_button.clicked.connect(() => { do_close(); }); send_button.clicked.connect(() => { send_file(); do_close(); }); load_file_widget.begin(file, file_info); main_box.realize.connect(() => { if (can_send) { send_button.grab_focus(); } else { close_button.grab_focus(); } }); var key_events = new EventControllerKey(); key_events.key_pressed.connect((keyval) => { if (keyval == Gdk.Key.Escape) { do_close(); } return false; }); this.main_box.add_controller(key_events); } private async void load_file_widget(File file, FileInfo file_info) { string file_name = file_info.get_display_name(); string mime_type = file_info.get_content_type(); bool is_image = Dino.Util.is_pixbuf_supported_mime_type(mime_type); Widget? widget = null; if (is_image) { FileImageWidget image_widget = new FileImageWidget(); try { yield image_widget.load_from_file(file, file_name); widget = image_widget; } catch (Error e) { } } if (widget == null) { FileDefaultWidget default_widget = new FileDefaultWidget(); default_widget.name_label.label = file_name; default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, FileTransfer.DIRECTION_SENT, (long)file_info.get_size(), 0); widget = default_widget; } widget.set_parent(file_widget_insert); } public void set_file_too_large() { info_label.label= _("The file exceeds the server's maximum upload size."); Util.force_error_color(info_label); send_button.sensitive = false; can_send = false; } private void do_close() { this.close(); main_box.unparent(); main_box.destroy(); } public Widget get_widget() { return main_box; } } } dino-0.5.0/main/src/ui/global_search.vala0000664000000000000000000003554514776241610016753 0ustar rootrootusing Gee; using Gtk; using Pango; using Dino.Entities; namespace Dino.Ui { public class GlobalSearch { public signal void selected_item(MessageItem item); private StreamInteractor stream_interactor; private string search = ""; private int loaded_results = -1; private Mutex reloading_mutex = Mutex(); public Overlay overlay; public SearchEntry search_entry; public Label entry_number_label; public ScrolledWindow results_scrolled; public Box results_box; public Stack results_empty_stack; public Frame auto_complete_overlay; public ListBox auto_complete_list; private ArrayList auto_complete_children = new ArrayList(); private ArrayList results_box_children = new ArrayList(); public GlobalSearch(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; Builder builder = new Builder.from_resource("/im/dino/Dino/global_search.ui"); overlay = (Overlay) builder.get_object("overlay"); search_entry = (SearchEntry) builder.get_object("search_entry"); entry_number_label = (Label) builder.get_object("entry_number_label"); results_scrolled = (ScrolledWindow) builder.get_object("results_scrolled"); results_box = (Box) builder.get_object("results_box"); results_empty_stack = (Stack) builder.get_object("results_empty_stack"); auto_complete_overlay = (Frame) builder.get_object("auto_complete_overlay"); auto_complete_list = (ListBox) builder.get_object("auto_complete_list"); search_entry.search_changed.connect(() => { set_search(search_entry.text); }); search_entry.notify["text"].connect_after(update_auto_complete); search_entry.notify["cursor-position"].connect_after(update_auto_complete); results_scrolled.vadjustment.notify["value"].connect(on_scrolled_window_vadjustment_value); results_scrolled.vadjustment.notify["upper"].connect_after(on_scrolled_window_vadjustment_upper); var overlay_key_events = new EventControllerKey() { name = "dino-search-overlay-key-events" }; overlay_key_events.key_pressed.connect(on_key_pressed); overlay_key_events.key_released.connect(on_key_released); overlay.add_controller(overlay_key_events); } private void on_scrolled_window_vadjustment_value() { if (results_scrolled.vadjustment.upper - (results_scrolled.vadjustment.value + results_scrolled.vadjustment.page_size) < 100) { if (!reloading_mutex.trylock()) return; Gee.List new_messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search, loaded_results); if (new_messages.size == 0) { reloading_mutex.unlock(); return; } loaded_results += new_messages.size; append_messages(new_messages); } } private void on_scrolled_window_vadjustment_upper() { reloading_mutex.trylock(); reloading_mutex.unlock(); } private bool on_key_pressed(uint keyval, uint keycode, Gdk.ModifierType state) { if (!auto_complete_overlay.visible) return false; if (keyval == Gdk.Key.Up) { var row = auto_complete_list.get_selected_row(); var index = row == null ? -1 : row.get_index() - 1; if (index == -1) index = (int)auto_complete_children.size - 1; auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); return true; } if (keyval == Gdk.Key.Down) { var row = auto_complete_list.get_selected_row(); var index = row == null ? 0 : row.get_index() + 1; if (index == auto_complete_children.size) index = 0; auto_complete_list.select_row(auto_complete_list.get_row_at_index(index)); return true; } if (keyval == Gdk.Key.Tab) { auto_complete_list.get_selected_row().activate(); return true; } // TODO: Handle cursor movement in results // TODO: Direct all keystrokes to text input return false; } private void on_key_released(uint keyval, uint keycode, Gdk.ModifierType state) { if (!auto_complete_overlay.visible) return; if (keyval == Gdk.Key.Return) { auto_complete_list.get_selected_row().activate(); } } private void update_auto_complete() { Gee.List suggestions = stream_interactor.get_module(SearchProcessor.IDENTITY).suggest_auto_complete(search_entry.text, search_entry.cursor_position); auto_complete_overlay.visible = suggestions.size > 0; if (suggestions.size > 0) { // Remove current suggestions foreach (Widget widget in auto_complete_children) { auto_complete_list.remove(widget); } auto_complete_children.clear(); // Populate new suggestions foreach(SearchSuggestion suggestion in suggestions) { Builder builder = new Builder.from_resource("/im/dino/Dino/search_autocomplete.ui"); AvatarPicture avatar = (AvatarPicture)builder.get_object("picture"); Label label = (Label)builder.get_object("label"); string display_name; if (suggestion.conversation.type_ == Conversation.Type.GROUPCHAT && !suggestion.conversation.counterpart.equals(suggestion.jid) || suggestion.conversation.type_ == Conversation.Type.GROUPCHAT_PM) { display_name = Util.get_participant_display_name(stream_interactor, suggestion.conversation, suggestion.jid); avatar.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(suggestion.conversation, suggestion.jid); } else { display_name = Util.get_conversation_display_name(stream_interactor, suggestion.conversation); avatar.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(suggestion.conversation); } if (display_name != suggestion.jid.to_string()) { label.set_markup("%s %s".printf(Markup.escape_text(display_name), Markup.escape_text(suggestion.jid.to_string()))); } else { label.label = display_name; } ListBoxRow row = new ListBoxRow() { visible = true, can_focus = false }; row.set_child((Widget)builder.get_object("root")); row.activate.connect(() => { handle_suggestion(suggestion); }); auto_complete_list.append(row); auto_complete_children.add(row); } auto_complete_list.select_row(auto_complete_list.get_row_at_index(0)); } } private void handle_suggestion(SearchSuggestion suggestion) { search_entry.delete_text(suggestion.start_index, suggestion.end_index); int position = search_entry.cursor_position; search_entry.insert_text(suggestion.completion + " ", suggestion.completion.length + 1, ref position); search_entry.set_position(-1); } private void clear_search() { // Scroll to top results_scrolled.vadjustment.value = 0; foreach (Widget widget in results_box_children) { results_box.remove(widget); } results_box_children.clear(); loaded_results = 0; } private void set_search(string search) { clear_search(); this.search = search; if (get_keywords(search).is_empty) { results_empty_stack.set_visible_child_name("empty"); return; } Gee.List messages = stream_interactor.get_module(SearchProcessor.IDENTITY).match_messages(search); if (messages.size == 0) { results_empty_stack.set_visible_child_name("no-result"); } else { results_empty_stack.set_visible_child_name("results"); int match_count = messages.size < 10 ? messages.size : stream_interactor.get_module(SearchProcessor.IDENTITY).count_match_messages(search); entry_number_label.label = "" + n("%i search result", "%i search results", match_count).printf(match_count) + ""; loaded_results += messages.size; append_messages(messages); } } private void append_messages(Gee.List messages) { foreach (MessageItem item in messages) { Gee.List before_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_before_message(item.conversation, item.message.time, item.message.id, 1); Gee.List after_message = stream_interactor.get_module(MessageStorage.IDENTITY).get_messages_after_message(item.conversation, item.message.time, item.message.id, 1); Box context_box = new Box(Orientation.VERTICAL, 5); if (before_message != null && before_message.size > 0) { context_box.append(get_context_message_widget(before_message.first())); } Widget match_widget = get_match_message_widget(item); context_box.append(match_widget); if (after_message != null && after_message.size > 0) { context_box.append(get_context_message_widget(after_message.first())); } Label date_label = new Label(ConversationSummary.ConversationItemSkeleton.get_relative_time(item.time.to_local())) { xalign=0 }; date_label.add_css_class("dim-label"); string display_name = Util.get_conversation_display_name(stream_interactor, item.conversation); string title = item.message.type_ == Message.Type.GROUPCHAT ? _("In %s").printf(display_name) : _("With %s").printf(display_name); Box header_box = new Box(Orientation.HORIZONTAL, 10) { margin_start=7 }; header_box.append(new Label(@"$(Markup.escape_text(title))") { ellipsize=EllipsizeMode.END, xalign=0, use_markup=true }); header_box.append(date_label); Box result_box = new Box(Orientation.VERTICAL, 7); result_box.append(header_box); result_box.append(context_box); results_box.append(result_box); results_box_children.add(result_box); } } private Widget get_match_message_widget(MessageItem item) { Grid grid = get_skeleton(item); grid.margin_top = 3; grid.margin_bottom = 3; string text = Util.unbreak_space_around_non_spacing_mark(item.message.body.replace("\n", "").replace("\r", "")); if (text.char_count() > 200) { int index = text.index_of(search); int char_index = index < 0 ? 0 : text.char_count(index); if (char_index + search.char_count() <= 100) { text = text.substring(0, text.index_of_nth_char(150)) + " … " + text.substring(text.index_of_nth_char(text.char_count() - 50)); } else if (char_index >= text.char_count() - 100) { text = text.substring(0, text.index_of_nth_char(50)) + " … " + text.substring(text.index_of_nth_char(text.char_count() - 150)); } else { text = text.substring(0, text.index_of_nth_char(25)) + " … " + text.substring(text.index_of_nth_char(char_index - 50), text.index_of_nth_char(char_index + 100)) + " … " + text.substring(text.index_of_nth_char(text.char_count() - 25)); } } Label label = new Label("") { use_markup=true, xalign=0, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, vexpand=true }; // Build regex containing all keywords string regex_str = "("; Gee.List keywords = get_keywords(Regex.escape_string(search.down())); bool first = true; foreach (string keyword in keywords) { if (first) { first = false; } else { regex_str += "|"; } regex_str += "\\b" + keyword; } regex_str += ")"; // Color the keywords string markup_text = ""; try { Regex highlight_regex = new Regex(regex_str, RegexCompileFlags.CASELESS); MatchInfo match_info; highlight_regex.match(text, 0, out match_info); int last_end = 0; for (; match_info.matches(); match_info.next()) { int start, end; match_info.fetch_pos(0, out start, out end); string themed_span = Util.is_dark_theme(label) ? "" : ""; markup_text += Markup.escape_text(text[last_end:start]) + themed_span + Markup.escape_text(text[start:end]) + ""; last_end = end; } markup_text += Markup.escape_text(text[last_end:text.length]); } catch (RegexError e) { assert_not_reached(); } label.label = markup_text; grid.attach(label, 1, 1, 1, 1); Button button = new Button() { has_frame=false }; button.clicked.connect(() => { selected_item(item); }); button.child = grid; return button; } private Grid get_context_message_widget(MessageItem item) { Grid grid = get_skeleton(item); grid.margin_start = 7; Label label = new Label(item.message.body.replace("\n", "").replace("\r", "")) { ellipsize=EllipsizeMode.MIDDLE, xalign=0 }; grid.attach(label, 1, 1, 1, 1); grid.opacity = 0.55; return grid; } private Grid get_skeleton(MessageItem item) { AvatarPicture picture = new AvatarPicture() { height_request=32, width_request=32, margin_end=7, valign=Align.START }; picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(item.conversation, item.jid); Grid grid = new Grid() { row_homogeneous=false }; grid.attach(picture, 0, 0, 1, 2); string display_name = Util.get_participant_display_name(stream_interactor, item.conversation, item.jid); Label name_label = new Label(display_name) { ellipsize=EllipsizeMode.END, xalign=0 }; name_label.attributes = new AttrList(); name_label.attributes.insert(attr_weight_new(Weight.BOLD)); grid.attach(name_label, 1, 0, 1, 1); return grid; } private static Gee.List get_keywords(string search_string) { Gee.List ret = new ArrayList(); foreach (string search in search_string.split(" ")) { bool is_filter = search.has_prefix("from:") || search.has_prefix("in:") || search.has_prefix("with:"); if (!is_filter && search != "") { ret.add(search); } } return ret; } public Widget get_widget() { return overlay; } } } dino-0.5.0/main/src/ui/main_window.vala0000664000000000000000000001656714776241610016504 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class MainWindow : Adw.ApplicationWindow { public signal void conversation_selected(Conversation conversation); public new string? title { get; set; } public string? subtitle { get; set; } public WelcomePlaceholder welcome_placeholder = new WelcomePlaceholder(); public NoAccountsPlaceholder accounts_placeholder = new NoAccountsPlaceholder(); public ConversationView conversation_view; public ConversationSelector conversation_selector; public ConversationTitlebar conversation_titlebar; public Widget conversation_list_titlebar; public Box box = new Box(Orientation.VERTICAL, 0) { orientation=Orientation.VERTICAL }; private Adw.Leaflet leaflet; public Box left_box; public Box right_box; public Adw.Flap search_flap; public GlobalSearch global_search; private Stack stack = new Stack(); private Stack left_stack; private Stack right_stack; private StreamInteractor stream_interactor; private Database db; private Config config; class construct { var shortcut = new Shortcut(new KeyvalTrigger(Key.F, ModifierType.CONTROL_MASK), new CallbackAction((widget, args) => { ((MainWindow) widget).search_flap.reveal_flap = true; return false; })); add_shortcut(shortcut); } public MainWindow(Application application, StreamInteractor stream_interactor, Database db, Config config) { Object(application : application); this.stream_interactor = stream_interactor; this.db = db; this.config = config; this.title = "Dino"; this.add_css_class("dino-main"); ((Widget)this).realize.connect(restore_window_size); setup_unified(); setup_headerbar(); setup_stack(); } private void setup_unified() { Builder builder = new Builder.from_resource("/im/dino/Dino/unified_main_content.ui"); leaflet = (Adw.Leaflet) builder.get_object("leaflet"); box.append(leaflet); left_box = (Box) builder.get_object("left_box"); right_box = (Box) builder.get_object("right_box"); left_stack = (Stack) builder.get_object("left_stack"); right_stack = (Stack) builder.get_object("right_stack"); conversation_view = (ConversationView) builder.get_object("conversation_view"); search_flap = (Adw.Flap) builder.get_object("search_flap"); conversation_selector = ((ConversationSelector) builder.get_object("conversation_list")).init(stream_interactor); conversation_selector.conversation_selected.connect_after(() => leaflet.navigate(Adw.NavigationDirection.FORWARD)); Adw.Bin search_frame = (Adw.Bin) builder.get_object("search_frame"); global_search = new GlobalSearch(stream_interactor); search_frame.set_child(global_search.get_widget()); } private void setup_headerbar() { conversation_list_titlebar = get_conversation_list_titlebar(); conversation_titlebar = new ConversationTitlebar(); leaflet.bind_property("folded", conversation_list_titlebar, "show-end-title-buttons", BindingFlags.SYNC_CREATE); leaflet.bind_property("folded", conversation_titlebar.get_widget(), "show-start-title-buttons", BindingFlags.SYNC_CREATE); left_box.prepend(conversation_list_titlebar); right_box.prepend(conversation_titlebar.get_widget()); leaflet.notify["folded"].connect_after(() => conversation_titlebar.back_button_visible = leaflet.folded); conversation_titlebar.back_pressed.connect(() => leaflet.navigate(Adw.NavigationDirection.BACK)); } private void setup_stack() { stack.add_named(box, "main"); stack.add_named(welcome_placeholder, "welcome_placeholder"); stack.add_named(accounts_placeholder, "accounts_placeholder"); set_content(stack); } public enum StackState { CLEAN_START, NO_ACTIVE_ACCOUNTS, NO_ACTIVE_CONVERSATIONS, CONVERSATION } public void set_stack_state(StackState stack_state) { if (stack_state == StackState.CONVERSATION) { left_stack.set_visible_child_name("content"); right_stack.set_visible_child_name("content"); stack.set_visible_child_name("main"); } else if (stack_state == StackState.CLEAN_START || stack_state == StackState.NO_ACTIVE_ACCOUNTS) { if (stack_state == StackState.CLEAN_START) { stack.set_visible_child_name("welcome_placeholder"); } else if (stack_state == StackState.NO_ACTIVE_ACCOUNTS) { stack.set_visible_child_name("accounts_placeholder"); } } else if (stack_state == StackState.NO_ACTIVE_CONVERSATIONS) { stack.set_visible_child_name("main"); left_stack.set_visible_child_name("placeholder"); right_stack.set_visible_child_name("placeholder"); } } public void loop_conversations(bool backwards) { conversation_selector.loop_conversations(backwards); } public void restore_window_size() { Gdk.Display? display = Gdk.Display.get_default(); if (display != null) { Gdk.Surface? surface = get_surface(); Gdk.Monitor? monitor = display.get_monitor_at_surface(surface); if (monitor != null && config.window_width <= monitor.geometry.width && config.window_height <= monitor.geometry.height) { set_default_size(config.window_width, config.window_height); } } if (config.window_maximize) { maximize(); } ((Widget)this).unrealize.connect(() => { save_window_size(); config.window_maximize = this.maximized; }); } public void save_window_size() { if (this.maximized) return; Gdk.Display? display = get_display(); Gdk.Surface? surface = get_surface(); if (display != null && surface != null) { Gdk.Monitor monitor = display.get_monitor_at_surface(surface); // Only store if the values have changed and are reasonable-looking. if (config.window_width != default_width && default_width > 0 && default_width <= monitor.geometry.width) { config.window_width = default_width; } if (config.window_height != default_height && default_height > 0 && default_height <= monitor.geometry.height) { config.window_height = default_height; } } } } public class WelcomePlaceholder : MainWindowPlaceholder { public WelcomePlaceholder() { status_page.title = _("Welcome to Dino!"); status_page.description = _("Sign in or create an account to get started."); primary_button.label = _("Set up account"); primary_button.visible = true; } } public class NoAccountsPlaceholder : MainWindowPlaceholder { public NoAccountsPlaceholder() { status_page.title = _("No active accounts"); primary_button.label = _("Manage accounts"); primary_button.visible = true; } } [GtkTemplate (ui = "/im/dino/Dino/unified_window_placeholder.ui")] public class MainWindowPlaceholder : Box { [GtkChild] public unowned Adw.StatusPage status_page; [GtkChild] public unowned Button primary_button; [GtkChild] public unowned Button secondary_button; } } dino-0.5.0/main/src/ui/main_window_controller.vala0000664000000000000000000002136114776241610020733 0ustar rootrootusing Gee; using Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class MainWindowController : Object { private StreamInteractor stream_interactor; private Conversation? conversation; private Application app; private Database db; private MainWindow window; private ConversationViewController conversation_view_controller; public MainWindowController(Application application, StreamInteractor stream_interactor, Database db) { this.app = application; this.stream_interactor = stream_interactor; this.db = db; stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(check_unset_conversation); stream_interactor.account_removed.connect(check_unset_conversation); SimpleAction jump_to_conversatio_message_action = new SimpleAction("jump-to-conversation-message", new VariantType.tuple(new VariantType[]{VariantType.INT32, VariantType.INT32})); jump_to_conversatio_message_action.activate.connect((variant) => { int conversation_id = variant.get_child_value(0).get_int32(); Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation_by_id(conversation_id); if (conversation == null || !this.conversation.equals(conversation)) return; int item_id = variant.get_child_value(1).get_int32(); ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_id(conversation, item_id); select_conversation(conversation, false, false); window.conversation_view.conversation_frame.initialize_around_message(conversation, content_item); }); app.add_action(jump_to_conversatio_message_action); } public void set_window(MainWindow window) { this.window = window; this.conversation_view_controller = new ConversationViewController(window.conversation_view, window.conversation_titlebar, stream_interactor); conversation_view_controller.search_menu_entry.button.bind_property("active", window.search_flap, "reveal-flap", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); window.search_flap.notify["reveal-flap"].connect(() => { if (window.search_flap.reveal_flap) { if (window.conversation_view.conversation_frame.conversation != null && window.global_search.search_entry.text == "") { reset_search_entry(); } window.global_search.search_entry.grab_focus(); window.global_search.search_entry.set_position((int)window.global_search.search_entry.text.length); } }); window.global_search.selected_item.connect((item) => { select_conversation(item.conversation, false, false); window.conversation_view.conversation_frame.initialize_around_message(item.conversation, item); if (window.search_flap.folded) { close_search(); } }); window.welcome_placeholder.primary_button.clicked.connect(() => { ManageAccounts.AddAccountDialog dialog = new ManageAccounts.AddAccountDialog(stream_interactor, db); dialog.set_transient_for(app.get_active_window()); dialog.present(); }); window.accounts_placeholder.primary_button.clicked.connect(() => { app.activate_action("preferences", null); }); window.conversation_selector.conversation_selected.connect((conversation) => select_conversation(conversation)); // ConversationListModel list_model = new ConversationListModel(stream_interactor); // list_model.closed_conversation.connect((conversation) => { // print(@"closed $(conversation.counterpart.bare_jid)\n"); // stream_interactor.get_module(ConversationManager.IDENTITY).close_conversation(conversation); // }); // SingleSelection selection_model = new SingleSelection(list_model) { autoselect=false }; // selection_model.notify["selected-item"].connect(() => { // ConversationViewModel view_model = (ConversationViewModel) selection_model.selected_item; // if (view_model.conversation.equals(conversation)) return; // print(@"selected conversation $(view_model.conversation.counterpart)\n"); // select_conversation(view_model.conversation); // }); // window.conversation_list_view.set_model(selection_model); // print(list_model.get_n_items().to_string() + " " + selection_model.get_n_items().to_string() + "<<"); // print(selection_model.get_selected().to_string() + "<<"); // window.conversation_list_view.realize.connect(() => { // selection_model.set_selected(0); // }); Widget window_widget = ((Widget) window); EventControllerKey key_event_controller = new EventControllerKey(); window_widget.add_controller(key_event_controller); // TODO GTK4: Why doesn't this work with key_pressed signal key_event_controller.key_released.connect((keyval) => { if (keyval == Gdk.Key.Escape) { close_search(); } }); EventControllerFocus focus_event_controller = new EventControllerFocus(); window_widget.add_controller(focus_event_controller); focus_event_controller.enter.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_in(conversation); }); focus_event_controller.leave.connect(() => { stream_interactor.get_module(ChatInteraction.IDENTITY).on_window_focus_out(conversation); }); window.conversation_selected.connect(conversation => select_conversation(conversation)); stream_interactor.account_added.connect((account) => { update_stack_state(true); }); stream_interactor.account_removed.connect((account) => { update_stack_state(); }); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_activated.connect(() => update_stack_state()); stream_interactor.get_module(ConversationManager.IDENTITY).conversation_deactivated.connect(() => update_stack_state()); update_stack_state(); } public void select_conversation(Conversation? conversation, bool do_reset_search = true, bool default_initialize_conversation = true) { this.conversation = conversation; conversation_view_controller.select_conversation(conversation, default_initialize_conversation); stream_interactor.get_module(ChatInteraction.IDENTITY).on_conversation_selected(conversation); conversation.active = true; // only for conversation_selected window.conversation_selector.on_conversation_selected(conversation); // In case selection was not via ConversationSelector if (do_reset_search) { reset_search_entry(); } } private void check_unset_conversation() { if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { unset_conversation(); } } private void unset_conversation() { this.conversation = null; conversation_view_controller.unset_conversation(); foreach(Plugins.ConversationTitlebarEntry e in this.app.plugin_registry.conversation_titlebar_entries) { e.unset_conversation(); } } private void update_stack_state(bool know_exists = false) { ArrayList accounts = stream_interactor.get_accounts(); if (!know_exists && accounts.size == 0) { if (db.get_accounts().size == 0) { window.set_stack_state(MainWindow.StackState.CLEAN_START); } else { window.set_stack_state(MainWindow.StackState.NO_ACTIVE_ACCOUNTS); } } else if (stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations().size == 0) { window.set_stack_state(MainWindow.StackState.NO_ACTIVE_CONVERSATIONS); } else { window.set_stack_state(MainWindow.StackState.CONVERSATION); } } private void reset_search_entry() { if (window.conversation_view.conversation_frame.conversation != null) { switch (conversation.type_) { case Conversation.Type.CHAT: case Conversation.Type.GROUPCHAT_PM: window.global_search.search_entry.text = @"with:$(conversation.counterpart) "; break; case Conversation.Type.GROUPCHAT: window.global_search.search_entry.text = @"in:$(conversation.counterpart) "; break; } } } private void close_search() { conversation_view_controller.search_menu_entry.button.active = false; } } } dino-0.5.0/main/src/ui/notifier_freedesktop.vala0000664000000000000000000004056414776241610020375 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; public class Dino.Ui.FreeDesktopNotifier : NotificationProvider, Object { public signal void conversation_selected(Conversation conversation); private StreamInteractor stream_interactor; private DBusNotifications dbus_notifications; private bool supports_body_markup = false; private bool supports_body_hyperlinks = false; private HashMap content_notifications = new HashMap(Conversation.hash_func, Conversation.equals_func); private HashMap> conversation_notifications = new HashMap>(Conversation.hash_func, Conversation.equals_func); private HashMap> action_listeners = new HashMap>(); private HashMap call_notifications = new HashMap(Call.hash_func, Call.equals_func); public FreeDesktopNotifier(StreamInteractor stream_interactor, DBusNotifications dbus_notifications) { this.stream_interactor = stream_interactor; this.dbus_notifications = dbus_notifications; init_dbus_notifications.begin(); } private async void init_dbus_notifications() throws Error { string[] caps; yield dbus_notifications.get_capabilities(out caps); foreach (string cap in caps) { switch (cap) { case "body-markup": supports_body_markup = true; break; case "body-hyperlinks": supports_body_hyperlinks = true; break; } } dbus_notifications.action_invoked.connect((id, action) => { if (action_listeners.has_key(id) && action_listeners[id].has_key(action)) { action_listeners[id][action].func(); } }); dbus_notifications.notification_closed.connect((id) => { action_listeners.unset(id); }); } public double get_priority() { return 1; } public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) { string body = ""; if (supports_body_hyperlinks) { body = Util.parse_add_markup(message.body, null, true, false); } else if (supports_body_markup) { body = Markup.escape_text(message.body); } else { body = message.body; } yield notify_content_item(conversation, conversation_display_name, participant_display_name, body); } public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) { string text = ""; if (file_transfer.direction == Message.DIRECTION_SENT) { text = is_image ? _("Image sent") : _("File sent"); } else { text = is_image ? _("Image received") : _("File received"); } if (supports_body_markup) { text = "" + text + ""; } yield notify_content_item(conversation, conversation_display_name, participant_display_name, text); } private async void notify_content_item(Conversation conversation, string conversation_display_name, string? participant_display_name, string body_) { string body = body_; if (participant_display_name != null) { if (supports_body_markup) { body = @"$(Markup.escape_text(participant_display_name)): $body"; } else { body = @"$participant_display_name: $body"; } } uint32 replace_id = content_notifications.has_key(conversation) ? content_notifications[conversation] : 0; HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("im.received"); string[] actions = new string[] {"default", "Open conversation"}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", replace_id, "", conversation_display_name, body, actions, hash_table, -1); content_notifications[conversation] = notification_id; add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing content item notification: %s", e.message); } } public async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name) { debug("[%s] Call notification", call.account.bare_jid.to_string()); string summary = Markup.escape_text(conversation_display_name); string body = video ? _("Incoming video call") : _("Incoming call"); if (multiparty) { body = video ? _("Incoming video group call") : _("Incoming group call"); } HashTable hash_table = new HashTable(null, null); hash_table["image-path"] = "call-start-symbolic"; hash_table["sound-name"] = new Variant.string("phone-incoming-call"); hash_table["urgency"] = new Variant.byte(2); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("call.incoming"); string[] actions = new string[] {"default", "Open conversation", "reject", _("Reject"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, 0); call_notifications[call] = notification_id; add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "reject", () => { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.int32(call.id)}); GLib.Application.get_default().activate_action("reject-call", variant); }); add_action_listener(notification_id, "accept", () => { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.int32(call.id)}); GLib.Application.get_default().activate_action("accept-call", variant); }); } catch (Error e) { warning("Failed showing subscription request notification: %s", e.message); } } public async void retract_call_notification(Call call, Conversation conversation) { if (!call_notifications.has_key(call)) return; uint32 notification_id = call_notifications[call]; try { yield dbus_notifications.close_notification(notification_id); action_listeners.unset(notification_id); call_notifications.unset(call); } catch (Error e) { } } public async void notify_subscription_request(Conversation conversation) { string summary = _("Subscription request"); string body = Markup.escape_text(conversation.counterpart.to_string()); HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("im"); string[] actions = new string[] {"default", "Open conversation", "accept", _("Accept"), "deny", _("Deny")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); if (!conversation_notifications.has_key(conversation)) { conversation_notifications[conversation] = new ArrayList(); } conversation_notifications[conversation].add(notification_id); add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-conversation", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("accept-subscription", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-subscription", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing subscription request notification: %s", e.message); } } public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) { string summary = _("Could not connect to %s").printf(account.bare_jid.domainpart); string body = ""; switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: body = _("Wrong password"); break; case ConnectionManager.ConnectionError.Source.TLS: body = _("Invalid TLS certificate"); break; default: break; } HashTable hash_table = new HashTable(null, null); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("im.error"); string[] actions = new string[] {"default", "Open preferences"}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "im.dino.Dino", summary, body, actions, hash_table, -1); add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("preferences-account", new Variant.int32(account.id)); }); } catch (Error e) { warning("Failed showing connection error notification: %s", e.message); } } public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); string display_room = room_jid.bare_jid.to_string(); string summary = _("Invitation to %s").printf(display_room); string body = _("%s invited you to %s").printf(inviter_display_name, display_room); if (supports_body_markup) { body = Markup.escape_text(body); } HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(direct_conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("im"); string[] actions = new string[] {"default", "", "reject", _("Reject"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); add_action_listener(notification_id, "default", () => { GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); }); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("open-muc-join", new Variant.int32(group_conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-invite", new Variant.int32(group_conversation.id)); }); } catch (Error e) { warning("Failed showing muc invite notification: %s", e.message); } } public async void notify_voice_request(Conversation conversation, Jid from_jid) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, from_jid); string display_room = Util.get_conversation_display_name(stream_interactor, conversation); string summary = _("Permission request"); string body = _("%s requests the permission to write in %s").printf(display_name, display_room); if (supports_body_markup) { Markup.escape_text(body); } HashTable hash_table = new HashTable(null, null); hash_table["image-data"] = yield get_conversation_icon(conversation); hash_table["desktop-entry"] = new Variant.string(Dino.Application.get_default().get_application_id()); hash_table["category"] = new Variant.string("im"); string[] actions = new string[] {"deny", _("Deny"), "accept", _("Accept")}; try { uint32 notification_id = yield dbus_notifications.notify("Dino", 0, "", summary, body, actions, hash_table, -1); add_action_listener(notification_id, "accept", () => { GLib.Application.get_default().activate_action("accept-voice-request", new Variant.int32(conversation.id)); }); add_action_listener(notification_id, "deny", () => { GLib.Application.get_default().activate_action("deny-voice-request", new Variant.int32(conversation.id)); }); } catch (Error e) { warning("Failed showing voice request notification: %s", e.message); } } public async void retract_content_item_notifications() { foreach (uint32 id in content_notifications.values) { try { dbus_notifications.close_notification.begin(id); } catch (Error e) { } } content_notifications.clear(); } public async void retract_conversation_notifications(Conversation conversation) { try { if (content_notifications.has_key(conversation)) { dbus_notifications.close_notification.begin(content_notifications[conversation]); content_notifications.unset(conversation); } if (conversation_notifications.has_key(conversation)) { foreach (var notification_id in conversation_notifications[conversation]) { dbus_notifications.close_notification.begin(notification_id); } conversation_notifications.unset(conversation); } } catch (Error e) { } } private async Variant get_conversation_icon(Conversation conversation) { CompatAvatarDrawer drawer = new CompatAvatarDrawer() { model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(conversation), width_request = 40, height_request = 40 }; Cairo.ImageSurface surface = drawer.draw_image_surface(); Gdk.Pixbuf avatar = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()); var bytes = avatar.pixel_bytes; var image_bytes = Variant.new_from_data(new VariantType("ay"), bytes.get_data(), true, bytes); return new Variant("(iiibii@ay)", avatar.width, avatar.height, avatar.rowstride, avatar.has_alpha, avatar.bits_per_sample, avatar.n_channels, image_bytes); } private void add_action_listener(uint32 id, string name, owned ListenerFunc func) { if (!action_listeners.has_key(id)) { action_listeners[id] = new HashMap(); } action_listeners[id][name] = new ListenerFuncWrapper((owned) func); } delegate void ListenerFunc(); class ListenerFuncWrapper { public ListenerFunc func; public ListenerFuncWrapper(owned ListenerFunc func) { this.func = (owned) func; } } } dino-0.5.0/main/src/ui/notifier_gnotifications.vala0000664000000000000000000002344614776241610021102 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; namespace Dino.Ui { public class GNotificationsNotifier : NotificationProvider, Object { public signal void conversation_selected(Conversation conversation); private StreamInteractor stream_interactor; private HashMap notifications = new HashMap(Conversation.hash_func, Conversation.equals_func); private Set? active_conversation_ids = null; private Set? active_ids = new HashSet(); public GNotificationsNotifier(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public double get_priority() { return 0; } public async void notify_message(Message message, Conversation conversation, string conversation_display_name, string? participant_display_name) { string text = message.body; if (participant_display_name != null) { text = @"$participant_display_name: $text"; } yield notify_content_item(conversation, conversation_display_name, text); } public async void notify_file(FileTransfer file_transfer, Conversation conversation, bool is_image, string conversation_display_name, string? participant_display_name) { string text = ""; if (file_transfer.direction == Message.DIRECTION_SENT) { text = is_image ? _("Image sent") : _("File sent"); } else { text = is_image ? _("Image received") : _("File received"); } if (participant_display_name != null) { text = @"$participant_display_name: $text"; } yield notify_content_item(conversation, conversation_display_name, text); } private async void notify_content_item(Conversation conversation, string title, string body) { if (!notifications.has_key(conversation)) { notifications[conversation] = new Notification(""); notifications[conversation].set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); } Notification notification = notifications[conversation]; notification.set_title(title); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } GLib.Application.get_default().send_notification(conversation.id.to_string(), notifications[conversation]); if (active_conversation_ids != null) { active_conversation_ids.add(conversation.id.to_string()); } } public async void notify_call(Call call, Conversation conversation, bool video, bool multiparty, string conversation_display_name) { Notification notification = new Notification(conversation_display_name); string body = video ? _("Incoming video call") : _("Incoming call"); if (multiparty) { body = video ? _("Incoming video group call") : _("Incoming group call"); } notification.set_body(body); notification.set_urgent(true); notification.set_icon(new ThemedIcon.from_names(new string[] {"call-start-symbolic"})); notification.set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); notification.add_button_with_target_value(_("Reject"), "app.reject-call", new Variant.int32(call.id)); notification.add_button_with_target_value(_("Accept"), "app.accept-call", new Variant.int32(call.id)); GLib.Application.get_default().send_notification(call.id.to_string(), notification); } private async void retract_call_notification(Call call, Conversation conversation) { GLib.Application.get_default().withdraw_notification(call.id.to_string()); } public async void notify_subscription_request(Conversation conversation) { Notification notification = new Notification(_("Subscription request")); notification.set_body(conversation.counterpart.to_string()); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } notification.set_default_action_and_target_value("app.open-conversation", new Variant.int32(conversation.id)); notification.add_button_with_target_value(_("Accept"), "app.accept-subscription", conversation.id); notification.add_button_with_target_value(_("Deny"), "app.deny-subscription", conversation.id); GLib.Application.get_default().send_notification(conversation.id.to_string() + "-subscription", notification); active_ids.add(conversation.id.to_string() + "-subscription"); } public async void notify_connection_error(Account account, ConnectionManager.ConnectionError error) { Notification notification = new Notification(_("Could not connect to %s").printf(account.bare_jid.domainpart)); notification.set_default_action_and_target_value("app.preferences-account", new Variant.int32(account.id)); switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: notification.set_body("Wrong password"); break; case ConnectionManager.ConnectionError.Source.TLS: notification.set_body("Invalid TLS certificate"); break; default: break; } GLib.Application.get_default().send_notification(account.id.to_string() + "-connection-error", notification); } public async void notify_muc_invite(Account account, Jid room_jid, Jid from_jid, string inviter_display_name) { Conversation direct_conversation = new Conversation(from_jid, account, Conversation.Type.CHAT); string display_room = room_jid.bare_jid.to_string(); Notification notification = new Notification(_("Invitation to %s").printf(display_room)); string body = _("%s invited you to %s").printf(inviter_display_name, display_room); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(direct_conversation)); } catch (Error e) { } Conversation group_conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(room_jid, account, Conversation.Type.GROUPCHAT); notification.set_default_action_and_target_value("app.open-muc-join", new Variant.int32(group_conversation.id)); notification.add_button_with_target_value(_("Deny"), "app.deny-invite", group_conversation.id); notification.add_button_with_target_value(_("Accept"), "app.open-muc-join", group_conversation.id); GLib.Application.get_default().send_notification(null, notification); } public async void notify_voice_request(Conversation conversation, Jid from_jid) { string display_name = Util.get_participant_display_name(stream_interactor, conversation, from_jid); string display_room = Util.get_conversation_display_name(stream_interactor, conversation); Notification notification = new Notification(_("Permission request")); string body = _("%s requests the permission to write in %s").printf(display_name, display_room); notification.set_body(body); try { notification.set_icon(yield get_conversation_icon(conversation)); } catch (Error e) { } notification.add_button_with_target_value(_("Deny"), "app.deny-voice-request", conversation.id); notification.add_button_with_target_value(_("Accept"), "app.accept-voice-request", conversation.id); GLib.Application.get_default().send_notification(null, notification); } public async void retract_content_item_notifications() { if (active_conversation_ids == null) { Gee.List conversations = stream_interactor.get_module(ConversationManager.IDENTITY).get_active_conversations(); foreach (Conversation conversation in conversations) { GLib.Application.get_default().withdraw_notification(conversation.id.to_string()); } active_conversation_ids = new HashSet(); } else { foreach (string id in active_conversation_ids) { GLib.Application.get_default().withdraw_notification(id); } active_conversation_ids.clear(); } } public async void retract_conversation_notifications(Conversation conversation) { string subscription_id = conversation.id.to_string() + "-subscription"; if (active_ids.contains(subscription_id)) { GLib.Application.get_default().withdraw_notification(subscription_id); } } private async Icon get_conversation_icon(Conversation conversation) throws Error { CompatAvatarDrawer drawer = new CompatAvatarDrawer() { model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(conversation), width_request = 40, height_request = 40 }; Cairo.ImageSurface surface = drawer.draw_image_surface(); Gdk.Pixbuf avatar = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()); uint8[] buffer; avatar.save_to_buffer(out buffer, "png"); return new BytesIcon(new Bytes(buffer)); } } } dino-0.5.0/main/src/ui/occupant_menu/0000775000000000000000000000000014776241610016145 5ustar rootrootdino-0.5.0/main/src/ui/occupant_menu/list.vala0000664000000000000000000001533514776241610017774 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { [GtkTemplate (ui = "/im/dino/Dino/occupant_list.ui")] public class List : Box { public signal void conversation_selected(Conversation? conversation); private StreamInteractor stream_interactor; [GtkChild] public unowned ListBox list_box; [GtkChild] private unowned SearchEntry search_entry; private Conversation conversation; private string[]? filter_values; private HashMap rows = new HashMap(Jid.hash_func, Jid.equals_func); public HashMap row_wrappers = new HashMap(); public List(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; list_box.set_header_func(header); list_box.set_sort_func(sort); list_box.set_filter_func(filter); search_entry.search_changed.connect(refilter); stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_show_received); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_received_offline_presence); initialize_for_conversation(conversation); } public void initialize_for_conversation(Conversation conversation) { this.conversation = conversation; Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) { foreach (Jid occupant in occupants) { add_occupant(occupant); } } list_box.invalidate_filter(); } private void refilter() { string[]? values = null; string str = search_entry.get_text (); if (str != "") values = str.split(" "); if (filter_values == values) return; filter_values = values; list_box.invalidate_filter(); } public void add_occupant(Jid jid) { var row_wrapper = new ListRow(stream_interactor, conversation, jid); var widget = row_wrapper.get_widget(); row_wrappers[widget] = row_wrapper; rows[jid] = widget; list_box.append(widget); } public void remove_occupant(Jid jid) { list_box.remove(rows[jid]); rows.unset(jid); } private void on_received_offline_presence(Jid jid, Account account) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (rows.has_key(jid)) { remove_occupant(jid); } list_box.invalidate_filter(); } } private void on_show_received(Jid jid, Account account) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (!rows.has_key(jid)) { add_occupant(jid); } list_box.invalidate_filter(); } } private void header(ListBoxRow row, ListBoxRow? before_row) { ListRow row_wrapper1 = row_wrappers[row.get_child()]; Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account); if (a1 == null) return; if (before_row != null) { ListRow row_wrapper2 = row_wrappers[before_row.get_child()]; Xmpp.Xep.Muc.Affiliation? a2 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account); if (a1 != a2) { row.set_header(generate_header_widget(a1, false)); } else if (row.get_header() != null){ row.set_header(null); } } else { row.set_header(generate_header_widget(a1, true)); } } private Widget generate_header_widget(Xmpp.Xep.Muc.Affiliation affiliation, bool top) { string aff_str; switch (affiliation) { case Xmpp.Xep.Muc.Affiliation.OWNER: aff_str = _("Owner"); break; case Xmpp.Xep.Muc.Affiliation.ADMIN: aff_str = _("Admin"); break; case Xmpp.Xep.Muc.Affiliation.MEMBER: aff_str = _("Member"); break; default: aff_str = _("User"); break; } int count = 0; foreach (ListRow row in row_wrappers.values) { Xmpp.Xep.Muc.Affiliation aff = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row.jid, conversation.account); if (aff == affiliation) count++; } Label title_label = new Label("") { margin_start=10, xalign=0 }; title_label.set_markup(@"$(Markup.escape_text(aff_str))"); Label count_label = new Label(@"$count") { xalign=0, margin_end=7, hexpand=true }; count_label.add_css_class("dim-label"); Grid grid = new Grid() { margin_top=top?5:15, column_spacing=5, hexpand=true }; grid.attach(title_label, 0, 0, 1, 1); grid.attach(count_label, 1, 0, 1, 1); grid.attach(new Separator(Orientation.HORIZONTAL) { hexpand=true, vexpand=true }, 0, 1, 2, 1); return grid; } private bool filter(ListBoxRow r) { ListRow row_wrapper = row_wrappers[r.get_child()]; foreach (string filter in filter_values) { return row_wrapper.name_label.label.down().contains(filter.down()); } return true; } private int sort(ListBoxRow row1, ListBoxRow row2) { ListRow row_wrapper1 = row_wrappers[row1.get_child()]; ListRow row_wrapper2 = row_wrappers[row2.get_child()]; int affiliation1 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); int affiliation2 = get_affiliation_ranking(stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper2.jid, row_wrapper2.conversation.account) ?? Xmpp.Xep.Muc.Affiliation.NONE); if (affiliation1 < affiliation2) return -1; else if (affiliation1 > affiliation2) return 1; else return row_wrapper1.name_label.label.collate(row_wrapper2.name_label.label); } private int get_affiliation_ranking(Xmpp.Xep.Muc.Affiliation affiliation) { switch (affiliation) { case Xmpp.Xep.Muc.Affiliation.OWNER: return 1; case Xmpp.Xep.Muc.Affiliation.ADMIN: return 2; case Xmpp.Xep.Muc.Affiliation.MEMBER: return 3; default: return 4; } } } } dino-0.5.0/main/src/ui/occupant_menu/list_row.vala0000664000000000000000000000225014776241610020653 0ustar rootrootusing Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { public class ListRow : Object { private Grid main_grid; private AvatarPicture picture; public Label name_label; public Conversation? conversation; public Jid? jid; construct { Builder builder = new Builder.from_resource("/im/dino/Dino/occupant_list_item.ui"); main_grid = (Grid) builder.get_object("main_grid"); picture = (AvatarPicture) builder.get_object("picture"); name_label = (Label) builder.get_object("name_label"); } public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) { this.conversation = conversation; this.jid = jid; name_label.label = Util.get_participant_display_name(stream_interactor, conversation, jid); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(conversation, jid); } public ListRow.label(string c, string text) { name_label.label = text; picture.model = new ViewModel.CompatAvatarPictureModel(null).add(c); } public Widget get_widget() { return main_grid; } } } dino-0.5.0/main/src/ui/occupant_menu/view.vala0000664000000000000000000001456714776241610020001 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui.OccupantMenu { public class View : Popover { private StreamInteractor stream_interactor; private Conversation conversation; private Stack stack = new Stack() { vhomogeneous=false }; private Box list_box = new Box(Orientation.VERTICAL, 1); private List? list = null; private ListBox invite_list = new ListBox(); private Box? jid_menu = null; private Jid? selected_jid; public View(StreamInteractor stream_interactor, Conversation conversation) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.show.connect(initialize_list); invite_list.append(new ListRow.label("+", _("Invite")).get_widget()); invite_list.can_focus = false; list_box.append(invite_list); invite_list.row_activated.connect(on_invite_clicked); stack.add_named(list_box, "list"); set_child(stack); stack.visible_child_name = "list"; hide.connect(reset); } public void reset() { stack.transition_type = StackTransitionType.NONE; stack.visible_child_name = "list"; if (list != null) list.list_box.unselect_all(); invite_list.unselect_all(); } private void initialize_list() { if (list == null) { list = new List(stream_interactor, conversation); list_box.prepend(list); list.list_box.row_activated.connect((row) => { ListRow row_wrapper = list.row_wrappers[row.get_child()]; show_menu(row_wrapper.jid, row_wrapper.name_label.label); }); } } private void show_list() { if (list != null) list.list_box.unselect_all(); stack.transition_type = StackTransitionType.SLIDE_RIGHT; stack.visible_child_name = "list"; } private void show_menu(Jid jid, string name_) { selected_jid = jid; stack.transition_type = StackTransitionType.SLIDE_LEFT; string name = Markup.escape_text(name_); Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, conversation.account); if (real_jid != null) name += "\n%s".printf(Markup.escape_text(real_jid.bare_jid.to_string())); Box header_box = new Box(Orientation.HORIZONTAL, 5); header_box.append(new Image.from_icon_name("pan-start-symbolic")); header_box.append(new Label(name) { xalign=0, use_markup=true, hexpand=true }); Button header_button = new Button() { has_frame=false }; header_button.child = header_box; Box outer_box = new Box(Orientation.VERTICAL, 5); outer_box.append(header_button); header_button.clicked.connect(show_list); Button private_button = new Button.with_label(_("Start private conversation")) ; outer_box.append(private_button); private_button.clicked.connect(private_conversation_button_clicked); Jid? own_jid = stream_interactor.get_module(MucManager.IDENTITY).get_own_jid(conversation.counterpart, conversation.account); Xmpp.Xep.Muc.Role? role = stream_interactor.get_module(MucManager.IDENTITY).get_role(own_jid, conversation.account); if (role == Xmpp.Xep.Muc.Role.MODERATOR && stream_interactor.get_module(MucManager.IDENTITY).kick_possible(conversation.account, jid)) { Button kick_button = new Button.with_label(_("Kick")) ; outer_box.append(kick_button); kick_button.clicked.connect(kick_button_clicked); } if (stream_interactor.get_module(MucManager.IDENTITY).is_moderated_room(conversation.account, conversation.counterpart) && role == Xmpp.Xep.Muc.Role.MODERATOR){ if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.VISITOR) { Button voice_button = new Button.with_label(_("Grant write permission")) ; outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("participant")); } else if (stream_interactor.get_module(MucManager.IDENTITY).get_role(selected_jid, conversation.account) == Xmpp.Xep.Muc.Role.PARTICIPANT){ Button voice_button = new Button.with_label(_("Revoke write permission")) ; outer_box.append(voice_button); voice_button.clicked.connect(() => voice_button_clicked("visitor")); } } if (jid_menu != null) stack.remove(jid_menu); stack.add_named(outer_box, "menu"); stack.visible_child_name = "menu"; jid_menu = outer_box; } private void private_conversation_button_clicked() { if (selected_jid == null) return; Conversation conversation = stream_interactor.get_module(ConversationManager.IDENTITY).create_conversation(selected_jid, conversation.account, Conversation.Type.GROUPCHAT_PM); stream_interactor.get_module(ConversationManager.IDENTITY).start_conversation(conversation); Application app = GLib.Application.get_default() as Application; app.controller.select_conversation(conversation); } private void kick_button_clicked() { if (selected_jid == null) return; stream_interactor.get_module(MucManager.IDENTITY).kick(conversation.account, conversation.counterpart, selected_jid.resourcepart); } private void voice_button_clicked(string role) { if (selected_jid == null) return; stream_interactor.get_module(MucManager.IDENTITY).change_role(conversation.account, conversation.counterpart, selected_jid.resourcepart, role); } private void on_invite_clicked() { hide(); Gee.List acc_list = new ArrayList(Account.equals_func); acc_list.add(conversation.account); SelectContactDialog add_chat_dialog = new SelectContactDialog(stream_interactor, acc_list); add_chat_dialog.set_transient_for((Window) get_root()); add_chat_dialog.title = _("Invite to Conference"); add_chat_dialog.ok_button.label = _("Invite"); add_chat_dialog.selected.connect((account, jid) => { stream_interactor.get_module(MucManager.IDENTITY).invite(conversation.account, conversation.counterpart, jid); }); add_chat_dialog.present(); } } } dino-0.5.0/main/src/ui/util/0000775000000000000000000000000014776241610014262 5ustar rootrootdino-0.5.0/main/src/ui/util/accounts_combo_box.vala0000664000000000000000000000317714776241610021005 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { class AccountComboBox : ComboBox { public Account? selected { get { TreeIter selected; if (get_active_iter(out selected)) { Value value; list_store.get_value(selected, 1, out value); return value as Account; } return null; } set { TreeIter iter; if (list_store.get_iter_first(out iter)) { int i = 0; do { Value val; list_store.get_value(iter, 1, out val); Account? account = val as Account; if (account != null && account.equals(value)) { active = i; break; } i++; } while (list_store.iter_next(ref iter)); } } } private StreamInteractor? stream_interactor; private Gtk.ListStore list_store = new Gtk.ListStore(2, typeof(string), typeof(Account)); public void initialize(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; CellRendererText renderer = new Gtk.CellRendererText(); pack_start(renderer, true); add_attribute(renderer, "text", 0); TreeIter iter; foreach (Account account in stream_interactor.get_accounts()) { list_store.append(out iter); list_store.set(iter, 0, account.bare_jid.to_string(), 1, account); } set_model(list_store); active = 0; } } } dino-0.5.0/main/src/ui/util/config.vala0000664000000000000000000000414314776241610016376 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; namespace Dino.Ui { public class Config : Object { public Database db { get; private set; } public Config(Database db) { this.db = db; window_maximize = col_to_bool_or_default("window_maximized", false); window_width = col_to_int_or_default("window_width", 1200); window_height = col_to_int_or_default("window_height", 700); } private bool window_maximize_; public bool window_maximize { get { return window_maximize_; } set { if (value == window_maximize_) return; db.settings.upsert() .value(db.settings.key, "window_maximized", true) .value(db.settings.value, value.to_string()) .perform(); window_maximize_ = value; } } public int window_height_; public int window_height { get { return window_height_; } set { if (value == window_height_) return; db.settings.upsert() .value(db.settings.key, "window_height", true) .value(db.settings.value, value.to_string()) .perform(); window_height_ = value; } } public int window_width_; public int window_width { get { return window_width_; } set { if (value == window_width_) return; db.settings.upsert() .value(db.settings.key, "window_width", true) .value(db.settings.value, value.to_string()) .perform(); window_width_ = value; } } private bool col_to_bool_or_default(string key, bool def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? bool.parse(val) : def; } private int col_to_int_or_default(string key, int def) { string? val = db.settings.select({db.settings.value}).with(db.settings.key, "=", key)[db.settings.value]; return val != null ? int.parse(val) : def; } } } dino-0.5.0/main/src/ui/util/data_forms.vala0000664000000000000000000001240414776241610017247 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp.Xep; namespace Dino.Ui.Util { public static GLib.ListStore get_data_form_view_model(DataForms.DataForm data_form) { var list_store = new GLib.ListStore(typeof(ViewModel.PreferencesRow.Any)); foreach (var field in data_form.fields) { var field_view_model = Util.get_data_form_field_view_model(field); if (field_view_model != null) { list_store.append(field_view_model); } } return list_store; } public static ViewModel.PreferencesRow.Any? get_data_form_field_view_model(DataForms.DataForm.Field field) { if (field.type_ == null) return null; ViewModel.PreferencesRow.Any? view_model = null; string? label = null; string? desc = null; if (field.var != null) { switch (field.var) { case "muc#roomconfig_roomname": label = _("Name of the room"); break; case "muc#roomconfig_roomdesc": label = _("Description of the room"); break; case "muc#roomconfig_persistentroom": label = _("Persistent"); desc = _("The room will persist after the last occupant leaves"); break; case "muc#roomconfig_publicroom": label = _("Publicly searchable"); break; case "muc#roomconfig_changesubject": label = _("Occupants may change the subject"); break; case "muc#roomconfig_whois": label = _("Permission to view JIDs"); desc = _("Who is allowed to view the occupants' JIDs?"); break; case "muc#roomconfig_roomsecret": label = _("Password"); // desc = _("A password to restrict access to the room"); break; case "muc#roomconfig_moderatedroom": label = _("Moderated"); desc = _("Only occupants with voice may send messages"); break; case "muc#roomconfig_membersonly": label = _("Members only"); desc = _("Only members may enter the room"); break; // case "muc#roomconfig_historylength": // label = _("Message history"); // desc = _("Maximum amount of backlog issued by the room"); // break; } } if (label == null) label = field.label; switch (field.type_) { case DataForms.DataForm.Type.FIXED: var fixed_field = field as DataForms.DataForm.FixedField; var fixed_model = new ViewModel.PreferencesRow.Text() { text=fixed_field.value }; view_model = fixed_model; break; case DataForms.DataForm.Type.BOOLEAN: DataForms.DataForm.BooleanField boolean_field = field as DataForms.DataForm.BooleanField; var toggle_model = new ViewModel.PreferencesRow.Toggle() { subtitle = desc, state = boolean_field.value }; boolean_field.bind_property("value", toggle_model, "state", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); view_model = toggle_model; break; case DataForms.DataForm.Type.JID_MULTI: return null; case DataForms.DataForm.Type.LIST_SINGLE: DataForms.DataForm.ListSingleField list_single_field = field as DataForms.DataForm.ListSingleField; var combobox_model = new ViewModel.PreferencesRow.ComboBox(); for (int i = 0; i < list_single_field.options.size; i++) { DataForms.DataForm.Option option = list_single_field.options[i]; combobox_model.items.add(option.label); if (option.value == list_single_field.value) combobox_model.active_item = i; } combobox_model.bind_property("active-item", list_single_field, "value", BindingFlags.DEFAULT, (binding, from, ref to) => { var active_item = (int) from; to = list_single_field.options[active_item].value; return true; }); view_model = combobox_model; break; case DataForms.DataForm.Type.LIST_MULTI: return null; case DataForms.DataForm.Type.TEXT_PRIVATE: DataForms.DataForm.TextPrivateField text_private_field = field as DataForms.DataForm.TextPrivateField; var private_entry_model = new ViewModel.PreferencesRow.PrivateText() { text = text_private_field.value }; text_private_field.bind_property("value", private_entry_model, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); view_model = private_entry_model; break; case DataForms.DataForm.Type.TEXT_SINGLE: DataForms.DataForm.TextSingleField text_single_field = field as DataForms.DataForm.TextSingleField; var entry_model = new ViewModel.PreferencesRow.Entry() { text = text_single_field.value }; text_single_field.bind_property("value", entry_model, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); view_model = entry_model; break; default: return null; } view_model.title = label; return view_model; } } dino-0.5.0/main/src/ui/util/file_metadata_providers.vala0000664000000000000000000000153314776241610022005 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gtk; namespace Dino.Ui.Util { public class AudioVideoFileMetadataProvider: Dino.FileMetadataProvider, Object { public bool supports_file(File file) { string? mime_type = file.query_info("*", FileQueryInfoFlags.NONE).get_content_type(); if (mime_type == null) { return false; } return mime_type.has_prefix("audio") || mime_type.has_prefix("video"); } public async void fill_metadata(File file, Xep.FileMetadataElement.FileMetadata metadata) { MediaFile media = MediaFile.for_file(file); if (!media.prepared) { media.notify["prepared"].connect((object, pspec) => { Idle.add(fill_metadata.callback); }); yield; } metadata.length = media.duration / 1000; } } }dino-0.5.0/main/src/ui/util/helper.vala0000664000000000000000000005076214776241610016420 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; using Xmpp.Xep; namespace Dino.Ui.Util { private static Regex URL_REGEX; private static Regex CODE_BLOCK_REGEX; private static Map MATCHING_CHARS; private const unichar[] NON_TRAILING_CHARS = {'\'', '"', ',', '.', ';', '!', '?', '»', '”', '’', '`', '~', '‽', ':', '>', '*', '_'}; private const string[] ALLOWED_SCHEMAS = {"http", "https", "ftp", "ftps", "irc", "ircs", "xmpp", "mailto", "sms", "smsto", "mms", "tel", "geo", "openpgp4fpr", "im", "news", "nntp", "sip", "ssh", "bitcoin", "sftp", "magnet", "vnc"}; private const string[] tango_colors_light = {"FCE94F", "FCAF3E", "E9B96E", "8AE234", "729FCF", "AD7FA8", "EF2929"}; private const string[] tango_colors_medium = {"EDD400", "F57900", "C17D11", "73D216", "3465A4", "75507B", "CC0000"}; private const string[] material_colors_800 = {"D32F2F", "C2185B", "7B1FA2", "512DA8", "303F9F", "1976D2", "0288D1", "0097A7", "00796B", "388E3C", "689F38", "AFB42B", "FFA000", "F57C00", "E64A19", "5D4037"}; private const string[] material_colors_500 = {"F44336", "E91E63", "9C27B0", "673AB7", "3f51B5", "2196F3", "03A9f4", "00BCD4", "009688", "4CAF50", "8BC34a", "CDDC39", "FFC107", "FF9800", "FF5722", "795548"}; private const string[] material_colors_300 = {"E57373", "F06292", "BA68C8", "9575CD", "7986CB", "64B5F6", "4FC3F7", "4DD0E1", "4DB6AC", "81C784", "AED581", "DCE775", "FFD54F", "FFB74D", "FF8A65", "A1887F"}; private const string[] material_colors_200 = {"EF9A9A", "F48FB1", "CE93D8", "B39DDB", "9FA8DA", "90CAF9", "81D4FA", "80DEEA", "80CBC4", "A5D6A7", "C5E1A5", "E6EE9C", "FFE082", "FFCC80", "FFAB91", "BCAAA4"}; public static string get_consistent_hex_color(StreamInteractor stream_interactor, Account account, Jid jid, bool dark_theme = false) { uint8[] rgb; if (stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(jid.bare_jid, account) && jid.resourcepart != null) { rgb = ConsistentColor.string_to_rgb(jid.resourcepart); } else { rgb = ConsistentColor.string_to_rgb(jid.bare_jid.to_string()); } return "%.2x%.2x%.2x".printf(rgb[0], rgb[1], rgb[2]); } public static string get_avatar_hex_color(StreamInteractor stream_interactor, Account account, Jid jid, Conversation? conversation = null) { return get_consistent_hex_color(stream_interactor, account, get_relevant_jid(stream_interactor, account, jid, conversation)); } public static string get_name_hex_color(StreamInteractor stream_interactor, Account account, Jid jid, bool dark_theme = false, Conversation? conversation = null) { return get_consistent_hex_color(stream_interactor, account, get_relevant_jid(stream_interactor, account, jid, conversation), dark_theme); } private static Jid get_relevant_jid(StreamInteractor stream_interactor, Account account, Jid jid, Conversation? conversation = null) { Conversation conversation_ = conversation ?? stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(jid.bare_jid, account); if (conversation_ != null && conversation_.type_ == Conversation.Type.GROUPCHAT) { Jid? real_jid = stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(jid, account); if (real_jid != null) { return real_jid.bare_jid; } } else { return jid.bare_jid; } return jid; } public static string color_for_show(string show) { switch(show) { case "online": return "#9CCC65"; case "away": return "#FFCA28"; case "chat": return "#66BB6A"; case "xa": return "#EF5350"; case "dnd": return "#EF5350"; default: return "#BDBDBD"; } } public static string get_conversation_display_name(StreamInteractor stream_interactor, Conversation conversation) { return Dino.get_conversation_display_name(stream_interactor, conversation, _("%s from %s")); } public static string get_participant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid participant, bool me_is_me = false) { return Dino.get_participant_display_name(stream_interactor, conversation, participant, me_is_me ? _("Me") : null); } public static string? get_real_display_name(StreamInteractor stream_interactor, Account account, Jid jid, bool me_is_me = false) { return Dino.get_real_display_name(stream_interactor, account, jid, me_is_me ? _("Me") : null); } public static string get_groupchat_display_name(StreamInteractor stream_interactor, Account account, Jid jid) { return Dino.get_groupchat_display_name(stream_interactor, account, jid); } public static string get_occupant_display_name(StreamInteractor stream_interactor, Conversation conversation, Jid jid, bool me_is_me = false, bool muc_real_name = false) { return Dino.get_occupant_display_name(stream_interactor, conversation, jid, me_is_me ? _("Me") : null); } public static Gdk.RGBA get_label_pango_color(Label label, string css_color) { Gtk.CssProvider provider = force_color(label, css_color); Gdk.RGBA color_rgba = label.get_style_context().get_color(); label.get_style_context().remove_provider(provider); return color_rgba; } public static string rgba_to_hex(Gdk.RGBA rgba) { return "#%02x%02x%02x%02x".printf( (uint8)(Math.round(rgba.red.clamp(0,1)*255)), (uint8)(Math.round(rgba.green.clamp(0,1)*255)), (uint8)(Math.round(rgba.blue.clamp(0,1)*255)), (uint8)(Math.round(rgba.alpha.clamp(0,1)*255))) .up(); } private const string force_background_css = "%s { background-color: %s; }"; private const string force_color_css = "%s { color: %s; }"; public static Gtk.CssProvider force_css(Gtk.Widget widget, string css) { var p = new Gtk.CssProvider(); try { #if GTK_4_12 && (VALA_0_56_GREATER_11 || VALA_0_58) p.load_from_string(css); #elif (VALA_0_56_11 || VALA_0_56_12) p.load_from_data(css, css.length); #else p.load_from_data(css.data); #endif widget.get_style_context().add_provider(p, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); } catch (GLib.Error err) { // handle err } return p; } public static void force_background(Gtk.Widget widget, string color, string selector = "*") { force_css(widget, force_background_css.printf(selector, color)); } public static Gtk.CssProvider force_color(Gtk.Widget widget, string color, string selector = "*") { return force_css(widget, force_color_css.printf(selector, color)); } public static void force_error_color(Gtk.Widget widget, string selector = "*") { force_color(widget, "@error_color", selector); } public static bool is_dark_theme(Gtk.Widget widget) { Gdk.RGBA bg = widget.get_style_context().get_color(); return (bg.red > 0.5 && bg.green > 0.5 && bg.blue > 0.5); } private static int8 is24h = 0; public static bool is_24h_format() { if (is24h == 0) { Regex has_ampm = /(^|[^%])%[pP]/; Regex has_t_fmt_ampm = /(^|[^%])%r/; unowned string t_fmt = Posix.nl_langinfo(Posix.NLItem.T_FMT); unowned string t_fmt_ampm = Posix.nl_langinfo(Posix.NLItem.T_FMT_AMPM); bool has_am_str = Posix.nl_langinfo(Posix.NLItem.AM_STR).strip() != ""; bool has_pm_str = Posix.nl_langinfo(Posix.NLItem.PM_STR).strip() != ""; is24h = ((has_ampm.match(t_fmt) || has_t_fmt_ampm.match(t_fmt) && has_ampm.match(t_fmt_ampm)) && (has_am_str || has_pm_str)) ? -1 : 1; } return is24h == 1; } public static string format_time(DateTime datetime, string format_24h, string format_12h) { string format = Util.is_24h_format() ? format_24h : format_12h; if (!get_charset(null)) { // No UTF-8 support, use simple colon for time instead format = format.replace("∶", ":"); } return datetime.format(format); } public static Regex get_url_regex() { if (URL_REGEX == null) { URL_REGEX = /\b(((http|ftp)s?:\/\/|(ircs?|xmpp|mailto|sms|smsto|mms|tel|geo|openpgp4fpr|im|news|nntp|sip|ssh|bitcoin|sftp|magnet|vnc|urn):)\S+)/; } return URL_REGEX; } public static Map get_matching_chars() { if (MATCHING_CHARS == null) { MATCHING_CHARS = new HashMap(); MATCHING_CHARS[")".get_char(0)] = "(".get_char(0); MATCHING_CHARS["]".get_char(0)] = "[".get_char(0); MATCHING_CHARS["}".get_char(0)] = "{".get_char(0); } return MATCHING_CHARS; } /** * This replaces spaces with non-breaking spaces when they are adjacent to a non-spacing mark. * * We do this to work-around a bug in Pango. See https://gitlab.gnome.org/GNOME/pango/-/issues/798 and * https://gitlab.gnome.org/GNOME/pango/-/issues/832 * * This is zero-copy iff no space is adjacent to a non-spacing mark, otherwise the provided string will be destroyed * and the returned string should be used instead. */ public static string unbreak_space_around_non_spacing_mark(owned string s) { int current_index = 0; unichar current_char = 0; int prev_index = 0; unichar prev_char = 0; bool is_non_spacing_mark = false; while (s.get_next_char(ref current_index, out current_char)) { int replace_index = -1; if (is_non_spacing_mark && current_char == ' ') { replace_index = prev_index; current_char = ' '; } is_non_spacing_mark = ICU.get_int_property_value(current_char, ICU.Property.BIDI_CLASS) == ICU.CharDirection.DIR_NON_SPACING_MARK; if (prev_char == ' ' && is_non_spacing_mark) { replace_index = prev_index - 1; } if (replace_index != -1) { s = s[0:replace_index] + " " + s[(replace_index + 1):s.length]; current_index += 1; } prev_index = current_index; prev_char = current_char; } return (owned) s; } public static string parse_add_markup(string s_, string? highlight_word, bool parse_links, bool parse_text_markup) { bool ignore_out_var = false; return parse_add_markup_theme(s_, highlight_word, parse_links, parse_text_markup, parse_text_markup, false, ref ignore_out_var); } public static string parse_add_markup_theme(string s_, string? highlight_word, bool parse_links, bool parse_text_markup, bool parse_quotes, bool dark_theme, ref bool theme_dependent, bool already_escaped_ = false) { string s = s_; bool already_escaped = already_escaped_; if (parse_quotes) { string gt = already_escaped ? ">" : ">"; Regex quote_regex = new Regex("((?<=\n)" + gt + ".*(\n|$))|(^" + gt + ".*(\n|$))"); MatchInfo quote_match_info; quote_regex.match(s.down(), 0, out quote_match_info); if (quote_match_info.matches()) { int start, end; string dim_color = dark_theme ? "#BDBDBD": "#707070"; theme_dependent = true; quote_match_info.fetch_pos(0, out start, out end); return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, parse_quotes, dark_theme, ref theme_dependent, already_escaped) + @"$gt" + parse_add_markup_theme(s[start + gt.length:end], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, parse_quotes, dark_theme, ref theme_dependent, already_escaped); } } if (parse_links && !already_escaped) { MatchInfo match_info; get_url_regex().match(s.down(), 0, out match_info); while (match_info.matches()) { int start, end; match_info.fetch_pos(0, out start, out end); string link = s[start:end]; if (GLib.Uri.parse_scheme(link) in ALLOWED_SCHEMAS) { Map matching_chars = get_matching_chars(); unichar close_char; int last_char_index = link.length; while (link.get_prev_char(ref last_char_index, out close_char)) { if (matching_chars.has_key(close_char)) { unichar open_char = matching_chars[close_char]; unichar char; int index = 0; int open = 0, close = 0; while (link.get_next_char(ref index, out char)) { if (char == open_char) { open++; } else if (char == close_char) { close++; } } if (close > open) { // Remove last char from url end -= close_char.to_string().length; link = s[start:end]; } else { break; } } else if (close_char in NON_TRAILING_CHARS) { // Remove last char from url end -= close_char.to_string().length; link = s[start:end]; } else { break; } } return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(link, highlight_word, false, false, false, dark_theme, ref theme_dependent, already_escaped) + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } match_info.next(); } } if (!already_escaped) { s = Markup.escape_text(s); already_escaped = true; } if (highlight_word != null) { try { Regex highlight_regex = new Regex("\\b" + Regex.escape_string(highlight_word.down()) + "\\b"); MatchInfo match_info; highlight_regex.match(s.down(), 0, out match_info); if (match_info.matches()) { int start, end; match_info.fetch_pos(0, out start, out end); return parse_add_markup_theme(s[0:start], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + s[start:end] + "" + parse_add_markup_theme(s[end:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } } catch (RegexError e) { assert_not_reached(); } } if (parse_text_markup) { string[] markup_string = new string[]{"`", "_", "*", "~"}; string[] convenience_tag = new string[]{"tt", "i", "b", "s"}; for (int i = 0; i < markup_string.length; i++) { string markup_esc = Regex.escape_string(markup_string[i]); try { Regex regex = new Regex("(^|\\s)" + markup_esc + "(\\S|\\S.*?\\S)" + markup_esc); MatchInfo match_info; regex.match(s.down(), 0, out match_info); if (match_info.matches()) { int start, end; match_info.fetch_pos(2, out start, out end); return parse_add_markup_theme(s[0:start-1], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped) + "" + s[start-1:start] + "" + @"<$(convenience_tag[i])>" + s[start:end] + @"" + "" + s[end:end+1] + "" + parse_add_markup_theme(s[end+1:s.length], highlight_word, parse_links, parse_text_markup, false, dark_theme, ref theme_dependent, already_escaped); } } catch (RegexError e) { assert_not_reached(); } } } return s; } // Modifies `markups`. public string remove_fallbacks_adjust_markups(string text, bool contains_quote, Gee.List fallbacks, Gee.List markups) { string processed_text = text; foreach (var fallback in fallbacks) { if (fallback.ns_uri == Xep.Replies.NS_URI && contains_quote) { foreach (var fallback_location in fallback.locations) { processed_text = processed_text[0:processed_text.index_of_nth_char(fallback_location.from_char)] + processed_text[processed_text.index_of_nth_char(fallback_location.to_char):processed_text.length]; int length = fallback_location.to_char - fallback_location.from_char; foreach (Xep.MessageMarkup.Span span in markups) { if (span.start_char > fallback_location.to_char) { span.start_char -= length; } if (span.end_char > fallback_location.to_char) { span.end_char -= length; } } } } } return processed_text; } /** * This is a heuristic to count emojis in a string {@link http://example.com/} * * @param markup_text string to search in * @return number of emojis, or -1 if text includes non-emojis. */ public int get_only_emoji_count(string markup_text) { int emoji_no = 0; int index_ref = 0; unichar curchar = 0, altchar = 0; bool last_was_emoji = false, last_was_modifier_base = false, last_was_keycap = false; while (markup_text.get_next_char(ref index_ref, out curchar)) { if (last_was_emoji && last_was_keycap && curchar == 0x20E3) { // keycap sequence continue; } last_was_keycap = false; if (last_was_emoji && curchar == 0x200D && markup_text.get_next_char(ref index_ref, out curchar)) { // zero width joiner last_was_emoji = false; emoji_no--; } if (last_was_emoji && curchar == 0xFE0F) { // Variation selector after emoji is useless, ignoring. } else if (last_was_emoji && last_was_modifier_base && ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER)) { // still an emoji, but no longer a modifier base last_was_modifier_base = false; } else if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_PRESENTATION)) { if (ICU.has_binary_property(curchar, ICU.Property.EMOJI_MODIFIER_BASE)) { last_was_modifier_base = true; } emoji_no++; last_was_emoji = true; } else if (curchar == ' ') { last_was_emoji = false; } else if (markup_text.get_next_char(ref index_ref, out altchar) && altchar == 0xFE0F) { // U+FE0F = VARIATION SELECTOR-16 emoji_no++; last_was_emoji = true; last_was_keycap = (curchar >= 0x30 && curchar <= 0x39) || curchar == 0x23 || curchar == 0x2A; } else { return -1; } } return emoji_no; } public string summarize_whitespaces_to_space(string s) { try { return (/\s+/).replace_literal(s, -1, 0, " "); } catch (RegexError e) { critical("RegexError when summarizing whitespaces in '%s': %s", s, e.message); return s; } } public void present_window(Window window) { #if GDK3_WITH_X11 Gdk.X11.Window x11window = window.get_window() as Gdk.X11.Window; if (x11window != null) { window.present_with_time(Gdk.X11.get_server_time(x11window)); } else { window.present(); } #else window.present(); #endif } public Widget? widget_if_tooltips_active(Widget w) { return use_tooltips() ? w : null; } public string? string_if_tooltips_active(string? s) { return use_tooltips() ? s : null; } public bool use_tooltips() { return Gtk.MINOR_VERSION != 6 || (Gtk.MICRO_VERSION < 4 || Gtk.MICRO_VERSION > 6); } public static void menu_button_set_icon_with_size(MenuButton menu_button, string icon_name, int pixel_size) { #if GTK_4_6 && VALA_0_52 menu_button.set_child(new Image.from_icon_name(icon_name) { pixel_size=pixel_size }); #else menu_button.set_icon_name(icon_name); var button = menu_button.get_first_child() as Button; if (button == null) return; var box = button.child as Box; if (box == null) return; var image = box.get_first_child() as Image; if (image == null) return; image.pixel_size = pixel_size; #endif } } dino-0.5.0/main/src/ui/util/label_hybrid.vala0000664000000000000000000001125114776241610017547 0ustar rootrootusing Gee; using Gtk; namespace Dino.Ui.Util { public class LabelHybrid : Widget { public Stack stack = new Stack(); public Label label = new Label("") { max_width_chars=1, ellipsize=Pango.EllipsizeMode.END }; protected Button button = new Button() { has_frame=false }; internal virtual void init(Widget widget) { this.layout_manager = new BinLayout(); stack.set_parent(this); button.child = label; stack.add_named(button, "label"); stack.add_named(widget, "widget"); button.clicked.connect(() => { show_widget(); }); } public void show_widget() { stack.visible_child_name = "widget"; stack.get_child_by_name("widget").grab_focus(); } public void show_label() { stack.visible_child_name = "label"; } public override void dispose() { stack.unparent(); } } public class EntryLabelHybrid : LabelHybrid { public string text { get { return entry.text; } set { entry.text = value.dup(); update_label(); } } public bool visibility { get { return entry.visibility; } set { entry.visibility = value; } } public float xalign { get { return label.xalign; } set { label.xalign = value; entry.set_alignment(value); } } private Entry? entry_; public Entry entry { get { if (entry_ == null) { entry_ = new Entry(); init(entry_); } return entry_; } set { entry_ = value; } } public EntryLabelHybrid.wrap(Entry e) { init(e); } internal override void init(Widget widget) { Entry? e = widget as Entry; if (e == null) return; entry = e; base.init(entry); update_label(); var key_events = new EventControllerKey(); key_events.key_released.connect(on_key_released); entry.add_controller(key_events); entry.changed.connect(update_label); var focus_events = new EventControllerFocus(); focus_events.leave.connect(update_label); entry.add_controller(focus_events); } private void on_key_released(uint keyval) { if (keyval == Gdk.Key.Return) { show_label(); } } private void update_label() { if (visibility) { label.label = entry.text; } else { string filler = ""; for (int i = 0; i < entry.text.length; i++) filler += entry.get_invisible_char().to_string(); label.label = filler; } } } public class ComboBoxTextLabelHybrid : LabelHybrid { public int active { get { return combobox.active; } set { combobox.active = value; } } public float xalign { get { return label.xalign; } set { label.xalign = value; } } private ComboBoxText combobox_; public ComboBoxText combobox { get { if (combobox_ == null) { combobox_ = new ComboBoxText(); init(combobox_); } return combobox_; } set { combobox_ = combobox; } } public ComboBoxTextLabelHybrid.wrap(ComboBoxText cb) { combobox_ = cb; init(cb); } public void append(string id, string text) { combobox.append(id, text); } public string get_active_text() { return combobox.get_active_text(); } internal override void init(Widget widget) { ComboBoxText? combobox = widget as ComboBoxText; if (combobox == null) return; base.init(combobox); update_label(); combobox.changed.connect(() => { update_label(); show_label(); }); button.clicked.connect(() => { combobox.popup(); }); var focus_events = new EventControllerFocus(); focus_events.leave.connect(on_focus_leave); combobox.add_controller(focus_events); } private void on_focus_leave() { update_label(); show_label(); } private void update_label() { label.label = combobox.get_active_text(); } } public class LabelHybridGroup { private Gee.List hybrids = new ArrayList(); public void add(LabelHybrid hybrid) { hybrids.add(hybrid); hybrid.notify["visible-child-name"].connect(() => { if (hybrid.stack.visible_child_name == "label") return; foreach (LabelHybrid h in hybrids) { if (h != hybrid) { h.stack.set_visible_child_name("label"); } } }); } } } dino-0.5.0/main/src/ui/util/preference_group.vala0000664000000000000000000001104014776241610020455 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp.Xep; namespace Dino.Ui.Util { public Adw.PreferencesGroup rows_to_preference_group(GLib.ListStore row_view_models, string title) { var preference_group = new Adw.PreferencesGroup() { title=title }; for (int preference_group_i = 0; preference_group_i < row_view_models.get_n_items(); preference_group_i++) { var preferences_row = (ViewModel.PreferencesRow.Any) row_view_models.get_item(preference_group_i); Widget? w = row_to_preference_row(preferences_row); if (w == null) continue; preference_group.add(w); } return preference_group; } public Adw.PreferencesRow? row_to_preference_row(ViewModel.PreferencesRow.Any preferences_row) { var entry_view_model = preferences_row as ViewModel.PreferencesRow.Entry; if (entry_view_model != null) { Adw.EntryRow view = new Adw.EntryRow() { title = entry_view_model.title, show_apply_button=true }; entry_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => { var str = (string) from; to = str ?? ""; return true; }); view.apply.connect(() => { entry_view_model.changed(); }); return view; } var password_view_model = preferences_row as ViewModel.PreferencesRow.PrivateText; if (password_view_model != null) { Adw.PasswordEntryRow view = new Adw.PasswordEntryRow() { title = password_view_model.title, show_apply_button=true }; password_view_model.bind_property("text", view, "text", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from, ref to) => { var str = (string) from; to = str ?? ""; return true; }); view.apply.connect(() => { password_view_model.changed(); }); return view; } var row_text = preferences_row as ViewModel.PreferencesRow.Text; if (row_text != null) { var view = new Adw.ActionRow() { title = row_text.title, subtitle = row_text.text, #if Adw_1_3 subtitle_selectable = true, #endif }; view.add_css_class("property"); Util.force_css(view, "row.property > box.header > box.title > .title { font-weight: 400; font-size: 9pt; opacity: 0.55; }"); Util.force_css(view, "row.property > box.header > box.title > .subtitle { font-size: inherit; opacity: 1; }"); return view; } var toggle_view_model = preferences_row as ViewModel.PreferencesRow.Toggle; if (toggle_view_model != null) { var view = new Adw.ActionRow() { title = toggle_view_model.title, subtitle = toggle_view_model.subtitle }; var toggle = new Switch() { valign = Align.CENTER }; view.activatable_widget = toggle; view.add_suffix(toggle); toggle_view_model.bind_property("state", toggle, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); return view; } var combobox_view_model = preferences_row as ViewModel.PreferencesRow.ComboBox; if (combobox_view_model != null) { var string_list = new StringList(null); foreach (string text in combobox_view_model.items) { string_list.append(text); } #if Adw_1_4 var view = new Adw.ComboRow() { title = combobox_view_model.title }; view.model = string_list; combobox_view_model.bind_property("active-item", view, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); #else var view = new Adw.ActionRow() { title = combobox_view_model.title }; var drop_down = new DropDown(string_list, null) { valign = Align.CENTER }; combobox_view_model.bind_property("active-item", drop_down, "selected", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); view.activatable_widget = drop_down; view.add_suffix(drop_down); #endif return view; } var widget_view_model = preferences_row as ViewModel.PreferencesRow.WidgetDeprecated; if (widget_view_model != null) { var view = new Adw.ActionRow() { title = widget_view_model.title }; view.add_suffix(widget_view_model.widget); return view; } return null; } }dino-0.5.0/main/src/ui/util/preview_file_chooser_native.vala0000664000000000000000000000451514776241610022704 0ustar rootrootusing Gdk; using Gtk; using Dino.Entities; namespace Dino.Ui { public class PreviewFileChooserNative : Object { private const int PREVIEW_SIZE = 180; private const int PREVIEW_PADDING = 5; private Gtk.FileChooserNative? chooser = null; private Image preview_image = new Image(); public PreviewFileChooserNative(string? title, Gtk.Window? parent, FileChooserAction action, string? accept_label, string? cancel_label) { chooser = new FileChooserNative(title, parent, action, accept_label, cancel_label); chooser.set_preview_widget(this.preview_image); chooser.use_preview_label = false; chooser.preview_widget_active = false; chooser.update_preview.connect(on_update_preview); } public void add_filter(owned Gtk.FileFilter filter) { chooser.add_filter(filter); } public SList get_files() { return chooser.get_files(); } public int run() { return chooser.run(); } public string? get_filename() { return chooser.get_filename(); } private void on_update_preview() { Pixbuf preview_pixbuf = get_preview_pixbuf(); if (preview_pixbuf != null) { int extra_space = PREVIEW_SIZE - preview_pixbuf.width; int smaller_half = extra_space/2; int larger_half = extra_space - smaller_half; preview_image.set_margin_start(PREVIEW_PADDING + smaller_half); preview_image.set_margin_end(PREVIEW_PADDING + larger_half); preview_image.set_from_pixbuf(preview_pixbuf); chooser.preview_widget_active = true; } else { chooser.preview_widget_active = false; } } private Pixbuf? get_preview_pixbuf() { string? filename = chooser.get_preview_filename(); if (filename == null) { return null; } int width = 0; int height = 0; Gdk.PixbufFormat? format = Gdk.Pixbuf.get_file_info(filename, out width, out height); if (format == null) { return null; } try { Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file_at_scale(filename, PREVIEW_SIZE, PREVIEW_SIZE, true); pixbuf = pixbuf.apply_embedded_orientation(); return pixbuf; } catch (Error e) { return null; } } } } dino-0.5.0/main/src/ui/util/size_request_box.vala0000664000000000000000000000177214776241610020530 0ustar rootrootusing Gtk; namespace Dino.Ui { public class SizeRequestBox : Box { public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; } public override Gtk.SizeRequestMode get_request_mode() { return size_request_mode; } } public class SizeRequestBin : Widget { public SizeRequestMode size_request_mode { get; set; default = SizeRequestMode.CONSTANT_SIZE; } construct { this.layout_manager = new BinLayout(); } public override void compute_expand_internal(out bool hexpand, out bool vexpand) { hexpand = false; vexpand = false; Widget child = get_first_child(); while (child != null) { hexpand = hexpand || child.compute_expand(Orientation.HORIZONTAL); vexpand = vexpand || child.compute_expand(Orientation.VERTICAL); child = child.get_next_sibling(); } } public override Gtk.SizeRequestMode get_request_mode() { return size_request_mode; } } } dino-0.5.0/main/src/ui/util/sizing_bin.vala0000664000000000000000000000630414776241610017265 0ustar rootrootusing Gtk; namespace Dino.Ui { public class SizingBin : Widget { public int min_width { get; set; default = -1; } public int target_width { get; set; default = -1; } public int max_width { get; set; default = -1; } public int min_height { get; set; default = -1; } public int target_height { get; set; default = -1; } public int max_height { get; set; default = -1; } public override void compute_expand_internal(out bool hexpand, out bool vexpand) { hexpand = false; vexpand = false; Widget child = get_first_child(); while (child != null) { hexpand = hexpand || child.compute_expand(Orientation.HORIZONTAL); vexpand = vexpand || child.compute_expand(Orientation.VERTICAL); child = child.get_next_sibling(); } } public override void size_allocate(int width, int height, int baseline) { if (max_height != -1) height = int.min(height, max_height); if (max_width != -1) width = int.min(width, max_width); Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { child.allocate(width, height, baseline, null); } child = child.get_next_sibling(); } } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (orientation == Orientation.HORIZONTAL) { minimum = min_width; natural = target_width; } else { minimum = min_height; natural = target_height; } minimum_baseline = -1; natural_baseline = -1; Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { int child_min = 0; int child_nat = 0; int child_min_baseline = -1; int child_nat_baseline = -1; child.measure(orientation, for_size, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); minimum = int.max(minimum, child_min); natural = int.max(natural, child_nat); if (child_min_baseline > 0) { minimum_baseline = int.max(minimum_baseline, child_min_baseline); } if (child_nat_baseline > 0) { natural_baseline = int.max(natural_baseline, child_nat_baseline); } } child = child.get_next_sibling(); } if (orientation == Orientation.HORIZONTAL) { if (max_width != -1) natural = int.min(natural, max_width); } else { if (max_height != -1) natural = int.min(natural, max_height); } natural = int.max(natural, minimum); } public override SizeRequestMode get_request_mode() { Widget child = get_first_child(); if (child != null) { return child.get_request_mode(); } return SizeRequestMode.CONSTANT_SIZE; } public override void dispose() { var child = this.get_first_child(); if (child != null) child.unparent(); } } } dino-0.5.0/main/src/ui/widgets/0000775000000000000000000000000014776241610014753 5ustar rootrootdino-0.5.0/main/src/ui/widgets/avatar_picture.vala0000664000000000000000000006252714776241610020645 0ustar rootrootusing Dino.Entities; using Gtk; using Xmpp; public class Dino.Ui.ViewModel.AvatarPictureTileModel : Object { public string display_text { get; set; } public Gdk.RGBA background_color { get; set; } public File? image_file { get; set; } } public class Dino.Ui.ViewModel.AvatarPictureModel : Object { public ListModel tiles { get; set; } } public class Dino.Ui.ViewModel.ConversationParticipantAvatarPictureTileModel : AvatarPictureTileModel { private StreamInteractor stream_interactor; private AvatarManager? avatar_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(AvatarManager.IDENTITY); } } private MucManager? muc_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(MucManager.IDENTITY); } } private Conversation? conversation; private Jid? primary_avatar_jid; private Jid? secondary_avatar_jid; private Jid? display_name_jid; public ConversationParticipantAvatarPictureTileModel(StreamInteractor stream_interactor, Conversation conversation, Jid jid) { this.stream_interactor = stream_interactor; this.conversation = conversation; this.primary_avatar_jid = jid; this.display_name_jid = jid; string color_id = jid.to_string(); if (conversation.type_ != Conversation.Type.CHAT && primary_avatar_jid.equals_bare(conversation.counterpart)) { Jid? real_jid = muc_manager.get_real_jid(primary_avatar_jid, conversation.account); if (real_jid != null && muc_manager.is_private_room(conversation.account, conversation.counterpart.bare_jid)) { secondary_avatar_jid = primary_avatar_jid; primary_avatar_jid = real_jid.bare_jid; color_id = primary_avatar_jid.to_string(); } else { color_id = jid.resourcepart.to_string(); } } else if (conversation.type_ == Conversation.Type.CHAT) { primary_avatar_jid = jid.bare_jid; color_id = primary_avatar_jid.to_string(); } string display = Util.get_participant_display_name(stream_interactor, conversation, display_name_jid); display_text = display.get_char(0).toupper().to_string(); stream_interactor.get_module(RosterManager.IDENTITY).updated_roster_item.connect(on_roster_updated); float[] rgbf = color_id != null ? Xep.ConsistentColor.string_to_rgbf(color_id) : new float[] {0.5f, 0.5f, 0.5f}; background_color = Gdk.RGBA() { red = rgbf[0], green = rgbf[1], blue = rgbf[2], alpha = 1.0f}; update_image_file(); avatar_manager.received_avatar.connect(on_received_avatar); avatar_manager.fetched_avatar.connect(on_received_avatar); } private void update_image_file() { File image_file = avatar_manager.get_avatar_file(conversation.account, primary_avatar_jid); if (image_file == null && secondary_avatar_jid != null) { image_file = avatar_manager.get_avatar_file(conversation.account, secondary_avatar_jid); } this.image_file = image_file; } private void on_received_avatar(Jid jid, Account account) { if (account.equals(conversation.account) && (jid.equals(primary_avatar_jid) || jid.equals(secondary_avatar_jid))) { update_image_file(); } } private void on_roster_updated(Account account, Jid jid) { if (account.equals(conversation.account) && jid.equals(display_name_jid)) { string display = Util.get_participant_display_name(stream_interactor, conversation, display_name_jid); display_text = display.get_char(0).toupper().to_string(); } } } public class Dino.Ui.ViewModel.CompatAvatarPictureModel : AvatarPictureModel { private StreamInteractor stream_interactor; private AvatarManager? avatar_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(AvatarManager.IDENTITY); } } private MucManager? muc_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(MucManager.IDENTITY); } } private PresenceManager? presence_manager { owned get { return stream_interactor == null ? null : stream_interactor.get_module(PresenceManager.IDENTITY); } } private ConnectionManager? connection_manager { owned get { return stream_interactor == null ? null : stream_interactor.connection_manager; } } private Conversation? conversation; construct { tiles = new GLib.ListStore(typeof(ViewModel.AvatarPictureTileModel)); } public CompatAvatarPictureModel(StreamInteractor? stream_interactor) { this.stream_interactor = stream_interactor; if (stream_interactor != null) { connect_signals_weak(this); } } private static void connect_signals_weak(CompatAvatarPictureModel model_) { WeakRef model_weak = WeakRef(model_); ulong muc_manager_private_room_occupant_updated_handler_id = 0; ulong muc_manager_proom_info_updated_handler_id = 0; ulong avatar_manager_received_avatar_handler_id = 0; ulong avatar_manager_fetched_avatar_handler_id = 0; muc_manager_private_room_occupant_updated_handler_id = model_.muc_manager.private_room_occupant_updated.connect((muc_manager, account, room, jid) => { CompatAvatarPictureModel? model = (CompatAvatarPictureModel) model_weak.get(); if (model != null) { model.on_room_updated(account, room); } else if (muc_manager_private_room_occupant_updated_handler_id != 0) { muc_manager.disconnect(muc_manager_private_room_occupant_updated_handler_id); muc_manager_private_room_occupant_updated_handler_id = 0; } }); muc_manager_proom_info_updated_handler_id = model_.muc_manager.room_info_updated.connect((muc_manager, account, room) => { CompatAvatarPictureModel? model = (CompatAvatarPictureModel) model_weak.get(); if (model != null) { model.on_room_updated(account, room); } else if (muc_manager_proom_info_updated_handler_id != 0) { muc_manager.disconnect(muc_manager_proom_info_updated_handler_id); muc_manager_proom_info_updated_handler_id = 0; } }); avatar_manager_received_avatar_handler_id = model_.avatar_manager.received_avatar.connect((avatar_manager, jid, account) => { CompatAvatarPictureModel? model = (CompatAvatarPictureModel) model_weak.get(); if (model != null) { model.on_received_avatar(jid, account); } else if (avatar_manager_received_avatar_handler_id != 0) { avatar_manager.disconnect(avatar_manager_received_avatar_handler_id); avatar_manager_received_avatar_handler_id = 0; } }); avatar_manager_fetched_avatar_handler_id = model_.avatar_manager.fetched_avatar.connect((avatar_manager, jid, account) => { CompatAvatarPictureModel? model = (CompatAvatarPictureModel) model_weak.get(); if (model != null) { model.on_received_avatar(jid, account); } else if (avatar_manager_fetched_avatar_handler_id != 0) { avatar_manager.disconnect(avatar_manager_fetched_avatar_handler_id); avatar_manager_fetched_avatar_handler_id = 0; } }); } private void on_room_updated(Account account, Jid room) { if (conversation != null && account.equals(conversation.account) && conversation.counterpart.equals_bare(room)) { reset(); set_conversation(conversation); } } private void on_received_avatar(Jid jid, Account account) { on_room_updated(account, jid); } public void reset() { (tiles as GLib.ListStore).remove_all(); } public CompatAvatarPictureModel set_conversation(Conversation conversation) { if (stream_interactor == null) { critical("set_conversation() used on CompatAvatarPictureModel without stream_interactor"); return this; } this.conversation = conversation; if (conversation.type_ == Conversation.Type.GROUPCHAT) { if (avatar_manager.has_avatar(conversation.account, conversation.counterpart)) { add_internal("#", conversation.counterpart.to_string(), avatar_manager.get_avatar_file(conversation.account, conversation.counterpart)); } else { Gee.List? occupants = muc_manager.get_other_offline_members(conversation.counterpart, conversation.account); if (occupants != null && !occupants.is_empty && muc_manager.is_private_room(conversation.account, conversation.counterpart)) { int count = occupants.size > 4 ? 3 : occupants.size; for (int i = 0; i < count; i++) { add_participant(conversation, occupants[i]); } if (occupants.size > 4) { add_internal("+"); } } else { add_internal("#", conversation.counterpart.to_string()); } } } else { add_participant(conversation, conversation.counterpart); } return this; } public CompatAvatarPictureModel add_participant(Conversation conversation, Jid jid) { if (stream_interactor == null) { critical("add_participant() used on CompatAvatarPictureModel without stream_interactor"); return this; } (tiles as GLib.ListStore).append(new ConversationParticipantAvatarPictureTileModel(stream_interactor, conversation, jid)); return this; } public CompatAvatarPictureModel add(string display, string? color_id = null, File? image_file = null) { add_internal(display, color_id, image_file); return this; } private AvatarPictureTileModel add_internal(string display, string? color_id = null, File? image_file = null) { GLib.ListStore store = tiles as GLib.ListStore; float[] rgbf = color_id != null ? Xep.ConsistentColor.string_to_rgbf(color_id) : new float[] {0.5f, 0.5f, 0.5f}; var model = new ViewModel.AvatarPictureTileModel() { display_text = display.get_char(0).toupper().to_string(), background_color = Gdk.RGBA() { red = rgbf[0], green = rgbf[1], blue = rgbf[2], alpha = 1.0f}, image_file = image_file }; store.append(model); return model; } } public class Dino.Ui.CompatAvatarDrawer { public float radius_percent { get; set; default = 0.2f; } public ViewModel.AvatarPictureModel? model { get; set; } public int height_request { get; set; default = 35; } public int width_request { get; set; default = 35; } public string font_family { get; set; default = "Sans"; } public Cairo.ImageSurface draw_image_surface() { Cairo.ImageSurface surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, width_request, height_request); draw_on_context(new Cairo.Context(surface)); return surface; } public void draw_on_context(Cairo.Context ctx) { double radius = (width_request + height_request) * 0.25f * radius_percent; double degrees = Math.PI / 180.0; ctx.new_sub_path(); ctx.arc(width_request - radius, radius, radius, -90 * degrees, 0 * degrees); ctx.arc(width_request - radius, height_request - radius, radius, 0 * degrees, 90 * degrees); ctx.arc(radius, height_request - radius, radius, 90 * degrees, 180 * degrees); ctx.arc(radius, radius, radius, 180 * degrees, 270 * degrees); ctx.close_path(); ctx.clip(); if (this.model.tiles.get_n_items() == 4) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width_request, height_request); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width_request - 1, height_request - 1, 2), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width_request - 1, height_request - 1, 2), width_request + 1, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 2, width_request - 1, height_request - 1, 2), 0, height_request + 1); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 3, width_request - 1, height_request - 1, 2), width_request + 1, height_request + 1); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.model.tiles.get_n_items() == 3) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width_request, height_request); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width_request - 1, height_request - 1, 2), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width_request - 1, height_request * 2, 2), width_request + 1, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 2, width_request - 1, height_request - 1, 2), 0, width_request + 1); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.model.tiles.get_n_items() == 2) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width_request, height_request); Cairo.Context bufctx = new Cairo.Context(buffer); bufctx.scale(0.5, 0.5); bufctx.set_source_surface(sub_surface_idx(ctx, 0, width_request - 1, height_request * 2, 2), 0, 0); bufctx.paint(); bufctx.set_source_surface(sub_surface_idx(ctx, 1, width_request - 1, height_request * 2, 2), width_request + 1, 0); bufctx.paint(); ctx.set_source_surface(buffer, 0, 0); ctx.paint(); } else if (this.model.tiles.get_n_items() == 1) { ctx.set_source_surface(sub_surface_idx(ctx, 0, width_request, height_request, 1), 0, 0); ctx.paint(); } else if (this.model.tiles.get_n_items() == 0) { ctx.set_source_surface(sub_surface_idx(ctx, -1, width_request, height_request, 1), 0, 0); ctx.paint(); } ctx.set_source_rgb(0, 0, 0); } private Cairo.Surface sub_surface_idx(Cairo.Context ctx, int idx, int width, int height, int font_factor = 1) { ViewModel.AvatarPictureTileModel tile = (ViewModel.AvatarPictureTileModel) this.model.tiles.get_item(idx); Gdk.Pixbuf? avatar = tile.image_file != null ? new Gdk.Pixbuf.from_file(tile.image_file.get_path()) : null; string? name = idx >= 0 ? tile.display_text : ""; Gdk.RGBA hex_color = tile.background_color; return sub_surface(ctx, font_family, avatar, name, hex_color, width, height, font_factor); } private static Cairo.Surface sub_surface(Cairo.Context ctx, string font_family, Gdk.Pixbuf? avatar, string? name, Gdk.RGBA background_color, int width, int height, int font_factor = 1) { Cairo.Surface buffer = new Cairo.Surface.similar(ctx.get_target(), Cairo.Content.COLOR_ALPHA, width, height); Cairo.Context bufctx = new Cairo.Context(buffer); if (avatar == null) { Gdk.cairo_set_source_rgba(bufctx, background_color); bufctx.rectangle(0, 0, width, height); bufctx.fill(); string text = name == null ? "…" : name.get_char(0).toupper().to_string(); bufctx.select_font_face(font_family, Cairo.FontSlant.NORMAL, Cairo.FontWeight.NORMAL); bufctx.set_font_size(width / font_factor < 40 ? font_factor * 17 : font_factor * 25); Cairo.TextExtents extents; bufctx.text_extents(text, out extents); double x_pos = width/2 - (extents.width/2 + extents.x_bearing); double y_pos = height/2 - (extents.height/2 + extents.y_bearing); bufctx.move_to(x_pos, y_pos); bufctx.set_source_rgba(1, 1, 1, 1); bufctx.show_text(text); } else { double w_scale = (double) width / avatar.width; double h_scale = (double) height / avatar.height; double scale = double.max(w_scale, h_scale); bufctx.scale(scale, scale); double x_off = 0, y_off = 0; if (scale == h_scale) { x_off = (width / scale - avatar.width) / 2.0; } else { y_off = (height / scale - avatar.height) / 2.0; } Gdk.cairo_set_source_pixbuf(bufctx, avatar, x_off, y_off); bufctx.get_source().set_filter(Cairo.Filter.BEST); bufctx.paint(); } return buffer; } } public class Dino.Ui.AvatarPicture : Gtk.Widget { public float radius_percent { get; set; default = 0.2f; } public ViewModel.AvatarPictureModel? model { get; set; } private Gee.List tiles = new Gee.ArrayList(); private ViewModel.AvatarPictureModel? old_model; private ulong model_tiles_items_changed_handler; construct { height_request = 35; width_request = 35; set_css_name("picture"); add_css_class("avatar"); notify["radius-percent"].connect(queue_draw); notify["model"].connect(on_model_changed); } private void on_model_changed() { if (old_model != null) { old_model.tiles.disconnect(model_tiles_items_changed_handler); } foreach (Tile tile in tiles) { tile.unparent(); tile.dispose(); } tiles.clear(); old_model = model; if (model != null) { model_tiles_items_changed_handler = model.tiles.items_changed.connect(on_model_items_changed); for(int i = 0; i < model.tiles.get_n_items(); i++) { Tile tile = new Tile(); tile.model = model.tiles.get_item(i) as ViewModel.AvatarPictureTileModel; tile.insert_after(this, tiles.is_empty ? null : tiles.last()); tiles.add(tile); } } } private void on_model_items_changed(uint position, uint removed, uint added) { while (removed > 0) { Tile old = tiles.remove_at((int) position); old.unparent(); old.dispose(); removed--; } while (added > 0) { Tile tile = new Tile(); tile.model = model.tiles.get_item(position) as ViewModel.AvatarPictureTileModel; tile.insert_after(this, position == 0 ? null : tiles[(int) position - 1]); tiles.insert((int) position, tile); position++; added--; } queue_allocate(); } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum_baseline = natural_baseline = -1; if (orientation == Orientation.HORIZONTAL) { minimum = natural = width_request; } else { minimum = natural = height_request; } } public override void size_allocate(int width, int height, int baseline) { int half_width_size = width / 2; int half_height_size = height / 2; int half_width_offset = (width % 2 == 0) ? half_width_size : half_width_size + 1; int half_height_offset = (height % 2 == 0) ? half_height_size : half_height_size + 1; if (tiles.size == 1) { tiles[0].allocate(width, height, baseline, null); } else if (tiles.size == 2) { tiles[0].allocate_size(Allocation() { x = 0, y = 0, width = half_width_size, height = height }, baseline); tiles[1].allocate_size(Allocation() { x = half_width_offset, y = 0, width = half_width_size, height = height }, baseline); } else if (tiles.size == 3) { tiles[0].allocate_size(Allocation() { x = 0, y = 0, width = half_width_size, height = height }, baseline); tiles[1].allocate_size(Allocation() { x = half_width_offset, y = 0, width = half_width_size, height = half_height_size }, baseline); tiles[2].allocate_size(Allocation() { x = half_width_offset, y = half_height_offset, width = half_width_size, height = half_height_size }, baseline); } else if (tiles.size == 4) { tiles[0].allocate_size(Allocation() { x = 0, y = 0, width = half_width_size, height = half_height_size }, baseline); tiles[1].allocate_size(Allocation() { x = half_width_offset, y = 0, width = half_width_size, height = half_height_size }, baseline); tiles[2].allocate_size(Allocation() { x = 0, y = half_height_offset, width = half_width_size, height = half_height_size }, baseline); tiles[3].allocate_size(Allocation() { x = half_width_offset, y = half_height_offset, width = half_width_size, height = half_height_size }, baseline); } } public override SizeRequestMode get_request_mode() { return SizeRequestMode.CONSTANT_SIZE; } public override void snapshot(Gtk.Snapshot snapshot) { Graphene.Rect bounds = Graphene.Rect(); bounds.init(0, 0, get_width(), get_height()); Gsk.RoundedRect rounded_rect = Gsk.RoundedRect(); rounded_rect.init_from_rect(bounds, (get_width() + get_height()) * 0.25f * radius_percent); snapshot.push_rounded_clip(rounded_rect); base.snapshot(snapshot); snapshot.pop(); } public override void dispose() { model = null; on_model_changed(); base.dispose(); } private class Tile : Gtk.Widget { public ViewModel.AvatarPictureTileModel? model { get; set; } public Gdk.RGBA background_color { get; set; default = Gdk.RGBA(){ red = 1.0f, green = 1.0f, blue = 1.0f, alpha = 0.0f }; } public string display_text { get { return label.get_text(); } set { label.set_text(value); } } public File? image_file { get { return picture.file; } set { picture.file = value; } } private Binding? background_color_binding; private Binding? display_text_binding; private Binding? image_file_binding; private Label label = new Label(""); private Picture picture = new Picture(); construct { label.insert_after(this, null); label.attributes = new Pango.AttrList(); label.attributes.insert(Pango.attr_foreground_new(uint16.MAX, uint16.MAX, uint16.MAX)); #if GTK_4_8 && (VALA_0_56_GREATER_5 || VALA_0_58) picture.content_fit = Gtk.ContentFit.COVER; #elif GTK_4_8 picture.@set("content-fit", 2); #endif picture.insert_after(this, label); this.notify["model"].connect(on_model_changed); } private void on_model_changed() { if (background_color_binding != null) background_color_binding.unbind(); if (display_text_binding != null) display_text_binding.unbind(); if (image_file_binding != null) image_file_binding.unbind(); if (model != null) { background_color_binding = model.bind_property("background-color", this, "background-color", BindingFlags.SYNC_CREATE); display_text_binding = model.bind_property("display-text", this, "display-text", BindingFlags.SYNC_CREATE); image_file_binding = model.bind_property("image-file", this, "image-file", BindingFlags.SYNC_CREATE); } else { background_color_binding = null; display_text_binding = null; image_file_binding = null; } } public override void dispose() { if (background_color_binding != null) background_color_binding.unbind(); if (display_text_binding != null) display_text_binding.unbind(); if (image_file_binding != null) image_file_binding.unbind(); background_color_binding = null; display_text_binding = null; image_file_binding = null; label.unparent(); picture.unparent(); base.dispose(); } public override void size_allocate(int width, int height, int baseline) { int min, nat, bl_min, bl_nat; picture.measure(Orientation.HORIZONTAL, -1, out min, out nat, out bl_min, out bl_nat); if (nat > 0) { picture.allocate(width, height, baseline, null); label.visible = false; } else { picture.allocate(0, 0, 0, null); label.attributes = new Pango.AttrList(); label.attributes.insert(Pango.attr_foreground_new(uint16.MAX, uint16.MAX, uint16.MAX)); label.attributes.insert(Pango.attr_scale_new(double.min((double)width, (double)height) * 0.05)); label.margin_bottom = height/40; label.visible = true; label.allocate(width, height, baseline, null); } } public override void snapshot(Gtk.Snapshot snapshot) { if (label.visible) { Graphene.Rect bounds = Graphene.Rect(); bounds.init(0, 0, get_width(), get_height()); snapshot.append_node(new Gsk.ColorNode(background_color, bounds)); } base.snapshot(snapshot); } } } dino-0.5.0/main/src/ui/widgets/date_separator.vala0000664000000000000000000001000014776241610020604 0ustar rootrootusing Gtk; public class Dino.Ui.ViewModel.DateSeparatorModel : Object { public string date_label { get; set; } } public class Dino.Ui.ViewModel.CompatDateSeparatorModel : DateSeparatorModel { private DateTime date; private uint time_update_timeout = 0; public CompatDateSeparatorModel(DateTime date) { this.date = date; update_time_label(); } private static string get_relative_time(DateTime time) { DateTime time_local = time.to_local(); DateTime now_local = new DateTime.now_local(); if (time_local.get_year() == now_local.get_year() && time_local.get_month() == now_local.get_month() && time_local.get_day_of_month() == now_local.get_day_of_month()) { return _("Today"); } DateTime now_local_minus = now_local.add_days(-1); if (time_local.get_year() == now_local_minus.get_year() && time_local.get_month() == now_local_minus.get_month() && time_local.get_day_of_month() == now_local_minus.get_day_of_month()) { return _("Yesterday"); } if (time_local.get_year() != now_local.get_year()) { return /* xgettext:no-c-format */ time_local.format("%x"); } TimeSpan timespan = now_local.difference(time_local); if (timespan < 7 * TimeSpan.DAY) { return /* xgettext:no-c-format */ time_local.format(_("%a, %b %d")); } else { return /* xgettext:no-c-format */ time_local.format(_("%b %d")); } } private static void on_time_update_timeout(CompatDateSeparatorModel self) { if (self.time_update_timeout != 0) self.update_time_label(); } private void update_time_label() { date_label = get_relative_time(date); time_update_timeout = Dino.WeakTimeout.add_seconds_once(get_next_time_change(), this, on_time_update_timeout); } private int get_next_time_change() { DateTime now = new DateTime.now_local(); return (23 - now.get_hour()) * 3600 + (59 - now.get_minute()) * 60 + (59 - now.get_second()) + 1; } public override void dispose() { base.dispose(); if (time_update_timeout != 0) { Source.remove(time_update_timeout); time_update_timeout = 0; } } } public class Dino.Ui.DateSeparator : Gtk.Widget { public ViewModel.DateSeparatorModel? model { get; set; } public string date_label { get { return label.get_text(); } set { label.set_text(value); } } private Label label = new Label("") { halign = Align.CENTER, hexpand = false }; private Binding? label_text_binding; construct { layout_manager = new BinLayout(); halign = Align.CENTER; hexpand = true; label.add_css_class("dim-label"); label.attributes = new Pango.AttrList(); label.attributes.insert(Pango.attr_scale_new(Pango.Scale.SMALL)); Box box = new Box(Orientation.HORIZONTAL, 10); box.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }); box.append(label); box.append(new Separator(Orientation.HORIZONTAL) { valign=Align.CENTER, hexpand=true }); Adw.Clamp clamp = new Adw.Clamp() { maximum_size = 300, tightening_threshold = 300, child = box, halign = Align.CENTER }; clamp.insert_after(this, null); notify["model"].connect(on_model_changed); } private void on_model_changed() { if (label_text_binding != null) label_text_binding.unbind(); if (model != null) { label_text_binding = model.bind_property("date-label", this, "date-label", BindingFlags.SYNC_CREATE); } else { label_text_binding = null; } } public override void dispose() { if (label_text_binding != null) label_text_binding.unbind(); label_text_binding = null; var clamp = get_first_child(); if (clamp != null) { clamp.unparent(); clamp.dispose(); } base.dispose(); } } dino-0.5.0/main/src/ui/widgets/fixed_ratio_picture.vala0000664000000000000000000000665514776241610021664 0ustar rootrootusing Gdk; using Gtk; class Dino.Ui.FixedRatioPicture : Gtk.Widget { public int min_width { get; set; default = -1; } public int max_width { get; set; default = int.MAX; } public int min_height { get; set; default = -1; } public int max_height { get; set; default = int.MAX; } public File file { get { return inner.file; } set { inner.file = value; } } public Gdk.Paintable paintable { get { return inner.paintable; } set { inner.paintable = value; } } #if GTK_4_8 && (VALA_0_56_GREATER_5 || VALA_0_58) public Gtk.ContentFit content_fit { get { return inner.content_fit; } set { inner.content_fit = value; } } #endif private Gtk.Picture inner = new Gtk.Picture(); construct { set_css_name("picture"); add_css_class("fixed-ratio"); inner.insert_after(this, null); this.notify.connect(queue_resize); } private void measure_target_size(out int width, out int height) { if (width_request != -1 && height_request != -1) { width = width_request; height = height_request; return; } width = min_width; height = min_height; if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.HORIZONTAL, -1, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.VERTICAL, width, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); height = int.max(child_nat, height); } if (height > max_height) { height = max_height; width = min_width; if (inner.should_layout()) { int child_min = 0, child_nat = 0, child_min_baseline = -1, child_nat_baseline = -1; inner.measure(Orientation.HORIZONTAL, max_height, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); width = int.max(child_nat, width); } width = int.min(width, max_width); } } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum_baseline = -1; natural_baseline = -1; int width, height; measure_target_size(out width, out height); if (orientation == Orientation.HORIZONTAL) { minimum = min_width; natural = int.max(min_width, int.min(width, max_width)); } else if (for_size == -1) { minimum = min_height; natural = int.max(min_height, int.min(height, max_height)); } else { minimum = natural = int.max(min_height, int.min(height * for_size / width, height)); } } public override void size_allocate(int width, int height, int baseline) { if (inner.should_layout()) { inner.allocate(width, height, baseline, null); } } public override SizeRequestMode get_request_mode() { return SizeRequestMode.HEIGHT_FOR_WIDTH; } public override void dispose() { inner.unparent(); base.dispose(); } } dino-0.5.0/main/src/ui/widgets/natural_size_increase.vala0000664000000000000000000000521614776241610022175 0ustar rootrootusing Gtk; public class Dino.Ui.NaturalSizeIncrease : Gtk.Widget { public uint min_natural_height { get; set; default = 0; } public uint min_natural_width { get; set; default = 0; } construct { this.notify.connect(queue_resize); } public override void compute_expand_internal(out bool hexpand, out bool vexpand) { hexpand = false; vexpand = false; Widget child = get_first_child(); while (child != null) { hexpand = hexpand || child.compute_expand(Orientation.HORIZONTAL); vexpand = vexpand || child.compute_expand(Orientation.VERTICAL); child = child.get_next_sibling(); } } public override void measure(Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { minimum = 0; if (orientation == Orientation.HORIZONTAL) { natural = (int) min_natural_width; } else { natural = (int) min_natural_height; } minimum_baseline = -1; natural_baseline = -1; Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { int child_min = 0; int child_nat = 0; int child_min_baseline = -1; int child_nat_baseline = -1; child.measure(orientation, for_size, out child_min, out child_nat, out child_min_baseline, out child_nat_baseline); minimum = int.max(minimum, child_min); natural = int.max(natural, child_nat); if (child_min_baseline > 0) { minimum_baseline = int.max(minimum_baseline, child_min_baseline); } if (child_nat_baseline > 0) { natural_baseline = int.max(natural_baseline, child_nat_baseline); } } child = child.get_next_sibling(); } } public override void size_allocate(int width, int height, int baseline) { Widget child = get_first_child(); while (child != null) { if (child.should_layout()) { child.allocate(width, height, baseline, null); } child = child.get_next_sibling(); } } public override SizeRequestMode get_request_mode() { Widget child = get_first_child(); if (child != null) { return child.get_request_mode(); } return SizeRequestMode.CONSTANT_SIZE; } public override void dispose() { var child = this.get_first_child(); if (child != null) child.unparent(); } }dino-0.5.0/main/src/view_model/0000775000000000000000000000000014776241610015022 5ustar rootrootdino-0.5.0/main/src/view_model/account_details.vala0000664000000000000000000000172514776241610021035 0ustar rootrootusing Dino; using Dino.Entities; using Xmpp; using Xmpp.Xep; public class Dino.Ui.ViewModel.AccountDetails : Object { public Entities.Account account { get; set; } public string bare_jid { owned get { return account.bare_jid.to_string(); } } public CompatAvatarPictureModel avatar_model { get; set; } public ConnectionManager.ConnectionState connection_state { get; set; } public ConnectionManager.ConnectionError? connection_error { get; set; } public AccountDetails(Account account, StreamInteractor stream_interactor) { var account_conv = new Conversation(account.bare_jid, account, Conversation.Type.CHAT); this.account = account; this.avatar_model = new ViewModel.CompatAvatarPictureModel(stream_interactor).set_conversation(account_conv); this.connection_state = stream_interactor.connection_manager.get_state(account); this.connection_error = stream_interactor.connection_manager.get_error(account); } }dino-0.5.0/main/src/view_model/conversation_details.vala0000664000000000000000000001160614776241610022112 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; public class Dino.Ui.ViewModel.ConversationDetails : Object { public signal void pin_changed(); public signal void block_changed(BlockState action); public signal void notification_flipped(); public signal void notification_changed(NotificationSetting setting); public enum BlockState { USER, DOMAIN, UNBLOCK } public enum NotificationOptions { ON_OFF, ON_HIGHLIGHT_OFF } public enum NotificationSetting { DEFAULT, ON, HIGHLIGHT, OFF } public ViewModel.CompatAvatarPictureModel avatar { get; set; } public string name { get; set; } public bool pinned { get; set; } public NotificationSetting notification { get; set; } public NotificationOptions notification_options { get; set; } public bool notification_is_default { get; set; } public bool show_blocked { get; set; } public BlockState blocked { get; set; } public GLib.ListStore about_rows = new GLib.ListStore(typeof(PreferencesRow.Any)); public GLib.ListStore settings_rows = new GLib.ListStore(typeof(PreferencesRow.Any)); public GLib.ListStore room_configuration_rows { get; set; } public MapListModel members = new MapListModel(null, null); public SortListModel members_sorted = new SortListModel(null, new MucMemberSorter()); construct { members = new MapListModel(members_sorted, null); } } public class MucMemberSorter : Sorter { public override Gtk.Ordering compare(GLib.Object? item1, GLib.Object? item2) { var member_list_row1 = (Dino.Ui.Model.ConferenceMember) item1; var member_list_row2 = (Dino.Ui.Model.ConferenceMember) item2; var test = new Xmpp.Xep.Muc.Affiliation[] { OWNER, ADMIN, MEMBER }; var affiliation_ordering = new ArrayList.wrap(test); var affiliation_sorting = affiliation_ordering.index_of(member_list_row1.affiliation) - affiliation_ordering.index_of(member_list_row2.affiliation); if (affiliation_sorting == 0) { return Ordering.from_cmpfunc(member_list_row1.name.collate(member_list_row2.name)); } return Ordering.from_cmpfunc(affiliation_sorting); } public override Gtk.SorterOrder get_order() { return SorterOrder.TOTAL; } } //public class Dino.Ui.ViewModel.ConferenceDetails : Dino.Ui.ViewModel.ConversationDetails { // public static //} public class Dino.Ui.Model.ConversationDetails : Object { public Conversation conversation { get; set; } public Dino.Model.ConversationDisplayName display_name { get; set; } public DataForms.DataForm? data_form { get; set; } public string? data_form_bak; public bool blocked { get; set; } public bool domain_blocked { get; set; } public GLib.ListStore members = new GLib.ListStore(typeof(Ui.Model.ConferenceMember)); public void populate(StreamInteractor stream_interactor, Conversation conversation) { Ui.ConversationDetails.populate_dialog(this, conversation, stream_interactor); if (conversation.type_ == GROUPCHAT) { Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account); if (occupants != null) { foreach (Jid occupant in occupants) { var affiliation = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, occupant, conversation.account); members.append(new Dino.Ui.Model.ConferenceMember() { name = occupant.to_string(), jid = occupant, affiliation = affiliation }); } } } } } public class Dino.Ui.Model.ConferenceMember : Object { public string name { get; set; } public Jid jid { get; set; } public Xmpp.Xep.Muc.Affiliation affiliation { get; set; } } public class Dino.Ui.ViewModel.ConferenceMemberListRow : Object { public ViewModel.CompatAvatarPictureModel avatar { get; set; } public string name { get; set; } public string jid { get; set; } public Xmpp.Xep.Muc.Affiliation affiliation { get; set; } public string? affiliation_str { get; set; } construct { this.bind_property("affiliation", this, "affiliation-str", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL, (_, from_value, ref to_value) => { to_value = affiliation_to_str((Xmpp.Xep.Muc.Affiliation) from_value); return true; }); } private string? affiliation_to_str(Xmpp.Xep.Muc.Affiliation affiliation) { switch (affiliation) { case OWNER: return _("Owner"); case ADMIN: return _("Admin"); case MEMBER: return _("Member"); default: return null; } } }dino-0.5.0/main/src/view_model/preferences_row.vala0000664000000000000000000000155714776241610021067 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; namespace Dino.Ui.ViewModel.PreferencesRow { public abstract class Any : Object { public string title { get; set; } } public class Text : Any { public string text { get; set; } } public class Entry : Any { public signal void changed(); public string text { get; set; } } public class PrivateText : Any { public signal void changed(); public string text { get; set; } } public class Toggle : Any { public string subtitle { get; set; } public bool state { get; set; } } public class ComboBox : Any { public Gee.List items = new ArrayList(); public int active_item { get; set; } } public class WidgetDeprecated : Any { public Widget widget; } }dino-0.5.0/main/src/view_model/preferences_window.vala0000664000000000000000000001226314776241610021563 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; public class Dino.Ui.ViewModel.PreferencesWindow : Object { public signal void update(); public HashMap account_details = new HashMap(Account.hash_func, Account.equals_func); public AccountDetails selected_account { get; set; } public Gtk.SingleSelection active_accounts_selection { get; default=new Gtk.SingleSelection(new GLib.ListStore(typeof(ViewModel.AccountDetails))); } public StreamInteractor stream_interactor; public Database db; public GeneralPreferencesPage general_page { get; set; default=new GeneralPreferencesPage(); } public void populate(Database db, StreamInteractor stream_interactor) { this.db = db; this.stream_interactor = stream_interactor; stream_interactor.connection_manager.connection_error.connect((account, error) => { var account_detail = account_details[account]; if (account_details != null) { account_detail.connection_error = error; } }); stream_interactor.connection_manager.connection_state_changed.connect((account, state) => { var account_detail = account_details[account]; if (account_details != null) { account_detail.connection_state = state; account_detail.connection_error = stream_interactor.connection_manager.get_error(account); } }); stream_interactor.account_added.connect(update_data); stream_interactor.account_removed.connect(update_data); bind_general_page(); update_data(); } private void update_data() { // account_details should hold the correct set of accounts (add or remove some if needed), but do not override remaining ones (would destroy bindings) var current_accounts = db.get_accounts(); var remove_accounts = new ArrayList(); foreach (var account in account_details.keys) { if (!current_accounts.contains(account)) remove_accounts.add(account); } foreach (var account in remove_accounts) { account_details.unset(account); } foreach (var account in current_accounts) { if (!account_details.has_key(account)) { account_details[account] = new AccountDetails(account, stream_interactor); } if (selected_account == null && account.enabled) selected_account = account_details[account]; } // Update account picker model with currently active accounts var list_model = (GLib.ListStore) active_accounts_selection.model; list_model.remove_all(); foreach (var account in stream_interactor.get_accounts()) { list_model.append(new ViewModel.AccountDetails(account, stream_interactor)); } update(); } public void set_avatar_uri(Account account, string uri) { stream_interactor.get_module(AvatarManager.IDENTITY).publish(account, uri); } public void remove_avatar(Account account) { stream_interactor.get_module(AvatarManager.IDENTITY).unset_avatar(account); } public void remove_account(Account account) { stream_interactor.disconnect_account.begin(account, () => { account.remove(); update_data(); }); } public void reconnect_account(Account account) { stream_interactor.disconnect_account.begin(account, () => { stream_interactor.connect_account(account); }); } public void enable_disable_account(Account account) { if (account.enabled) { account.enabled = false; stream_interactor.disconnect_account.begin(account); } else { account.enabled = true; stream_interactor.connect_account(account); } update_data(); } public ChangePasswordDialog get_change_password_dialog_model() { return new ChangePasswordDialog() { account = selected_account.account, stream_interactor = stream_interactor }; } private void bind_general_page() { var settings = Dino.Application.get_default().settings; settings.bind_property("send-typing", general_page, "send-typing", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); settings.bind_property("send-marker", general_page, "send-marker", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); settings.bind_property("notifications", general_page, "notifications", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); settings.bind_property("convert-utf8-smileys", general_page, "convert-emojis", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL); } } public class Dino.Ui.ViewModel.ChangePasswordDialog : Object { public Entities.Account account { get; set; } public StreamInteractor stream_interactor { get; set; } public async string? change_password(string new_password) { var res = yield stream_interactor.get_module(Register.IDENTITY).change_password(account, new_password); if (res == null) { account.password = new_password; } return res; } } dino-0.5.0/main/src/windows/0000775000000000000000000000000014776241610014362 5ustar rootrootdino-0.5.0/main/src/windows/conversation_details.vala0000664000000000000000000002620414776241610021452 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; namespace Dino.Ui.ConversationDetails { [GtkTemplate (ui = "/im/dino/Dino/conversation_details.ui")] public class Dialog : Adw.Window { [GtkChild] public unowned Stack stack; [GtkChild] public unowned Box about_box; [GtkChild] public unowned Button pin_button; [GtkChild] public unowned Adw.ButtonContent pin_button_content; [GtkChild] public unowned MenuButton block_button; [GtkChild] public unowned Adw.ButtonContent block_button_content; [GtkChild] public unowned Button notification_button_toggle; [GtkChild] public unowned Adw.ButtonContent notification_button_toggle_content; [GtkChild] public unowned MenuButton notification_button_menu; [GtkChild] public unowned Adw.ButtonContent notification_button_menu_content; [GtkChild] public unowned Adw.SplitButton notification_button_split; [GtkChild] public unowned Adw.ButtonContent notification_button_split_content; [GtkChild] public unowned ViewModel.ConversationDetails model { get; } public StackPage? encryption_stack_page = null; public Box? encryption_box = null; public StackPage? member_stack_page = null; public Box? member_box = null; private SimpleAction block_action = new SimpleAction.stateful("block", VariantType.INT32, new Variant.int32(ViewModel.ConversationDetails.BlockState.UNBLOCK)); class construct { install_action("notification.on", null, (widget, action_name) => { ((Dialog) widget).model.notification_changed(ViewModel.ConversationDetails.NotificationSetting.ON); } ); install_action("notification.off", null, (widget, action_name) => { ((Dialog) widget).model.notification_changed(ViewModel.ConversationDetails.NotificationSetting.OFF); } ); install_action("notification.highlight", null, (widget, action_name) => { ((Dialog) widget).model.notification_changed(ViewModel.ConversationDetails.NotificationSetting.HIGHLIGHT); } ); install_action("notification.default", null, (widget, action_name) => { ((Dialog) widget).model.notification_changed(ViewModel.ConversationDetails.NotificationSetting.DEFAULT); } ); } construct { pin_button.clicked.connect(() => { model.pin_changed(); }); notification_button_toggle.clicked.connect(() => { model.notification_flipped(); }); notification_button_split.clicked.connect(() => { model.notification_flipped(); }); model.notify["pinned"].connect(update_pinned_button); model.notify["blocked"].connect(update_blocked_button); model.notify["notification"].connect(update_notification_button); model.notify["notification"].connect(update_notification_button_state); model.notify["notification-options"].connect(update_notification_button_visibility); model.notify["notification-is-default"].connect(update_notification_button_visibility); model.about_rows.items_changed.connect(create_preferences_rows); model.settings_rows.items_changed.connect(create_preferences_rows); model.notify["room-configuration-rows"].connect(create_preferences_rows); model.notify["members"].connect(create_members); create_members(); // Create block action SimpleActionGroup block_action_group = new SimpleActionGroup(); block_action = new SimpleAction.stateful("block", VariantType.INT32, new Variant.int32(0)); block_action.activate.connect((parameter) => { block_action.set_state(parameter); model.block_changed((ViewModel.ConversationDetails.BlockState) parameter.get_int32()); }); block_action_group.insert(block_action); this.insert_action_group("block", block_action_group); // Create block menu model Menu block_menu_model = new Menu(); string[] menu_labels = new string[] { _("Block user"), _("Block entire domain"), _("Unblock") }; ViewModel.ConversationDetails.BlockState[] menu_states = new ViewModel.ConversationDetails.BlockState[] { ViewModel.ConversationDetails.BlockState.USER, ViewModel.ConversationDetails.BlockState.DOMAIN, ViewModel.ConversationDetails.BlockState.UNBLOCK }; for (int i = 0; i < menu_labels.length; i++) { MenuItem item = new MenuItem(menu_labels[i], null); item.set_action_and_target_value("block.block", new Variant.int32(menu_states[i])); block_menu_model.append_item(item); } block_button.menu_model = block_menu_model; #if Adw_1_4 // TODO: replace with putting buttons in new line on small screens notification_button_menu_content.can_shrink = true; #endif update_blocked_button(); } private void update_pinned_button() { pin_button_content.icon_name = "view-pin-symbolic"; pin_button_content.label = model.pinned ? _("Pinned") : _("Pin"); if (model.pinned) { pin_button.add_css_class("accent"); } else { pin_button.remove_css_class("accent"); } } private void update_blocked_button() { switch (model.blocked) { case USER: block_button_content.label = _("Blocked"); block_button.add_css_class("error"); break; case DOMAIN: block_button_content.label = _("Domain blocked"); block_button.add_css_class("error"); break; case UNBLOCK: block_button_content.label = _("Block"); block_button.remove_css_class("error"); break; } block_action.set_state(new Variant.int32(model.blocked)); } private void update_notification_button() { string icon_name = model.notification == OFF ? "dino-bell-large-none-symbolic" : "dino-bell-large-symbolic"; notification_button_toggle_content.icon_name = icon_name; notification_button_split_content.icon_name = icon_name; notification_button_menu_content.icon_name = icon_name; } private void update_notification_button_state() { switch (model.notification) { case ON: notification_button_toggle_content.label = _("Mute"); notification_button_split_content.label = _("Mute"); notification_button_menu_content.label = _("Notifications enabled"); break; case HIGHLIGHT: notification_button_menu_content.label = _("Notifications for mentions"); break; case OFF: notification_button_toggle_content.label = _("Muted"); notification_button_split_content.label = _("Muted"); notification_button_menu_content.label = _("Notifications disabled"); break; } } private void update_notification_button_visibility() { notification_button_toggle.visible = notification_button_menu.visible = notification_button_split.visible = false; if (model.notification_options == ON_OFF) { if (model.notification_is_default) { notification_button_toggle.visible = true; } else { notification_button_split.visible = true; } } else { notification_button_menu.visible = true; } } private void create_members() { #if GTK_4_8 && (VALA_0_56_GREATER_5 || VALA_0_58) if (model.members_sorted.n_items == 0) return; #else if (model.members_sorted.model.get_n_items() == 0) return; #endif var selection_model = new NoSelection(model.members_sorted); var item_factory = new BuilderListItemFactory.from_resource(null, "/im/dino/Dino/muc_member_list_row.ui"); var list_view = new ListView(selection_model, item_factory) { single_click_activate = true }; list_view.add_css_class("card"); list_view.activate.connect((position) => { // var widget = (Gtk.Widget) list_view.observe_children().get_item(position); // var name_label = widget.get_template_child(Type.OBJECT, "name-label"); // print(widget.get_type().name()); // var popover = new Popover(); // popover.parent = widget; // popover.popup(); var row_view_model = (Ui.Model.ConferenceMember) model.members_sorted.get_item(position); print(@"$(position) $(row_view_model.name)\n"); }); add_members_tab_element(list_view); } private void create_preferences_rows() { var widget = about_box.get_first_child(); while (widget != null) { about_box.remove(widget); widget = about_box.get_first_child(); } if (model.about_rows.get_n_items() > 0) { about_box.append(Util.rows_to_preference_group(model.about_rows, _("About"))); } if (model.settings_rows.get_n_items() > 0) { about_box.append(Util.rows_to_preference_group(model.settings_rows, _("Settings"))); } if (model.room_configuration_rows != null && model.room_configuration_rows.get_n_items() > 0) { about_box.append(Util.rows_to_preference_group(model.room_configuration_rows, _("Room Configuration"))); } } public void add_encryption_tab_element(Adw.PreferencesGroup preferences_group) { if (encryption_stack_page == null) { encryption_box = new Box(Orientation.VERTICAL, 12) { margin_end = 12, margin_start = 12, margin_top = 18, margin_bottom = 40 }; var scrolled_window = new ScrolledWindow() { vexpand = true }; var clamp = new Adw.Clamp(); clamp.set_child(encryption_box); scrolled_window.set_child(clamp); encryption_stack_page = stack.add_child(scrolled_window); encryption_stack_page.title = _("Encryption"); encryption_stack_page.name = "encryption"; } encryption_box.append(preferences_group); } public void add_members_tab_element(Widget widget) { if (member_stack_page == null) { member_box = new Box(Orientation.VERTICAL, 12) { margin_end = 12, margin_start = 12, margin_top = 18 }; member_stack_page = stack.add_child(member_box); member_stack_page.title = _("Members"); member_stack_page.name = "member"; } member_box.append(widget); } } [GtkTemplate (ui = "/im/dino/Dino/muc_member_list_row.ui")] public class Dino.Ui.ConversationDetails.MemberListItem : Gtk.Widget { } } dino-0.5.0/main/src/windows/preferences_window/0000775000000000000000000000000014776241610020252 5ustar rootrootdino-0.5.0/main/src/windows/preferences_window/account_preferences_subpage.vala0000664000000000000000000003022314776241610026642 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; using Gdk; [GtkTemplate (ui = "/im/dino/Dino/preferences_window/account_preferences_subpage.ui")] public class Dino.Ui.AccountPreferencesSubpage : Gtk.Box { [GtkChild] public unowned Adw.HeaderBar headerbar; [GtkChild] public unowned Button back_button; [GtkChild] public unowned AvatarPicture avatar; [GtkChild] public unowned Adw.ActionRow xmpp_address; [GtkChild] public unowned Adw.EntryRow local_alias; [GtkChild] public unowned Adw.ActionRow password_change; [GtkChild] public unowned Adw.ActionRow connection_status; [GtkChild] public unowned Button enter_password_button; [GtkChild] public unowned Box avatar_menu_box; [GtkChild] public unowned Button edit_avatar_button; [GtkChild] public unowned Button remove_avatar_button; [GtkChild] public unowned Widget button_container; [GtkChild] public unowned Button remove_account_button; [GtkChild] public unowned Button disable_account_button; public Account account { get { return model.selected_account.account; } } public ViewModel.PreferencesWindow model { get; set; } private Binding[] bindings = new Binding[0]; private ulong[] account_notify_ids = new ulong[0]; private ulong alias_entry_changed = 0; construct { #if Adw_1_4 headerbar.show_title = false; #endif button_container.layout_manager = new NaturalDirectionBoxLayout((BoxLayout)button_container.layout_manager); back_button.clicked.connect(() => { var window = (Adw.PreferencesWindow) this.get_root(); window.close_subpage(); }); edit_avatar_button.clicked.connect(() => { show_select_avatar(); }); remove_avatar_button.clicked.connect(() => { model.remove_avatar(account); }); disable_account_button.clicked.connect(() => { model.enable_disable_account(account); }); remove_account_button.clicked.connect(() => { show_remove_account_dialog(); }); password_change.activatable_widget = new Label(""); password_change.activated.connect(() => { var dialog = new ChangePasswordDialog(model.get_change_password_dialog_model()); dialog.set_transient_for((Gtk.Window)this.get_root()); dialog.present(); }); enter_password_button.clicked.connect(() => { var dialog = new Adw.MessageDialog((Window)this.get_root(), "Enter password for %s".printf(account.bare_jid.to_string()), null); var password = new PasswordEntry() { show_peek_icon=true }; dialog.response.connect((response) => { if (response == "connect") { account.password = password.text; model.reconnect_account(account); } }); dialog.set_default_response("connect"); dialog.set_extra_child(password); dialog.add_response("cancel", _("Cancel")); dialog.add_response("connect", _("Connect")); dialog.present(); }); this.notify["model"].connect(() => { model.notify["selected-account"].connect(() => { foreach (var binding in bindings) { binding.unbind(); } avatar.model = model.selected_account.avatar_model; xmpp_address.subtitle = account.bare_jid.to_string(); if (alias_entry_changed != 0) local_alias.disconnect(alias_entry_changed); local_alias.text = account.alias ?? ""; alias_entry_changed = local_alias.changed.connect(() => { account.alias = local_alias.text; }); bindings += account.bind_property("enabled", disable_account_button, "label", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { bool enabled_bool = (bool) from; to = enabled_bool ? _("Disable account") : _("Enable account"); return true; }); bindings += account.bind_property("enabled", avatar_menu_box, "visible", BindingFlags.SYNC_CREATE); bindings += account.bind_property("enabled", password_change, "visible", BindingFlags.SYNC_CREATE); bindings += account.bind_property("enabled", connection_status, "visible", BindingFlags.SYNC_CREATE); bindings += model.selected_account.bind_property("connection-state", connection_status, "subtitle", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { to = get_status_label(); return true; }); bindings += model.selected_account.bind_property("connection-error", connection_status, "subtitle", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { to = get_status_label(); return true; }); bindings += model.selected_account.bind_property("connection-error", enter_password_button, "visible", BindingFlags.SYNC_CREATE, (binding, from, ref to) => { var error = (ConnectionManager.ConnectionError) from; to = error != null && error.source == ConnectionManager.ConnectionError.Source.SASL; return true; }); // Only show avatar removal button if an avatar is set var avatar_model = model.selected_account.avatar_model.tiles.get_item(0) as ViewModel.AvatarPictureTileModel; avatar_model.notify["image-file"].connect(() => { remove_avatar_button.visible = avatar_model.image_file != null; }); remove_avatar_button.visible = avatar_model.image_file != null; model.selected_account.notify["connection-error"].connect(() => { if (model.selected_account.connection_error != null) { connection_status.add_css_class("error"); } else { connection_status.remove_css_class("error"); } }); if (model.selected_account.connection_error != null) { connection_status.add_css_class("error"); } else { connection_status.remove_css_class("error"); } }); }); } private void show_select_avatar() { FileChooserNative chooser = new FileChooserNative(_("Select avatar"), (Window)this.get_root(), FileChooserAction.OPEN, _("Select"), _("Cancel")); FileFilter filter = new FileFilter(); foreach (PixbufFormat pixbuf_format in Pixbuf.get_formats()) { foreach (string mime_type in pixbuf_format.get_mime_types()) { filter.add_mime_type(mime_type); } } filter.set_filter_name(_("Images")); chooser.add_filter(filter); filter = new FileFilter(); filter.set_filter_name(_("All files")); filter.add_pattern("*"); chooser.add_filter(filter); chooser.response.connect(() => { string uri = chooser.get_file().get_path(); model.set_avatar_uri(account, uri); }); chooser.show(); } private void show_remove_account_dialog() { Adw.MessageDialog dialog = new Adw.MessageDialog ( (Window)this.get_root(), _("Remove account %s?".printf(account.bare_jid.to_string())), "You won't be able to access your conversation history anymore." ); // TODO remove history! dialog.add_response("cancel", "Cancel"); dialog.add_response("remove", "Remove"); dialog.set_response_appearance("remove", Adw.ResponseAppearance.DESTRUCTIVE); dialog.response.connect((response) => { if (response == "remove") { model.remove_account(account); // Close the account subpage var window = (Adw.PreferencesWindow) this.get_root(); window.close_subpage(); // window.pop_subpage(); } dialog.close(); }); dialog.present(); } private string get_status_label() { string? error_label = get_connection_error_description(); if (error_label != null) return error_label; ConnectionManager.ConnectionState state = model.selected_account.connection_state; switch (state) { case ConnectionManager.ConnectionState.CONNECTING: return _("Connecting…"); case ConnectionManager.ConnectionState.CONNECTED: return _("Connected"); case ConnectionManager.ConnectionState.DISCONNECTED: return _("Disconnected"); } assert_not_reached(); } private string? get_connection_error_description() { ConnectionManager.ConnectionError? error = model.selected_account.connection_error; if (error == null) return null; switch (error.source) { case ConnectionManager.ConnectionError.Source.SASL: return _("Wrong password"); case ConnectionManager.ConnectionError.Source.TLS: return _("Invalid TLS certificate"); } if (error.identifier != null) { return _("Error") + ": " + error.identifier; } else { return _("Error"); } } } public class Dino.Ui.NaturalDirectionBoxLayout : LayoutManager { private BoxLayout original; private BoxLayout alternative; public NaturalDirectionBoxLayout(BoxLayout original) { this.original = original; if (original.orientation == Orientation.HORIZONTAL) { this.alternative = new BoxLayout(Orientation.VERTICAL); this.alternative.spacing = this.original.spacing / 2; } } public override SizeRequestMode get_request_mode(Widget widget) { return original.orientation == Orientation.HORIZONTAL ? SizeRequestMode.HEIGHT_FOR_WIDTH : SizeRequestMode.WIDTH_FOR_HEIGHT; } public override void allocate(Widget widget, int width, int height, int baseline) { int blind_minimum, blind_natural, blind_minimum_baseline, blind_natural_baseline; original.measure(widget, original.orientation, -1, out blind_minimum, out blind_natural, out blind_minimum_baseline, out blind_natural_baseline); int for_size = (original.orientation == Orientation.HORIZONTAL ? width : height); if (for_size >= blind_minimum) { original.allocate(widget, width, height, baseline); } else { alternative.allocate(widget, width, height, baseline); } } public override void measure(Widget widget, Orientation orientation, int for_size, out int minimum, out int natural, out int minimum_baseline, out int natural_baseline) { if (for_size == -1) { original.measure(widget, orientation, -1, out minimum, out natural, out minimum_baseline, out natural_baseline); int alt_minimum, alt_natural, alt_minimum_baseline, alt_natural_baseline; alternative.measure(widget, orientation, -1, out alt_minimum, out alt_natural, out alt_minimum_baseline, out alt_natural_baseline); if (alt_minimum < minimum && alt_minimum != -1) minimum = alt_minimum; if (alt_minimum_baseline < minimum_baseline && alt_minimum_baseline != -1) minimum = alt_minimum_baseline; } else { Orientation other_orientation = orientation == Orientation.HORIZONTAL ? Orientation.VERTICAL : Orientation.HORIZONTAL; int blind_minimum, blind_natural, blind_minimum_baseline, blind_natural_baseline; original.measure(widget, other_orientation, -1, out blind_minimum, out blind_natural, out blind_minimum_baseline, out blind_natural_baseline); if (for_size >= blind_minimum) { original.measure(widget, orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); } else { alternative.measure(widget, orientation, for_size, out minimum, out natural, out minimum_baseline, out natural_baseline); } } } }dino-0.5.0/main/src/windows/preferences_window/accounts_preferences_page.vala0000664000000000000000000000555414776241610026324 0ustar rootrootusing Dino.Entities; using Gee; using Gtk; public class Dino.Ui.PreferencesWindowAccounts : Adw.PreferencesPage { public signal void account_chosen(Account account); public Adw.PreferencesGroup active_accounts; public Adw.PreferencesGroup disabled_accounts; public ViewModel.PreferencesWindow model { get; set; } construct { this.title = _("Accounts"); this.icon_name = "system-users-symbolic"; this.notify["model"].connect(() => { model.update.connect(refresh); }); } private void refresh() { if (active_accounts != null) this.remove(active_accounts); if (disabled_accounts != null) this.remove(disabled_accounts); active_accounts = new Adw.PreferencesGroup() { title=_("Accounts")}; disabled_accounts = new Adw.PreferencesGroup() { title=_("Disabled accounts")}; Button add_account_button = new Button.from_icon_name("list-add-symbolic"); add_account_button.add_css_class("flat"); add_account_button.tooltip_text = _("Add Account"); active_accounts.header_suffix = add_account_button; this.add(active_accounts); this.add(disabled_accounts); add_account_button.clicked.connect(() => { Ui.ManageAccounts.AddAccountDialog add_account_dialog = new Ui.ManageAccounts.AddAccountDialog(model.stream_interactor, model.db); add_account_dialog.set_transient_for((Window)this.get_root()); add_account_dialog.added.connect((account) => { refresh(); }); add_account_dialog.present(); }); disabled_accounts.visible = false; // Only display disabled section if it contains accounts var enabled_account_added = false; foreach (ViewModel.AccountDetails account_details in model.account_details.values) { var row = new Adw.ActionRow() { title = account_details.bare_jid.to_string() }; row.add_prefix(new AvatarPicture() { valign=Align.CENTER, height_request=35, width_request=35, model = account_details.avatar_model }); row.add_suffix(new Image.from_icon_name("go-next-symbolic")); row.activatable = true; if (account_details.account.enabled) { active_accounts.add(row); enabled_account_added = true; } else { disabled_accounts.add(row); disabled_accounts.visible = true; } row.activated.connect(() => { account_chosen(account_details.account); }); } // We always have to show the active accounts group for the add new button. Display placeholder if there are no active accounts if (!enabled_account_added) { active_accounts.add(new Adw.ActionRow() { title=_("No active accounts") }); } } } dino-0.5.0/main/src/windows/preferences_window/add_account_dialog.vala0000664000000000000000000003773514776241610024721 0ustar rootrootusing Gee; using Gtk; using Pango; using Dino.Entities; using Xmpp; namespace Dino.Ui.ManageAccounts { [GtkTemplate (ui = "/im/dino/Dino/preferences_window/add_account_dialog.ui")] public class AddAccountDialog : Adw.Window { public signal void added(Account account); enum Page { SIGN_IN, SIGN_IN_TLS_ERROR, CREATE_ACCOUNT_SELECT_SERVER, CREATE_ACCOUNT_REGISTER_FORM, SUCCESS } [GtkChild] private unowned Stack stack; [GtkChild] private unowned Revealer notification_revealer; [GtkChild] private unowned Label notification_label; // Sign in - JID [GtkChild] private unowned Box sign_in_box; [GtkChild] private unowned Label sign_in_error_label; [GtkChild] private unowned Adw.EntryRow jid_entry; [GtkChild] private unowned Adw.PreferencesGroup password_group; [GtkChild] private unowned Adw.PasswordEntryRow password_entry; [GtkChild] private unowned Button sign_in_continue_button; [GtkChild] private unowned Spinner sign_in_continue_spinner; [GtkChild] private unowned Button sign_in_serverlist_button; // Sign in - TLS error [GtkChild] private unowned Box sign_in_tls_box; [GtkChild] private unowned Label sign_in_tls_label; [GtkChild] private unowned Button sign_in_tls_back_button; // Select Server [GtkChild] private unowned Box create_account_box; [GtkChild] private unowned Button login_button; [GtkChild] private unowned Spinner select_server_continue_spinner; [GtkChild] private unowned Button select_server_continue; [GtkChild] private unowned Label register_form_continue_label; [GtkChild] private unowned ListBox server_list_box; [GtkChild] private unowned Entry server_entry; // Register Form [GtkChild] private unowned Button back_button; [GtkChild] private unowned Box register_box; [GtkChild] private unowned Box form_box; [GtkChild] private unowned Spinner register_form_continue_spinner; [GtkChild] private unowned Button register_form_continue; // Success [GtkChild] private unowned Box success_box; [GtkChild] private unowned Label success_description; [GtkChild] private unowned Button success_continue_button; private static string[] server_list = new string[]{ "5222.de", "jabber.fr", "movim.eu", "yax.im" }; private StreamInteractor stream_interactor; private Database db; private HashMap list_box_jids = new HashMap(); private Jid? server_jid = null; private Jid? login_jid = null; private Xep.InBandRegistration.Form? form = null; public AddAccountDialog(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.title = _("Add Account"); // Sign in - Jid jid_entry.changed.connect(on_jid_entry_changed); sign_in_continue_button.clicked.connect(on_sign_in_continue_button_clicked); sign_in_serverlist_button.clicked.connect(show_select_server); // Sign in - TLS error sign_in_tls_back_button.clicked.connect(() => show_sign_in() ); // Select Server server_entry.changed.connect(() => { try { Jid jid = new Jid(server_entry.text); select_server_continue.sensitive = jid != null && jid.localpart == null && jid.resourcepart == null; } catch (InvalidJidError e) { select_server_continue.sensitive = false; } }); select_server_continue.clicked.connect(on_select_server_continue); login_button.clicked.connect(() => show_sign_in() ); foreach (string server in server_list) { ListBoxRow list_box_row = new ListBoxRow(); list_box_row.set_child(new Label(server) { xalign=0, margin_start=7, margin_end=7 }); list_box_jids[list_box_row] = server; server_list_box.append(list_box_row); } // Register Form register_form_continue.clicked.connect(on_register_form_continue_clicked); back_button.clicked.connect(() => { show_select_server(); back_button.visible = false; }); // Success success_continue_button.clicked.connect(() => close()); show_sign_in(); } private void show_sign_in(bool keep_jid = false) { switch_stack_page(Page.SIGN_IN); this.title = _("Sign in"); set_default_widget(sign_in_continue_button); sign_in_error_label.visible = false; sign_in_continue_spinner.visible = false; if (!keep_jid) { jid_entry.text = ""; jid_entry.grab_focus(); } password_entry.text = ""; password_group.visible = false; sign_in_serverlist_button.visible = true; } private void show_tls_error(string domain, TlsCertificateFlags error_flags) { switch_stack_page(Page.SIGN_IN_TLS_ERROR); string error_desc = _("The server could not prove that it is %s.").printf("" + domain + ""); if (TlsCertificateFlags.UNKNOWN_CA in error_flags) { error_desc += " " + _("Its security certificate is not trusted by your operating system."); } else if (TlsCertificateFlags.BAD_IDENTITY in error_flags) { error_desc += " " + _("Its security certificate is issued to another domain."); } else if (TlsCertificateFlags.NOT_ACTIVATED in error_flags) { error_desc += " " + _("Its security certificate will only become valid in the future."); } else if (TlsCertificateFlags.EXPIRED in error_flags) { error_desc += " " + _("Its security certificate is expired."); } sign_in_tls_label.label = error_desc; } private void show_select_server() { switch_stack_page(Page.CREATE_ACCOUNT_SELECT_SERVER); this.title = _("Create account"); server_entry.text = ""; server_entry.grab_focus(); set_default_widget(select_server_continue); server_list_box.row_activated.disconnect(on_server_list_row_activated); server_list_box.unselect_all(); server_list_box.row_activated.connect(on_server_list_row_activated); } private void show_register_form() { switch_stack_page(Page.CREATE_ACCOUNT_REGISTER_FORM); set_default_widget(register_form_continue); } private void show_success(Account account) { switch_stack_page(Page.SUCCESS); success_description.label = _("You can now use the account %s.").printf("" + Markup.escape_text(account.bare_jid.to_string()) + ""); set_default_widget(success_continue_button); } private void on_jid_entry_changed() { try { login_jid = new Jid(jid_entry.text); if (login_jid.localpart != null && login_jid.resourcepart == null) { sign_in_continue_button.sensitive = true; } else { sign_in_continue_button.sensitive = false; } } catch (InvalidJidError e) { sign_in_continue_button.sensitive = false; } } private async void on_sign_in_continue_button_clicked() { try { login_jid = new Jid(jid_entry.text); sign_in_tls_label.label = ""; sign_in_error_label.visible = false; sign_in_continue_button.sensitive = false; sign_in_continue_spinner.visible = true; ulong jid_entry_changed_handler_id = -1; jid_entry_changed_handler_id = jid_entry.changed.connect(() => { jid_entry.disconnect(jid_entry_changed_handler_id); show_sign_in(true); return; }); if (password_group.visible) { // JID + Psw fields were visible: Try to log in string password = password_entry.text; Account account = new Account(login_jid, password); ConnectionManager.ConnectionError.Source? error = yield stream_interactor.get_module(Register.IDENTITY).add_check_account(account); sign_in_continue_spinner.visible = false; sign_in_continue_button.sensitive = true; if (error != null) { sign_in_error_label.visible = true; switch (error) { case ConnectionManager.ConnectionError.Source.SASL: sign_in_error_label.label = _("Wrong username or password"); break; default: sign_in_error_label.label = _("Something went wrong"); break; } } else { add_activate_account(account); show_success(account); } } else { // Only JID field was visible: Check if server exists Register.ServerAvailabilityReturn server_status = yield Register.check_server_availability(login_jid); sign_in_continue_spinner.visible = false; sign_in_continue_button.sensitive = true; if (server_status.available) { password_group.visible = true; password_entry.grab_focus(); sign_in_serverlist_button.visible = false; } else { if (server_status.error_flags != null) { show_tls_error(login_jid.domainpart, server_status.error_flags); } else { sign_in_error_label.visible = true; sign_in_error_label.label = _("Could not connect to %s").printf(login_jid.domainpart); } } } } catch (InvalidJidError e) { warning("Invalid address from interface allowed login: %s", e.message); sign_in_error_label.visible = true; sign_in_error_label.label = _("Invalid address"); } } private void on_select_server_continue() { try { server_jid = new Jid(server_entry.text); request_show_register_form.begin(server_jid); } catch (InvalidJidError e) { warning("Invalid address from interface allowed server: %s", e.message); display_notification(_("Invalid address")); } } private void on_server_list_row_activated(ListBox box, ListBoxRow row) { try { server_jid = new Jid(list_box_jids[row]); request_show_register_form.begin(server_jid); } catch (InvalidJidError e) { warning("Invalid address from selected server: %s", e.message); display_notification(_("Invalid address")); } } private async void request_show_register_form(Jid server_jid) { select_server_continue_spinner.visible = true; Register.RegistrationFormReturn form_return = yield Register.get_registration_form(server_jid); if (select_server_continue_spinner == null) { return; } select_server_continue_spinner.visible = false; if (form_return.form != null) { form = form_return.form; set_register_form(server_jid, form); show_register_form(); } else if (form_return.error_flags != null) { show_tls_error(server_jid.domainpart, form_return.error_flags); } else { display_notification(_("No response from server")); } } private void set_register_form(Jid server, Xep.InBandRegistration.Form form) { Widget widget = form_box.get_first_child(); while (widget != null) { form_box.remove(widget); widget = form_box.get_first_child(); } this.title = _("Register on %s").printf(server.to_string()); if (form.oob != null) { form_box.append(new Label(_("The server requires to sign up through a website"))); form_box.append(new Label(@"$(form.oob)") { use_markup=true }); register_form_continue_label.label = _("Open website"); register_form_continue.visible = true; register_form_continue.grab_focus(); } else if (form.fields.size > 0) { if (form.instructions != null && form.instructions != "") { string markup_instructions = Util.parse_add_markup(Util.unbreak_space_around_non_spacing_mark(form.instructions), null, true, false); form_box.append(new Label(markup_instructions) { use_markup=true, xalign=0, margin_top=7, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR }); } var form_preference_group = Util.rows_to_preference_group(Util.get_data_form_view_model(form), ""); form_box.append(form_preference_group); register_form_continue.visible = true; register_form_continue_label.label = _("Register"); } else { form_box.append(new Label(_("Check %s for information on how to sign up").printf(@"$(server)")) { use_markup=true }); register_form_continue.visible = false; } } private async void on_register_form_continue_clicked() { notification_revealer.set_reveal_child(false); // Button is opening a registration website if (form.oob != null) { try { AppInfo.launch_default_for_uri(form.oob, null); } catch (Error e) { } show_sign_in(); return; } register_form_continue_spinner.visible = true; string? error = yield Register.submit_form(server_jid, form); if (register_form_continue_spinner == null) { return; } register_form_continue_spinner.visible = false; if (error == null) { string? username = null, password = null; foreach (Xep.DataForms.DataForm.Field field in form.fields) { switch (field.var) { case "username": username = field.get_value_string(); break; case "password": password = field.get_value_string(); break; } } try { Account account = new Account(new Jid.components(username, server_jid.domainpart, null), password); add_activate_account(account); show_success(account); } catch (InvalidJidError e) { warning("Invalid address from components of registration: %s", e.message); display_notification(_("Invalid address")); } } else { display_notification(error); } } private void display_notification(string text) { notification_label.label = text; notification_revealer.set_reveal_child(true); Timeout.add_seconds(5, () => { notification_revealer.set_reveal_child(false); return false; }); } private void add_activate_account(Account account) { account.enabled = true; account.persist(db); stream_interactor.connect_account(account); added(account); } private void switch_stack_page(Page page) { sign_in_box.visible = page == SIGN_IN; sign_in_tls_box.visible = page == SIGN_IN_TLS_ERROR; create_account_box.visible = page == CREATE_ACCOUNT_SELECT_SERVER; register_box.visible = page == CREATE_ACCOUNT_REGISTER_FORM; success_box.visible = page == SUCCESS; stack.visible_child_name = get_visible_stack_child_name(page); back_button.visible = page == CREATE_ACCOUNT_REGISTER_FORM; } private string get_visible_stack_child_name(Page page) { switch (page) { case SIGN_IN: return "login_jid"; case SIGN_IN_TLS_ERROR: return "tls_error"; case CREATE_ACCOUNT_SELECT_SERVER: return "server"; case CREATE_ACCOUNT_REGISTER_FORM: return "form"; case SUCCESS: return "success"; default: assert_not_reached(); } } } } dino-0.5.0/main/src/windows/preferences_window/change_password_dialog.vala0000664000000000000000000000525114776241610025610 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Ui{ [GtkTemplate (ui = "/im/dino/Dino/preferences_window/change_password_dialog.ui")] public class ChangePasswordDialog : Gtk.Dialog { [GtkChild] private unowned Button change_password_button; [GtkChild] private unowned Stack change_password_stack; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Adw.PasswordEntryRow current_password_entry; [GtkChild] private unowned Adw.PasswordEntryRow new_password_entry; [GtkChild] private unowned Adw.PasswordEntryRow confirm_new_password_entry; [GtkChild] private unowned Label change_password_error_label; private ViewModel.ChangePasswordDialog model; public ChangePasswordDialog(ViewModel.ChangePasswordDialog model) { Object(use_header_bar : 1); this.model = model; Util.force_error_color(change_password_error_label); cancel_button.clicked.connect(() => { close(); }); current_password_entry.changed.connect(is_form_filled); new_password_entry.changed.connect(is_form_filled); confirm_new_password_entry.changed.connect(is_form_filled); change_password_button.clicked.connect(on_change_password_button_clicked); } private void is_form_filled(){ if (current_password_entry.get_text().length > 0 && new_password_entry.get_text().length > 0 && confirm_new_password_entry.get_text().length > 0 && new_password_entry.get_text() == confirm_new_password_entry.get_text()){ change_password_button.sensitive = true; } else { change_password_button.sensitive = false; } } private async void on_change_password_button_clicked(){ string? pw_input = current_password_entry.get_text(); string? new_pw_input = new_password_entry.get_text(); if (pw_input != null && model.account.password == pw_input){ change_password_button.sensitive = false; change_password_stack.visible_child_name = "spinner"; string? ret = yield model.change_password(new_pw_input); change_password_button.sensitive = true; change_password_stack.visible_child_name = "label"; if (ret == null) { close(); } change_password_error_label.label = "Error: %s".printf(ret); } else { change_password_error_label.label = "Wrong current password"; } } } }dino-0.5.0/main/src/windows/preferences_window/encryption_preferences_page.vala0000664000000000000000000000500214776241610026663 0ustar rootrootusing Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; public class Dino.Ui.PreferencesWindowEncryption : Adw.PreferencesPage { private DropDown drop_down = null; private Adw.PreferencesGroup accounts_group = new Adw.PreferencesGroup(); private ArrayList added_widgets = new ArrayList(); public ViewModel.PreferencesWindow model { get; set; } construct { this.add(accounts_group); this.notify["model"].connect(() => { this.model.update.connect(() => { repopulate_account_selector(); }); }); } private void repopulate_account_selector() { // Remove current selector if (drop_down != null) { accounts_group.remove(drop_down); drop_down = null; } // Don't show selector if the user has only one account (active + inactive) accounts_group.visible = model.account_details.size != 1; // Populate selector if (model.active_accounts_selection.get_n_items() > 0) { drop_down = new DropDown(model.active_accounts_selection, null) { halign=Align.CENTER }; drop_down.factory = new BuilderListItemFactory.from_resource(null, "/im/dino/Dino/account_picker_row.ui"); drop_down.notify["selected-item"].connect(() => { var account_details = (ViewModel.AccountDetails) drop_down.selected_item; if (account_details == null) return; set_account(account_details.account); }); drop_down.selected = 0; set_account(((ViewModel.AccountDetails)model.active_accounts_selection.get_item(0)).account); } else { drop_down = new DropDown.from_strings(new string[] { _("No active accounts")}) { halign=Align.CENTER }; unset_account(); } accounts_group.add(drop_down); } private void unset_account() { foreach (var widget in added_widgets) { this.remove(widget); } added_widgets.clear(); } private void set_account(Account account) { unset_account(); Application app = GLib.Application.get_default() as Application; foreach (Plugins.EncryptionPreferencesEntry e in app.plugin_registry.encryption_preferences_entries) { var widget = (Adw.PreferencesGroup) e.get_widget(account, Plugins.WidgetType.GTK4); this.add(widget); this.added_widgets.add(widget); } } }dino-0.5.0/main/src/windows/preferences_window/general_preferences_page.vala0000664000000000000000000000325614776241610026117 0ustar rootrootusing Gtk; public class Dino.Ui.ViewModel.GeneralPreferencesPage : Object { public bool send_typing { get; set; } public bool send_marker { get; set; } public bool notifications { get; set; } public bool convert_emojis { get; set; } } [GtkTemplate (ui = "/im/dino/Dino/preferences_window/general_preferences_page.ui")] public class Dino.Ui.GeneralPreferencesPage : Adw.PreferencesPage { [GtkChild] private unowned Switch typing_switch; [GtkChild] private unowned Switch marker_switch; [GtkChild] private unowned Switch notification_switch; [GtkChild] private unowned Switch emoji_switch; public ViewModel.GeneralPreferencesPage model { get; set; default = new ViewModel.GeneralPreferencesPage(); } private Binding[] model_bindings = new Binding[0]; construct { this.notify["model"].connect(on_model_changed); } private void on_model_changed() { foreach (Binding binding in model_bindings) { binding.unbind(); } if (model != null) { model_bindings = new Binding[] { model.bind_property("send-typing", typing_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL), model.bind_property("send-marker", marker_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL), model.bind_property("notifications", notification_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL), model.bind_property("convert-emojis", emoji_switch, "active", BindingFlags.SYNC_CREATE | BindingFlags.BIDIRECTIONAL) }; } else { model_bindings = new Binding[0]; } } } dino-0.5.0/main/src/windows/preferences_window/preferences_window.vala0000664000000000000000000000244314776241610025012 0ustar rootrootusing Gdk; using Dino.Entities; using Xmpp; using Xmpp.Xep; using Gee; using Gtk; [GtkTemplate (ui = "/im/dino/Dino/preferences_window/preferences_window.ui")] public class Dino.Ui.PreferencesWindow : Adw.PreferencesWindow { [GtkChild] public unowned Dino.Ui.PreferencesWindowAccounts accounts_page; [GtkChild] public unowned Dino.Ui.PreferencesWindowEncryption encryption_page; [GtkChild] public unowned Dino.Ui.GeneralPreferencesPage general_page; public Dino.Ui.AccountPreferencesSubpage account_page = new Dino.Ui.AccountPreferencesSubpage(); [GtkChild] public unowned ViewModel.PreferencesWindow model { get; } construct { this.can_navigate_back = true; // remove once we require Adw > 1.4 this.bind_property("model", accounts_page, "model", BindingFlags.SYNC_CREATE); this.bind_property("model", account_page, "model", BindingFlags.SYNC_CREATE); this.bind_property("model", encryption_page, "model", BindingFlags.SYNC_CREATE); accounts_page.account_chosen.connect((account) => { model.selected_account = model.account_details[account]; this.present_subpage(account_page); // this.present_subpage(new Adw.NavigationPage(account_page, "Account: %s".printf(account.bare_jid.to_string()))); }); } }dino-0.5.0/main/vapi/0000775000000000000000000000000014776241610013040 5ustar rootrootdino-0.5.0/main/vapi/icu-uc.vapi0000664000000000000000000000126514776241610015112 0ustar rootrootnamespace ICU { [CCode (cname = "UProperty", cprefix = "UCHAR_", has_type_id = false, cheader_filename = "unicode/uchar.h")] public enum Property { EMOJI, EMOJI_PRESENTATION, EMOJI_MODIFIER, EMOJI_MODIFIER_BASE, BIDI_CLASS, } [CCode (cname = "UCharDirection", cprefix = "U_", has_type_id = false, cheader_filename = "unicode/uchar.h")] public enum CharDirection { DIR_NON_SPACING_MARK, } [CCode (cname = "u_hasBinaryProperty", cheader_filename = "unicode/uchar.h")] public bool has_binary_property(unichar c, Property p); [CCode (cname = "u_getIntPropertyValue", cheader_filename = "unicode/uchar.h")] public int32 get_int_property_value(unichar c, Property p); } dino-0.5.0/meson.build0000664000000000000000000000634514776241610013327 0ustar rootrootproject('Dino', 'c', 'cpp', 'vala', license: 'GPL-3.0') fs = import('fs') gnome = import('gnome') i18n = import('i18n') python = import('python') # plugin_crypto is enabled if any of the crypto plugins is enabled, auto if # none of them are explicitly enabled but at least one is set to auto, or # disabled if all of them are disabled. plugin_crypto = get_option('plugin-ice') foreach plugin : ['plugin-ice', 'plugin-omemo', 'plugin-rtp'] if get_option(plugin).enabled() and not plugin_crypto.enabled() plugin_crypto = get_option(plugin) elif get_option(plugin).allowed() and not plugin_crypto.allowed() plugin_crypto = get_option(plugin) endif endforeach dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0') dep_gee = dependency('gee-0.8') dep_gio = dependency('gio-2.0') dep_glib = dependency('glib-2.0') dep_gnutls = dependency('gnutls', disabler: true, required: get_option('plugin-ice')) dep_gmodule = dependency('gmodule-2.0') dep_gpgme = dependency('gpgme', disabler: true, required: get_option('plugin-openpgp')) dep_gstreamer = dependency('gstreamer-1.0', disabler: true, required: get_option('plugin-rtp')) dep_gstreamer_app = dependency('gstreamer-app-1.0', disabler: true, required: get_option('plugin-rtp')) dep_gstreamer_audio = dependency('gstreamer-audio-1.0', disabler: true, required: get_option('plugin-rtp')) dep_gstreamer_rtp = dependency('gstreamer-rtp-1.0', disabler: true, required: get_option('plugin-rtp')) dep_gstreamer_video = dependency('gstreamer-video-1.0', disabler: true, required: get_option('plugin-rtp')) dep_gtk4 = dependency('gtk4') dep_icu_uc = dependency('icu-uc') dep_libadwaita = dependency('libadwaita-1', version: '>=1.2') dep_libcanberra = dependency('libcanberra', disabler: true, required: get_option('plugin-notification-sound')) dep_libgcrypt = dependency('libgcrypt', disabler: true, required: plugin_crypto) dep_libqrencode = dependency('libqrencode', disabler: true, required: get_option('plugin-omemo')) dep_libsrtp2 = dependency('libsrtp2', disabler: true, required: plugin_crypto) dep_libomemo_c = dependency('libomemo-c', disabler: true, required: get_option('plugin-omemo')) dep_libsoup = dependency('libsoup-3.0', disabler: true, required: get_option('plugin-http-files')) dep_nice = dependency('nice', version: '>=0.1.15', disabler: true, required: get_option('plugin-ice')) dep_m = meson.get_compiler('c').find_library('m', required: false) dep_sqlite3 = dependency('sqlite3', version: '>=3.24') dep_webrtc_audio_processing = dependency('webrtc-audio-processing-1', required: false) if not dep_webrtc_audio_processing.found() dep_webrtc_audio_processing = dependency('webrtc-audio-processing', version: ['>=0.2'], required: false) endif if not dep_webrtc_audio_processing.found() dep_webrtc_audio_processing = dependency('webrtc-audio-processing-2', required: false) endif if not dep_webrtc_audio_processing.found() dep_webrtc_audio_processing = disabler() endif prog_git = find_program('git', required: false) prog_python = python.find_installation() default_install_rpath = '' if get_option('set-install-rpath') default_install_rpath = get_option('prefix') / get_option('libdir') endif subdir('qlite') subdir('xmpp-vala') subdir('libdino') subdir('main') subdir('crypto-vala') subdir('plugins') dino-0.5.0/meson_options.txt0000664000000000000000000000234114776241610014612 0ustar rootrootoption('plugindir', type: 'string', value: 'dino/plugins', description: 'Dino plugin directory, relative to libdir') option('set-install-rpath', type: 'boolean', value: false, description: 'Set rpath of installed binaries to the configured libdir') option('plugin-http-files', type: 'feature', description: 'HTTP file upload') option('plugin-ice', type: 'feature', description: 'Peer-to-peer communication') option('plugin-omemo', type: 'feature', description: 'End-to-end encryption using OMEMO') option('plugin-openpgp', type: 'feature', description: 'End-to-end encryption using PGP') option('plugin-rtp', type: 'feature', description: 'Voice/video calls') option('plugin-notification-sound', type: 'feature', value: 'disabled', description: 'Sound for chat notifications') option('plugin-rtp-h264', type: 'feature', value: 'disabled', description: 'H264 codec') option('plugin-rtp-msdk', type: 'feature', value: 'disabled', description: 'Intel MediaSDK') option('plugin-rtp-vaapi', type: 'feature', value: 'disabled', description: 'Video Acceleration API') option('plugin-rtp-vp9', type: 'feature', value: 'disabled', description: 'VP9 codec') option('plugin-rtp-webrtc-audio-processing', type: 'feature', description: 'Voice preprocessing') dino-0.5.0/plugins/0000775000000000000000000000000014776241610012636 5ustar rootrootdino-0.5.0/plugins/http-files/0000775000000000000000000000000014776241610014715 5ustar rootrootdino-0.5.0/plugins/http-files/meson.build0000664000000000000000000000134014776241610017055 0ustar rootrootdependencies = [ dep_dino, dep_gee, dep_glib, dep_gmodule, dep_gtk4, dep_libsoup, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/file_provider.vala', 'src/file_sender.vala', 'src/plugin.vala', 'src/register_plugin.vala', ) vala_args = [ '-D', 'SOUP_3_0', ] lib_http_files = shared_library('http-files', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_http_files = declare_dependency(link_with: lib_http_files, include_directories: include_directories('.')) summary('HTTP file upload (http-files)', dep_http_files, section: 'Plugins') dino-0.5.0/plugins/http-files/src/0000775000000000000000000000000014776241610015504 5ustar rootrootdino-0.5.0/plugins/http-files/src/file_provider.vala0000664000000000000000000001774714776241610021222 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Plugins.HttpFiles { public class FileProvider : Dino.FileProvider, Object { private StreamInteractor stream_interactor; private Dino.Database dino_db; private Soup.Session session; private static Regex http_url_regex = /^https?:\/\/([^\s#]*)$/; // Spaces are invalid in URLs and we can't use fragments for downloads private static Regex omemo_url_regex = /^aesgcm:\/\/(.*)#(([A-Fa-f0-9]{2}){48}|([A-Fa-f0-9]{2}){44})$/; public FileProvider(StreamInteractor stream_interactor, Dino.Database dino_db) { this.stream_interactor = stream_interactor; this.dino_db = dino_db; this.session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new ReceivedMessageListener(this)); } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "MESSAGE_REINTERPRETING"; } } public override string[] after_actions { get { return after_actions_const; } } private FileProvider outer; private StreamInteractor stream_interactor; public ReceivedMessageListener(FileProvider outer) { this.outer = outer; this.stream_interactor = outer.stream_interactor; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (Xep.StatelessFileSharing.get_file_shares(stanza) != null || Xep.StatelessFileSharing.get_source_attachments(stanza) != null) { return false; } string? oob_url = Xmpp.Xep.OutOfBandData.get_url_from_message(stanza); bool normal_file = oob_url != null && oob_url == message.body && FileProvider.http_url_regex.match(message.body); bool omemo_file = FileProvider.omemo_url_regex.match(message.body); if (normal_file || omemo_file) { outer.on_file_message(message, conversation); return true; } return false; } } private void on_file_message(Entities.Message message, Conversation conversation) { var additional_info = message.id.to_string(); var receive_data = new HttpFileReceiveData(); receive_data.url = message.body; var file_meta = new HttpFileMeta(); file_meta.file_name = extract_file_name_from_url(message.body); file_meta.message = message; file_incoming(additional_info, message.from, message.time, message.local_time, conversation, receive_data, file_meta); } public async FileMeta get_meta_info(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws FileReceiveError { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) return file_meta; var head_message = new Soup.Message("HEAD", http_receive_data.url); head_message.request_headers.append("Accept-Encoding", "identity"); #if SOUP_3_0 string transfer_host = Uri.parse(http_receive_data.url, UriFlags.NONE).get_host(); head_message.accept_certificate.connect((peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(transfer_host, peer_cert, errors); }); #endif try { #if SOUP_3_0 yield session.send_async(head_message, GLib.Priority.LOW, null); #else yield session.send_async(head_message, null); #endif } catch (Error e) { throw new FileReceiveError.GET_METADATA_FAILED("HEAD request failed"); } string? content_type = null, content_length = null; head_message.response_headers.foreach((name, val) => { if (name.down() == "content-type") content_type = val; if (name.down() == "content-length") content_length = val; }); file_meta.mime_type = content_type; if (content_length != null) { file_meta.size = int64.parse(content_length); } return file_meta; } public Encryption get_encryption(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { return Encryption.NONE; } public async InputStream download(FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) throws IOError { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) assert(false); var get_message = new Soup.Message("GET", http_receive_data.url); #if SOUP_3_0 string transfer_host = Uri.parse(http_receive_data.url, UriFlags.NONE).get_host(); get_message.accept_certificate.connect((peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(transfer_host, peer_cert, errors); }); #endif #if SOUP_3_0 InputStream stream = yield session.send_async(get_message, GLib.Priority.LOW, file_transfer.cancellable); #else InputStream stream = yield session.send_async(get_message, file_transfer.cancellable); #endif if (file_meta.size != -1) { return new LimitInputStream(stream, file_meta.size); } else { return stream; } } public FileMeta get_file_meta(FileTransfer file_transfer) throws FileReceiveError { if (file_transfer.provider == FileManager.SFS_PROVIDER_ID) { var file_meta = new HttpFileMeta(); file_meta.size = file_transfer.size; file_meta.mime_type = file_transfer.mime_type; file_meta.file_name = file_transfer.file_name; file_meta.message = null; return file_meta; } Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); if (conversation == null) throw new FileReceiveError.GET_METADATA_FAILED("No conversation"); Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); if (message == null) throw new FileReceiveError.GET_METADATA_FAILED("No message"); var file_meta = new HttpFileMeta(); file_meta.size = file_transfer.size; file_meta.mime_type = file_transfer.mime_type; file_meta.file_name = extract_file_name_from_url(message.body); file_meta.message = message; return file_meta; } public FileReceiveData? get_file_receive_data(FileTransfer file_transfer) { if (file_transfer.provider == FileManager.SFS_PROVIDER_ID) { if (!file_transfer.sfs_sources.is_empty) { var http_source = file_transfer.sfs_sources.get(0) as Xep.StatelessFileSharing.HttpSource; if (http_source != null) { var receive_data = new HttpFileReceiveData(); receive_data.url = http_source.url; return receive_data; } } return null; } Conversation? conversation = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(file_transfer.counterpart.bare_jid, file_transfer.account); if (conversation == null) return null; Message? message = stream_interactor.get_module(MessageStorage.IDENTITY).get_message_by_id(int.parse(file_transfer.info), conversation); if (message == null) return null; var receive_data = new HttpFileReceiveData(); receive_data.url = message.body; return receive_data; } private string extract_file_name_from_url(string url) { string ret = url; if (ret.contains("#")) { ret = ret.substring(0, ret.last_index_of("#")); } ret = Uri.unescape_string(ret.substring(ret.last_index_of("/") + 1)); return ret; } public int get_id() { return 0; } } } dino-0.5.0/plugins/http-files/src/file_sender.vala0000664000000000000000000002234014776241610020631 0ustar rootrootusing Dino.Entities; using Xmpp; using Gee; namespace Dino.Plugins.HttpFiles { public class HttpFileSender : FileSender, Object { private StreamInteractor stream_interactor; private Database db; private Soup.Session session; private HashMap max_file_sizes = new HashMap(Account.hash_func, Account.equals_func); public HttpFileSender(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; this.session = new Soup.Session(); session.user_agent = @"Dino/$(Dino.get_short_version()) "; stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).build_message_stanza.connect(check_add_sfs_element); } public async FileSendData? prepare_send_file(Conversation conversation, FileTransfer file_transfer, FileMeta file_meta) throws FileSendError { HttpFileSendData send_data = new HttpFileSendData(); if (send_data == null) return null; Xmpp.XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) return null; try { var slot_result = yield stream_interactor.module_manager.get_module(file_transfer.account, Xmpp.Xep.HttpFileUpload.Module.IDENTITY).request_slot(stream, file_transfer.server_file_name, file_meta.size, file_meta.mime_type); send_data.url_down = slot_result.url_get; send_data.url_up = slot_result.url_put; send_data.headers = slot_result.headers; } catch (Xep.HttpFileUpload.HttpFileTransferError e) { throw new FileSendError.UPLOAD_FAILED("Http file upload XMPP error: %s".printf(e.message)); } return send_data; } public async void send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) throws FileSendError { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return; bool can_reference_element = !conversation.type_.is_muc_semantic() || stream_interactor.get_module(EntityInfo.IDENTITY).has_feature_cached(conversation.account, conversation.counterpart, Xep.UniqueStableStanzaIDs.NS_URI); // Share unencrypted files via SFS (only if we'll be able to reference messages) if (conversation.encryption == Encryption.NONE && can_reference_element) { // Announce the file share Entities.Message file_share_message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(null, conversation); file_transfer.info = file_share_message.id.to_string(); file_transfer.file_sharing_id = Xmpp.random_uuid(); stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(file_share_message, conversation); // Upload file yield upload(file_transfer, send_data, file_meta); // Wait until we know the server id of the file share message (in MUCs; we get that from the reflected message) if (conversation.type_.is_muc_semantic()) { if (file_share_message.server_id == null) { ulong server_id_notify_id = file_share_message.notify["server-id"].connect(() => { Idle.add(send_file.callback); }); yield; file_share_message.disconnect(server_id_notify_id); } } file_transfer.sfs_sources.add(new Xep.StatelessFileSharing.HttpSource() { url=send_data.url_down } ); // Send source attachment MessageStanza stanza = new MessageStanza() { to = conversation.counterpart, type_ = conversation.type_ == GROUPCHAT ? MessageStanza.TYPE_GROUPCHAT : MessageStanza.TYPE_CHAT }; stanza.body = send_data.url_down; Xep.OutOfBandData.add_url_to_message(stanza, send_data.url_down); var sources = new ArrayList(); sources.add(new Xep.StatelessFileSharing.HttpSource() { url = send_data.url_down }); string attach_to_id = MessageStorage.get_reference_id(file_share_message); Xep.StatelessFileSharing.set_sfs_attachment(stanza, attach_to_id, file_transfer.file_sharing_id, sources); var stream = stream_interactor.get_stream(conversation.account); if (stream == null) throw new FileSendError.UPLOAD_FAILED("No stream"); stream.get_module(MessageModule.IDENTITY).send_message.begin(stream, stanza); } // Share encrypted files without SFS else { yield upload(file_transfer, send_data, file_meta); Entities.Message message = stream_interactor.get_module(MessageProcessor.IDENTITY).create_out_message(send_data.url_down, conversation); file_transfer.info = message.id.to_string(); message.encryption = send_data.encrypt_message ? conversation.encryption : Encryption.NONE; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(message, conversation); } } public async bool can_send(Conversation conversation, FileTransfer file_transfer) { if (!max_file_sizes.has_key(conversation.account)) return false; return file_transfer.size < max_file_sizes[conversation.account]; } public async long get_file_size_limit(Conversation conversation) { long? max_size = max_file_sizes[conversation.account]; if (max_size != null) { return max_size; } return -1; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer) { return false; } public async bool is_upload_available(Conversation conversation) { lock (max_file_sizes) { return max_file_sizes.has_key(conversation.account); } } #if !SOUP_3_0 private static void transfer_more_bytes(InputStream stream, Soup.MessageBody body) { uint8[] bytes = new uint8[4096]; ssize_t read = stream.read(bytes); if (read == 0) { body.complete(); return; } bytes.length = (int)read; body.append_buffer(new Soup.Buffer.take(bytes)); } #endif private async void upload(FileTransfer file_transfer, HttpFileSendData file_send_data, FileMeta file_meta) throws FileSendError { Xmpp.XmppStream? stream = stream_interactor.get_stream(file_transfer.account); if (stream == null) return; var put_message = new Soup.Message("PUT", file_send_data.url_up); #if SOUP_3_0 string transfer_host = Uri.parse(file_send_data.url_up, UriFlags.NONE).get_host(); put_message.accept_certificate.connect((peer_cert, errors) => { return ConnectionManager.on_invalid_certificate(transfer_host, peer_cert, errors); }); put_message.set_request_body(file_meta.mime_type, file_transfer.input_stream, (ssize_t) file_meta.size); #else put_message.request_headers.set_content_type(file_meta.mime_type, null); put_message.request_headers.set_content_length(file_meta.size); put_message.request_body.set_accumulate(false); put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); #endif foreach (var entry in file_send_data.headers.entries) { put_message.request_headers.append(entry.key, entry.value); } try { #if SOUP_3_0 yield session.send_async(put_message, GLib.Priority.LOW, file_transfer.cancellable); #else yield session.send_async(put_message, file_transfer.cancellable); #endif if (put_message.status_code < 200 || put_message.status_code >= 300) { throw new FileSendError.UPLOAD_FAILED("HTTP status code %s".printf(put_message.status_code.to_string())); } } catch (Error e) { throw new FileSendError.UPLOAD_FAILED("HTTP upload error: %s".printf(e.message)); } } private void on_stream_negotiated(Account account, XmppStream stream) { stream_interactor.module_manager.get_module(account, Xmpp.Xep.HttpFileUpload.Module.IDENTITY).feature_available.connect((stream, max_file_size) => { lock (max_file_sizes) { max_file_sizes[account] = max_file_size; } upload_available(account); }); } private void check_add_sfs_element(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (message.encryption != Encryption.NONE) return; FileTransfer? file_transfer = stream_interactor.get_module(FileTransferStorage.IDENTITY).get_file_by_message_id(message.id, conversation); if (file_transfer == null) return; Xep.StatelessFileSharing.set_sfs_element(message_stanza, file_transfer.file_sharing_id, file_transfer.file_metadata, file_transfer.sfs_sources); Xep.MessageProcessingHints.set_message_hint(message_stanza, Xep.MessageProcessingHints.HINT_STORE); } public int get_id() { return 0; } public float get_priority() { return 100; } } } dino-0.5.0/plugins/http-files/src/plugin.vala0000664000000000000000000000173314776241610017653 0ustar rootrootextern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.HttpFiles { public class Plugin : RootInterface, Object { public Dino.Application app; public FileProvider file_provider; public FileSender file_sender; public void registered(Dino.Application app) { this.app = app; file_provider = new FileProvider(app.stream_interactor, app.db); file_sender = new HttpFileSender(app.stream_interactor, app.db); app.stream_interactor.get_module(FileManager.IDENTITY).add_provider(file_provider); app.stream_interactor.get_module(FileManager.IDENTITY).add_sender(file_sender); } public void shutdown() { // Nothing to do } } private bool message_is_file(Database db, Entities.Message message) { Qlite.QueryBuilder builder = db.file_transfer.select({db.file_transfer.id}).with(db.file_transfer.info, "=", message.id.to_string()); return builder.count() > 0; } } dino-0.5.0/plugins/http-files/src/register_plugin.vala0000664000000000000000000000014214776241610021550 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.HttpFiles.Plugin); } dino-0.5.0/plugins/ice/0000775000000000000000000000000014776241610013376 5ustar rootrootdino-0.5.0/plugins/ice/meson.build0000664000000000000000000000156114776241610015543 0ustar rootrootdependencies = [ dep_crypto_vala, dep_dino, dep_gdk_pixbuf, dep_gee, dep_glib, dep_gmodule, dep_gnutls, dep_nice, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/dtls_srtp.vala', 'src/module.vala', 'src/plugin.vala', 'src/transport_parameters.vala', 'src/util.vala', 'src/register_plugin.vala', ) c_args = [ '-DG_LOG_DOMAIN="ice"', ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] lib_ice = shared_library('ice', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_ice = declare_dependency(link_with: lib_ice, include_directories: include_directories('.')) summary('Peer-to-peer communication (ice)', dep_ice, section: 'Plugins') dino-0.5.0/plugins/ice/src/0000775000000000000000000000000014776241610014165 5ustar rootrootdino-0.5.0/plugins/ice/src/dtls_srtp.vala0000664000000000000000000003150314776241610017052 0ustar rootrootusing GnuTLS; namespace Dino.Plugins.Ice.DtlsSrtp { public class CredentialsCapsule { public uint8[] own_fingerprint; public X509.Certificate[] own_cert; public X509.PrivateKey private_key; } public class Handler { public signal void send_data(uint8[] data); public bool ready { get { return srtp_session.has_encrypt && srtp_session.has_decrypt; }} public Mode mode { get; set; default = Mode.CLIENT; } public uint8[] own_fingerprint { get; private set; } public uint8[] peer_fingerprint { get; set; } public string peer_fp_algo { get; set; } private CredentialsCapsule credentials; private Cond buffer_cond = Cond(); private Mutex buffer_mutex = Mutex(); private Gee.LinkedList buffer_queue = new Gee.LinkedList(); private bool running = false; private bool stop = false; private bool restart = false; private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session(); public Handler.with_cert(CredentialsCapsule creds) { this.credentials = creds; this.own_fingerprint = creds.own_fingerprint; } public uint8[]? process_incoming_data(uint component_id, uint8[] data) throws Crypto.Error { if (data[0] >= 128) { if (!srtp_session.has_decrypt) { debug("Received data before SRTP session is ready, dropping."); return null; } if (component_id == 1) { if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { return srtp_session.decrypt_rtcp(data); } return srtp_session.decrypt_rtp(data); } if (component_id == 2) return srtp_session.decrypt_rtcp(data); } if (component_id == 1 && data.length >= 1 && (data[0] >= 20 && data[0] < 64)) { on_data_rec(data); return null; } debug("Dropping unknown data from component %u", component_id); return null; } public uint8[]? process_outgoing_data(uint component_id, uint8[] data) throws Crypto.Error { if (srtp_session.has_encrypt) { if (component_id == 1) { if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { return srtp_session.encrypt_rtcp(data); } return srtp_session.encrypt_rtp(data); } if (component_id == 2) return srtp_session.encrypt_rtcp(data); } return null; } public void on_data_rec(owned uint8[] data) { buffer_mutex.lock(); buffer_queue.add(new Bytes.take(data)); buffer_cond.signal(); buffer_mutex.unlock(); } internal static CredentialsCapsule generate_credentials() throws GLib.Error { int err = 0; X509.PrivateKey private_key = X509.PrivateKey.create(); err = private_key.generate(PKAlgorithm.ECDSA, 256); throw_if_error(err); var start_time = new DateTime.now_local().add_days(-1); var end_time = start_time.add_days(2); X509.Certificate cert = X509.Certificate.create(); cert.set_key(private_key); cert.set_version(1); cert.set_activation_time ((time_t) start_time.to_unix ()); cert.set_expiration_time ((time_t) end_time.to_unix ()); uint32 serial = 1; cert.set_serial(&serial, sizeof(uint32)); cert.sign(cert, private_key); uint8[] own_fingerprint = get_fingerprint(cert, DigestAlgorithm.SHA256); X509.Certificate[] own_cert = new X509.Certificate[] { (owned)cert }; var creds = new CredentialsCapsule(); creds.own_fingerprint = own_fingerprint; creds.own_cert = (owned) own_cert; creds.private_key = (owned) private_key; return creds; } public void stop_dtls_connection() { buffer_mutex.lock(); stop = true; buffer_cond.signal(); buffer_mutex.unlock(); } public async Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection() { MainContext context = MainContext.current_source().get_context(); var thread = new Thread("dtls-connection", () => { var res = setup_dtls_connection_thread(); Source source = new IdleSource(); source.set_callback(setup_dtls_connection.callback); source.attach(context); return res; }); yield; return thread.join(); } private Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection_thread() { buffer_mutex.lock(); if (stop) { restart = true; buffer_mutex.unlock(); return null; } if (running || ready) { buffer_mutex.unlock(); return null; } running = true; restart = false; buffer_mutex.unlock(); InitFlags server_or_client = mode == Mode.SERVER ? InitFlags.SERVER : InitFlags.CLIENT; debug("Setting up DTLS connection. We're %s", mode.to_string()); CertificateCredentials cert_cred = CertificateCredentials.create(); int err = cert_cred.set_x509_key(credentials.own_cert, credentials.private_key); throw_if_error(err); Session? session = Session.create(server_or_client | InitFlags.DATAGRAM); session.enable_heartbeat(1); session.set_srtp_profile_direct("SRTP_AES128_CM_HMAC_SHA1_80"); session.set_credentials(GnuTLS.CredentialsType.CERTIFICATE, cert_cred); session.server_set_request(CertificateRequest.REQUEST); session.set_priority_from_string("NORMAL:!VERS-TLS-ALL:+VERS-DTLS-ALL:+CTYPE-CLI-X509"); session.set_transport_pointer(this); session.set_pull_function(pull_function); session.set_pull_timeout_function(pull_timeout_function); session.set_push_function(push_function); session.set_verify_function(verify_function); DateTime maximum_time = new DateTime.now_utc().add_seconds(20); do { err = session.handshake(); DateTime current_time = new DateTime.now_utc(); if (maximum_time.compare(current_time) < 0) { warning("DTLS handshake timeouted"); err = ErrorCode.APPLICATION_ERROR_MIN + 1; break; } if (stop) { debug("DTLS handshake stopped"); err = ErrorCode.APPLICATION_ERROR_MIN + 2; break; } } while (err < 0 && !((ErrorCode)err).is_fatal()); buffer_mutex.lock(); if (stop) { stop = false; running = false; bool restart = restart; buffer_mutex.unlock(); if (restart) { debug("Restarting DTLS handshake"); return setup_dtls_connection_thread(); } return null; } buffer_mutex.unlock(); if (err != ErrorCode.SUCCESS) { warning("DTLS handshake failed: %s", ((ErrorCode)err).to_string()); return null; } uint8[] km = new uint8[150]; Datum? client_key, client_salt, server_key, server_salt; session.get_srtp_keys(km, km.length, out client_key, out client_salt, out server_key, out server_salt); if (client_key == null || client_salt == null || server_key == null || server_salt == null) { warning("SRTP client/server key/salt null"); } debug("Finished DTLS connection. We're %s", mode.to_string()); if (mode == Mode.SERVER) { srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); } else { srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); } return new Xmpp.Xep.Jingle.ContentEncryption(Xmpp.Xep.JingleIceUdp.DTLS_NS_URI, "DTLS-SRTP", credentials.own_fingerprint, peer_fingerprint); } private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) { Handler self = transport_ptr as Handler; self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait(self.buffer_mutex); if (self.stop) { self.buffer_mutex.unlock(); debug("DTLS handshake pull_function stopped"); return -1; } } Bytes data = self.buffer_queue.remove_at(0); self.buffer_mutex.unlock(); uint8[] data_uint8 = Bytes.unref_to_data((owned) data); Memory.copy(buffer, data_uint8, data_uint8.length); // The callback should return 0 on connection termination, a positive number indicating the number of bytes received, and -1 on error. return (ssize_t)data_uint8.length; } private static int pull_timeout_function(void* transport_ptr, uint ms) { Handler self = transport_ptr as Handler; int64 end_time = get_monotonic_time() + ms * 1000; self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait_until(self.buffer_mutex, end_time); if (self.stop) { self.buffer_mutex.unlock(); debug("DTLS handshake pull_timeout_function stopped"); return -1; } if (get_monotonic_time() > end_time) { self.buffer_mutex.unlock(); return 0; } } self.buffer_mutex.unlock(); // The callback should return 0 on timeout, a positive number if data can be received, and -1 on error. return 1; } private static ssize_t push_function(void* transport_ptr, uint8[] buffer) { Handler self = transport_ptr as Handler; self.send_data(buffer); // The callback should return a positive number indicating the bytes sent, and -1 on error. return (ssize_t)buffer.length; } private static int verify_function(Session session) { Handler self = session.get_transport_pointer() as Handler; try { bool valid = self.verify_peer_cert(session); if (!valid) { warning("DTLS certificate invalid. Aborting handshake."); return 1; } } catch (Error e) { warning("Error during DTLS certificate validation: %s. Aborting handshake.", e.message); return 1; } // The callback function should return 0 for the handshake to continue or non-zero to terminate. return 0; } private bool verify_peer_cert(Session session) throws GLib.Error { unowned Datum[] cert_datums = session.get_peer_certificates(); if (cert_datums.length == 0) { warning("No peer certs"); return false; } if (cert_datums.length > 1) warning("More than one peer cert"); X509.Certificate peer_cert = X509.Certificate.create(); peer_cert.import(ref cert_datums[0], CertificateFormat.DER); DigestAlgorithm algo; switch (peer_fp_algo) { case "sha-256": algo = DigestAlgorithm.SHA256; break; default: warning("Unkown peer fingerprint algorithm: %s", peer_fp_algo); return false; } uint8[] real_peer_fp = get_fingerprint(peer_cert, algo); if (real_peer_fp.length != this.peer_fingerprint.length) { warning("Fingerprint lengths not equal %i vs %i", real_peer_fp.length, peer_fingerprint.length); return false; } for (int i = 0; i < real_peer_fp.length; i++) { if (real_peer_fp[i] != this.peer_fingerprint[i]) { warning("First cert in peer cert list doesn't equal advertised one: %s vs %s", format_fingerprint(real_peer_fp), format_fingerprint(peer_fingerprint)); return false; } } return true; } } private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) { uint8[] buf = new uint8[512]; size_t buf_out_size = 512; certificate.get_fingerprint(digest_algo, buf, ref buf_out_size); uint8[] ret = new uint8[buf_out_size]; for (int i = 0; i < buf_out_size; i++) { ret[i] = buf[i]; } return ret; } private string format_fingerprint(uint8[] fingerprint) { var sb = new StringBuilder(); for (int i = 0; i < fingerprint.length; i++) { sb.append("%02X".printf(fingerprint[i])); if (i < fingerprint.length - 1) { sb.append(":"); } } return sb.str; } public enum Mode { CLIENT, SERVER } } dino-0.5.0/plugins/ice/src/module.vala0000664000000000000000000000453714776241610016330 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Ice.Module : JingleIceUdp.Module { public string? stun_ip = null; public uint stun_port = 0; public string? turn_ip = null; public Xep.ExternalServiceDiscovery.Service? turn_service = null; private weak Nice.Agent? agent; private HashMap cerds = new HashMap(); private Nice.Agent get_agent() { Nice.Agent? agent = this.agent; if (agent == null) { agent = new Nice.Agent(MainContext.@default(), Nice.Compatibility.RFC5245); if (stun_ip != null) { agent.stun_server = stun_ip; agent.stun_server_port = stun_port; } agent.ice_tcp = false; agent.set_software("Dino"); agent.weak_ref(agent_unweak); this.agent = agent; debug("STUN server for libnice %s %u", agent.stun_server, agent.stun_server_port); } return agent; } public override Jingle.TransportParameters create_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid) { DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid); return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid); } public override Jingle.TransportParameters parse_transport_parameters(XmppStream stream, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode transport) throws Jingle.IqError { DtlsSrtp.CredentialsCapsule? cred = get_create_credentials(local_full_jid, peer_full_jid); return new TransportParameters(get_agent(), cred, turn_service, turn_ip, components, local_full_jid, peer_full_jid, transport); } private DtlsSrtp.CredentialsCapsule? get_create_credentials(Jid local_full_jid, Jid peer_full_jid) { string from_to_id = local_full_jid.to_string() + peer_full_jid.to_string(); try { if (!cerds.has_key(from_to_id)) cerds[from_to_id] = DtlsSrtp.Handler.generate_credentials(); } catch (Error e) { warning("Error creating dtls credentials: %s", e.message); } return cerds[from_to_id]; } private void agent_unweak() { this.agent = null; } }dino-0.5.0/plugins/ice/src/plugin.vala0000664000000000000000000001101514776241610016326 0ustar rootrootusing Gee; using Dino.Entities; using Xmpp; using Xmpp.Xep; private extern const size_t NICE_ADDRESS_STRING_LEN; public class Dino.Plugins.Ice.Plugin : RootInterface, Object { private HashMap timeouts = new HashMap(Account.hash_func, Account.equals_func); public Dino.Application app; public void registered(Dino.Application app) { Nice.debug_enable(true); this.app = app; app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { list.add(new Module()); }); app.stream_interactor.stream_attached_modules.connect((account, stream) => { if (stream.get_module(Socks5Bytestreams.Module.IDENTITY) != null) { stream.get_module(Socks5Bytestreams.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses); } if (stream.get_module(JingleRawUdp.Module.IDENTITY) != null) { stream.get_module(JingleRawUdp.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses); } }); app.stream_interactor.stream_negotiated.connect(external_discovery_refresh_services); } private async void external_discovery_refresh_services(Account account, XmppStream stream) { Module? ice_udp_module = stream.get_module(JingleIceUdp.Module.IDENTITY) as Module; if (ice_udp_module == null) return; Gee.List services = yield ExternalServiceDiscovery.request_services(stream); foreach (Xep.ExternalServiceDiscovery.Service service in services) { if (service.transport == "udp" && (service.ty == "stun" || service.ty == "turn")) { InetAddress? ip = yield lookup_ipv4_addess(service.host); if (ip == null) continue; if (ip.is_any || ip.is_link_local || ip.is_loopback || ip.is_multicast || ip.is_site_local) { warning("Ignoring STUN/TURN server at %s", service.host); continue; } if (service.ty == "stun") { debug("Server offers STUN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string()); ice_udp_module.stun_ip = ip.to_string(); ice_udp_module.stun_port = service.port; } else if (service.ty == "turn") { debug("Server offers TURN server: %s:%u, resolved to %s", service.host, service.port, ip.to_string()); ice_udp_module.turn_ip = ip.to_string(); ice_udp_module.turn_service = service; } } } if (ice_udp_module.stun_ip == null) { InetAddress ip = yield lookup_ipv4_addess("stun.dino.im"); if (ip == null) return; debug("Using fallback STUN server: stun.dino.im:7886, resolved to %s", ip.to_string()); ice_udp_module.stun_ip = ip.to_string(); ice_udp_module.stun_port = 7886; } // Refresh TURN credentials before they expire if (ice_udp_module.turn_service != null) { DateTime? expires = ice_udp_module.turn_service.expires; if (expires != null) { uint refresh_delay = (uint) (expires.to_unix() - new DateTime.now_utc().to_unix()) / 2; if (refresh_delay >= 300 && refresh_delay <= (int64) uint.MAX) { // 5 min < refetch < max debug("Re-fetching TURN credentials in %u sec", refresh_delay); timeouts.unset(account); timeouts[account] = Timeout.add_seconds((uint) refresh_delay, () => { external_discovery_refresh_services.begin(account, stream); return false; }); } else { warning("TURN credentials' expiry time = %u, *not* re-fetching", refresh_delay); } } } } public void shutdown() { // Nothing to do } private async InetAddress? lookup_ipv4_addess(string host) { try { Resolver resolver = Resolver.get_default(); GLib.List? ips = yield resolver.lookup_by_name_async(host); foreach (GLib.InetAddress ina in ips) { if (ina.get_family() != SocketFamily.IPV4) continue; return ina; } } catch (Error e) { warning("Failed looking up IP address of %s", host); } return null; } }dino-0.5.0/plugins/ice/src/register_plugin.vala0000664000000000000000000000013414776241610020232 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Ice.Plugin); } dino-0.5.0/plugins/ice/src/transport_parameters.vala0000664000000000000000000004461614776241610021324 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransportParameters { private Nice.Agent agent; private uint stream_id; private bool we_want_connection; private bool remote_credentials_set; private Map connections = new HashMap(); private DtlsSrtp.Handler? dtls_srtp_handler; private MainContext thread_context; private MainLoop thread_loop; private class DatagramConnection : Jingle.DatagramConnection { private Nice.Agent agent; private DtlsSrtp.Handler? dtls_srtp_handler; private uint stream_id; private string? error; private ulong datagram_received_id; public DatagramConnection(Nice.Agent agent, DtlsSrtp.Handler? dtls_srtp_handler, uint stream_id, uint8 component_id) { this.agent = agent; this.dtls_srtp_handler = dtls_srtp_handler; this.stream_id = stream_id; this.component_id = component_id; this.datagram_received_id = this.datagram_received.connect((datagram) => { bytes_received += datagram.length; }); } public override async void terminate(bool we_terminated, string? reason_string = null, string? reason_text = null) { yield base.terminate(we_terminated, reason_string, reason_text); this.disconnect(datagram_received_id); agent = null; dtls_srtp_handler = null; } public override void send_datagram(Bytes datagram) { if (this.agent != null && is_component_ready(agent, stream_id, component_id)) { try { if (dtls_srtp_handler != null) { uint8[] encrypted_data = dtls_srtp_handler.process_outgoing_data(component_id, datagram.get_data()); if (encrypted_data == null) return; GLib.OutputVector vector = { encrypted_data, encrypted_data.length }; GLib.OutputVector[] vectors = { vector }; Nice.OutputMessage message = { vectors }; Nice.OutputMessage[] messages = { message }; agent.send_messages_nonblocking(stream_id, component_id, messages); } else { GLib.OutputVector vector = { datagram.get_data(), datagram.get_size() }; GLib.OutputVector[] vectors = { vector }; Nice.OutputMessage message = { vectors }; Nice.OutputMessage[] messages = { message }; agent.send_messages_nonblocking(stream_id, component_id, messages); } bytes_sent += datagram.length; } catch (GLib.Error e) { warning("%s while send_datagram stream %u component %u", e.message, stream_id, component_id); } } } } public TransportParameters(Nice.Agent agent, DtlsSrtp.CredentialsCapsule? credentials, Xep.ExternalServiceDiscovery.Service? turn_service, string? turn_ip, uint8 components, Jid local_full_jid, Jid peer_full_jid, StanzaNode? node = null) { base(components, local_full_jid, peer_full_jid, node); this.we_want_connection = (node == null); this.agent = agent; if (this.peer_fingerprint != null || !incoming) { dtls_srtp_handler = setup_dtls(this, credentials); own_fingerprint = dtls_srtp_handler.own_fingerprint; if (incoming) { own_setup = "active"; dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } else { own_setup = "actpass"; dtls_srtp_handler.mode = DtlsSrtp.Mode.SERVER; dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (content_encryption != null) { this.content.encryptions[content_encryption.encryption_ns] = content_encryption; } }); } } agent.candidate_gathering_done.connect(on_candidate_gathering_done); agent.initial_binding_request_received.connect(on_initial_binding_request_received); agent.component_state_changed.connect(on_component_state_changed); agent.new_selected_pair_full.connect(on_new_selected_pair_full); agent.new_candidate_full.connect(on_new_candidate); agent.controlling_mode = !incoming; stream_id = agent.add_stream(components); thread_context = new MainContext(); new Thread(@"ice-thread-$stream_id", () => { thread_context.push_thread_default(); thread_loop = new MainLoop(thread_context, false); thread_loop.run(); thread_context.pop_thread_default(); return null; }); if (turn_ip != null) { for (uint8 component_id = 1; component_id <= components; component_id++) { agent.set_relay_info(stream_id, component_id, turn_ip, turn_service.port, turn_service.username, turn_service.password, Nice.RelayType.UDP); debug("TURN info (component %i) %s:%u", component_id, turn_ip, turn_service.port); } } string ufrag; string pwd; agent.get_local_credentials(stream_id, out ufrag, out pwd); init(ufrag, pwd); for (uint8 component_id = 1; component_id <= components; component_id++) { // We don't properly get local candidates before this call agent.attach_recv(stream_id, component_id, thread_context, on_recv); } agent.gather_candidates(stream_id); } private static DtlsSrtp.Handler setup_dtls(TransportParameters tp, DtlsSrtp.CredentialsCapsule credentials) { var weak_self = WeakRef(tp); DtlsSrtp.Handler dtls_srtp = new DtlsSrtp.Handler.with_cert(credentials); dtls_srtp.send_data.connect((data) => { TransportParameters self = (TransportParameters) weak_self.get(); if (self != null) self.agent.send(self.stream_id, 1, data); }); return dtls_srtp; } private void on_candidate_gathering_done(uint stream_id) { if (stream_id != this.stream_id) return; debug("on_candidate_gathering_done in %u", stream_id); for (uint8 i = 1; i <= components; i++) { foreach (unowned Nice.Candidate nc in agent.get_local_candidates(stream_id, i)) { if (nc.transport == Nice.CandidateTransport.UDP) { JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc); if (candidate == null) continue; debug("Local candidate summary: %s", agent.generate_local_candidate_sdp(nc)); } } } } private void on_new_candidate(Nice.Candidate nc) { if (nc.stream_id != stream_id) return; JingleIceUdp.Candidate? candidate = candidate_to_jingle(nc); if (candidate == null) return; if (nc.transport == Nice.CandidateTransport.UDP) { // Execution was in the agent thread before add_local_candidate_threadsafe(candidate); } } private bool bytes_equal(uint8[] a1, uint8[] a2) { return a1.length == a2.length && Memory.cmp(a1, a2, a1.length) == 0; } public override void handle_transport_accept(StanzaNode transport) throws Jingle.IqError { debug("on_transport_accept from %s", peer_full_jid.to_string()); base.handle_transport_accept(transport); if (dtls_srtp_handler != null && peer_fingerprint != null) { if (dtls_srtp_handler.peer_fingerprint != null) { if (!bytes_equal(dtls_srtp_handler.peer_fingerprint, peer_fingerprint)) { warning("Tried to replace certificate fingerprint mid use. We don't allow that."); peer_fingerprint = dtls_srtp_handler.peer_fingerprint; peer_fp_algo = dtls_srtp_handler.peer_fp_algo; } } else { dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } if (peer_setup == "passive") { dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; dtls_srtp_handler.stop_dtls_connection(); dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { var content_encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (content_encryption != null) { this.content.encryptions[content_encryption.encryption_ns] = content_encryption; } }); } } else { dtls_srtp_handler = null; } } public override void handle_transport_info(StanzaNode transport) throws Jingle.IqError { debug("on_transport_info from %s", peer_full_jid.to_string()); base.handle_transport_info(transport); if (dtls_srtp_handler != null && peer_fingerprint != null) { if (dtls_srtp_handler.peer_fingerprint != null) { if (!bytes_equal(dtls_srtp_handler.peer_fingerprint, peer_fingerprint)) { warning("Tried to replace certificate fingerprint mid use. We don't allow that."); peer_fingerprint = dtls_srtp_handler.peer_fingerprint; peer_fp_algo = dtls_srtp_handler.peer_fp_algo; } } else { dtls_srtp_handler.peer_fingerprint = peer_fingerprint; dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } } if (!we_want_connection) return; if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) { agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd); remote_credentials_set = true; } for (uint8 i = 1; i <= components; i++) { SList candidates = new SList(); foreach (JingleIceUdp.Candidate candidate in remote_candidates) { if (candidate.component == i) { candidates.append(candidate_to_nice(candidate)); } } int new_candidates = agent.set_remote_candidates(stream_id, i, candidates); debug("Updated to %i remote candidates for candidate %u via transport info", new_candidates, i); } } public override void create_transport_connection(XmppStream stream, Jingle.Content content) { debug("create_transport_connection: %s", content.session.sid); debug("local_credentials: %s %s", local_ufrag, local_pwd); debug("remote_credentials: %s %s", remote_ufrag, remote_pwd); debug("expected incoming credentials: %s %s", local_ufrag + ":" + remote_ufrag, local_pwd); debug("expected outgoing credentials: %s %s", remote_ufrag + ":" + local_ufrag, remote_pwd); we_want_connection = true; if (remote_ufrag != null && remote_pwd != null && !remote_credentials_set) { agent.set_remote_credentials(stream_id, remote_ufrag, remote_pwd); remote_credentials_set = true; } for (uint8 i = 1; i <= components; i++) { SList candidates = new SList(); foreach (JingleIceUdp.Candidate candidate in remote_candidates) { if (candidate.ip.has_prefix("fe80::")) continue; if (candidate.component == i) { candidates.append(candidate_to_nice(candidate)); debug("remote candidate: %s", agent.generate_local_candidate_sdp(candidate_to_nice(candidate))); } } int new_candidates = agent.set_remote_candidates(stream_id, i, candidates); debug("Initiated component %u with %i remote candidates", i, new_candidates); connections[i] = new DatagramConnection(agent, dtls_srtp_handler, stream_id, i); content.set_transport_connection(connections[i], i); } base.create_transport_connection(stream, content); } private void on_component_state_changed(uint stream_id, uint component_id, uint state) { if (stream_id != this.stream_id) return; debug("stream %u component %u state changed to %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string()); may_consider_ready(stream_id, component_id); if (incoming && dtls_srtp_handler != null && !dtls_srtp_handler.ready && is_component_ready(agent, stream_id, component_id) && dtls_srtp_handler.mode == DtlsSrtp.Mode.CLIENT) { dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { Jingle.ContentEncryption? encryption = dtls_srtp_handler.setup_dtls_connection.end(res); if (encryption != null) { this.content.encryptions[encryption.encryption_ns] = encryption; } }); } } private void may_consider_ready(uint stream_id, uint component_id) { if (stream_id != this.stream_id) return; if (connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready && is_component_ready(agent, stream_id, component_id) && (dtls_srtp_handler == null || dtls_srtp_handler.ready)) { connections[(uint8)component_id].ready = true; } } private void on_initial_binding_request_received(uint stream_id) { if (stream_id != this.stream_id) return; debug("initial_binding_request_received"); } private void on_new_selected_pair_full(uint stream_id, uint component_id, Nice.Candidate p1, Nice.Candidate p2) { if (stream_id != this.stream_id) return; debug("new_selected_pair_full %u [%s, %s]", component_id, agent.generate_local_candidate_sdp(p1), agent.generate_local_candidate_sdp(p2)); } private void on_recv(Nice.Agent agent, uint stream_id, uint component_id, uint8[] data) { if (stream_id != this.stream_id) return; uint8[] decrypt_data = null; if (dtls_srtp_handler != null) { try { decrypt_data = dtls_srtp_handler.process_incoming_data(component_id, data); if (decrypt_data == null) return; } catch (Crypto.Error e) { warning("%s while on_recv stream %u component %u", e.message, stream_id, component_id); return; } } may_consider_ready(stream_id, component_id); if (connections.has_key((uint8) component_id)) { if (!connections[(uint8) component_id].ready) { debug("on_recv stream %u component %u when state %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string()); } connections[(uint8) component_id].datagram_received(new Bytes(decrypt_data ?? data)); } else { debug("on_recv stream %u component %u length %u", stream_id, component_id, data.length); } } private static Nice.Candidate candidate_to_nice(JingleIceUdp.Candidate c) { Nice.CandidateType type; switch (c.type_) { case JingleIceUdp.Candidate.Type.HOST: type = Nice.CandidateType.HOST; break; case JingleIceUdp.Candidate.Type.PRFLX: type = Nice.CandidateType.PEER_REFLEXIVE; break; case JingleIceUdp.Candidate.Type.RELAY: type = Nice.CandidateType.RELAYED; break; case JingleIceUdp.Candidate.Type.SRFLX: type = Nice.CandidateType.SERVER_REFLEXIVE; break; default: assert_not_reached(); } Nice.Candidate candidate = new Nice.Candidate(type); candidate.component_id = c.component; char[] foundation = new char[Nice.CANDIDATE_MAX_FOUNDATION]; Memory.copy(foundation, c.foundation.data, size_t.min(c.foundation.length, Nice.CANDIDATE_MAX_FOUNDATION - 1)); candidate.foundation = foundation; candidate.addr = Nice.Address(); candidate.addr.init(); candidate.addr.set_from_string(c.ip); candidate.addr.set_port(c.port); candidate.priority = c.priority; if (c.rel_addr != null) { candidate.base_addr = Nice.Address(); candidate.base_addr.init(); candidate.base_addr.set_from_string(c.rel_addr); candidate.base_addr.set_port(c.rel_port); } candidate.transport = Nice.CandidateTransport.UDP; return candidate; } private static JingleIceUdp.Candidate? candidate_to_jingle(Nice.Candidate nc) { JingleIceUdp.Candidate candidate = new JingleIceUdp.Candidate(); switch (nc.type) { case Nice.CandidateType.HOST: candidate.type_ = JingleIceUdp.Candidate.Type.HOST; break; case Nice.CandidateType.PEER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.PRFLX; break; case Nice.CandidateType.RELAYED: candidate.type_ = JingleIceUdp.Candidate.Type.RELAY; break; case Nice.CandidateType.SERVER_REFLEXIVE: candidate.type_ = JingleIceUdp.Candidate.Type.SRFLX; break; default: assert_not_reached(); } candidate.component = (uint8) nc.component_id; candidate.foundation = ((string)nc.foundation).dup(); candidate.generation = 0; candidate.id = Random.next_int().to_string("%08x"); // TODO char[] res = new char[NICE_ADDRESS_STRING_LEN]; nc.addr.to_string(res); candidate.ip = (string) res; candidate.network = 0; // TODO candidate.port = (uint16) nc.addr.get_port(); candidate.priority = nc.priority; candidate.protocol = "udp"; if (nc.base_addr.is_valid() && !nc.base_addr.equal(nc.addr)) { res = new char[NICE_ADDRESS_STRING_LEN]; nc.base_addr.to_string(res); candidate.rel_addr = (string) res; candidate.rel_port = (uint16) nc.base_addr.get_port(); } if (candidate.ip.has_prefix("fe80::")) return null; return candidate; } public override void dispose() { base.dispose(); agent = null; dtls_srtp_handler = null; connections.clear(); if (thread_loop != null) { thread_loop.quit(); } } } dino-0.5.0/plugins/ice/src/util.vala0000664000000000000000000000103414776241610016005 0ustar rootrootusing Gee; namespace Dino.Plugins.Ice { internal static bool is_component_ready(Nice.Agent agent, uint stream_id, uint component_id) { var state = agent.get_component_state(stream_id, component_id); return state == Nice.ComponentState.CONNECTED || state == Nice.ComponentState.READY; } internal Gee.List get_local_ip_addresses() { Gee.List result = new ArrayList(); foreach (string ip_address in Nice.interfaces_get_local_ips(false)) { result.add(ip_address); } return result; } }dino-0.5.0/plugins/ice/vapi/0000775000000000000000000000000014776241610014335 5ustar rootrootdino-0.5.0/plugins/ice/vapi/gnutls.vapi0000664000000000000000000003671514776241610016546 0ustar rootroot[CCode (cprefix = "gnutls_", lower_case_cprefix = "gnutls_", cheader_filename = "gnutls/gnutls.h")] namespace GnuTLS { public int global_init(); [CCode (cname = "gnutls_pull_func", has_target = false)] public delegate ssize_t PullFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array); [CCode (cname = "gnutls_pull_timeout_func", has_target = false)] public delegate int PullTimeoutFunc(void* transport_ptr, uint ms); [CCode (cname = "gnutls_push_func", has_target = false)] public delegate ssize_t PushFunc(void* transport_ptr, [CCode (ctype = "void*", array_length_type="size_t")] uint8[] array); [CCode (cname = "gnutls_certificate_verify_function", has_target = false)] public delegate int VerifyFunc(Session session); [Compact] [CCode (cname = "struct gnutls_session_int", free_function = "gnutls_deinit")] public class Session { public static Session? create(int con_end) throws GLib.Error { Session result; var ret = init(out result, con_end); throw_if_error(ret); return result; } [CCode (cname = "gnutls_init")] private static int init(out Session session, int con_end); [CCode (cname = "gnutls_transport_set_push_function")] public void set_push_function(PushFunc func); [CCode (cname = "gnutls_transport_set_pull_function")] public void set_pull_function(PullFunc func); [CCode (cname = "gnutls_transport_set_pull_timeout_function")] public void set_pull_timeout_function(PullTimeoutFunc func); [CCode (cname = "gnutls_transport_set_ptr")] public void set_transport_pointer(void* ptr); [CCode (cname = "gnutls_transport_get_ptr")] public void* get_transport_pointer(); [CCode (cname = "gnutls_heartbeat_enable")] public int enable_heartbeat(uint type); [CCode (cname = "gnutls_certificate_server_set_request")] public void server_set_request(CertificateRequest req); [CCode (cname = "gnutls_credentials_set")] public int set_credentials_(CredentialsType type, void* cred); [CCode (cname = "gnutls_credentials_set_")] public void set_credentials(CredentialsType type, void* cred) throws GLib.Error { int err = set_credentials_(type, cred); throw_if_error(err); } [CCode (cname = "gnutls_priority_set_direct")] public int set_priority_from_string_(string priority, out unowned string err_pos = null); [CCode (cname = "gnutls_priority_set_direct_")] public void set_priority_from_string(string priority, out unowned string err_pos = null) throws GLib.Error { int err = set_priority_from_string_(priority, out err_pos); throw_if_error(err); } [CCode (cname = "gnutls_srtp_set_profile_direct")] public int set_srtp_profile_direct_(string profiles, out unowned string err_pos = null); [CCode (cname = "gnutls_srtp_set_profile_direct_")] public void set_srtp_profile_direct(string profiles, out unowned string err_pos = null) throws GLib.Error { int err = set_srtp_profile_direct_(profiles, out err_pos); throw_if_error(err); } [CCode (cname = "gnutls_transport_set_int")] public void transport_set_int(int fd); [CCode (cname = "gnutls_handshake")] public int handshake(); [CCode (cname = "gnutls_srtp_get_keys")] public int get_srtp_keys_(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt); [CCode (cname = "gnutls_srtp_get_keys_")] public void get_srtp_keys(void *key_material, uint32 key_material_size, out Datum client_key, out Datum client_salt, out Datum server_key, out Datum server_salt) throws GLib.Error { get_srtp_keys_(key_material, key_material_size, out client_key, out client_salt, out server_key, out server_salt); } [CCode (cname = "gnutls_certificate_get_peers", array_length_type = "unsigned int")] public unowned Datum[]? get_peer_certificates(); [CCode (cname = "gnutls_session_set_verify_function")] public void set_verify_function(VerifyFunc func); } [Compact] [CCode (cname = "struct gnutls_certificate_credentials_st", free_function = "gnutls_certificate_free_credentials", cprefix = "gnutls_certificate_")] public class CertificateCredentials { [CCode (cname = "gnutls_certificate_allocate_credentials")] private static int allocate(out CertificateCredentials credentials); public static CertificateCredentials create() throws GLib.Error { CertificateCredentials result; var ret = allocate (out result); throw_if_error(ret); return result; } public void get_x509_crt(uint index, [CCode (array_length_type = "unsigned int")] out unowned X509.Certificate[] x509_ca_list); public int set_x509_key(X509.Certificate[] cert_list, X509.PrivateKey key); } [CCode (cheader_filename = "gnutls/x509.h", cprefix = "GNUTLS_")] namespace X509 { [Compact] [CCode (cname = "struct gnutls_x509_crt_int", cprefix = "gnutls_x509_crt_", free_function = "gnutls_x509_crt_deinit")] public class Certificate { [CCode (cname = "gnutls_x509_crt_init")] private static int init (out Certificate cert); public static Certificate create() throws GLib.Error { Certificate result; var ret = init (out result); throw_if_error(ret); return result; } [CCode (cname = "gnutls_x509_crt_import")] public int import_(ref Datum data, CertificateFormat format); [CCode (cname = "gnutls_x509_crt_import_")] public void import(ref Datum data, CertificateFormat format) throws GLib.Error { int err = import_(ref data, format); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_version")] public int set_version_(uint version); [CCode (cname = "gnutls_x509_crt_set_version_")] public void set_version(uint version) throws GLib.Error { int err = set_version_(version); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_key")] public int set_key_(PrivateKey key); [CCode (cname = "gnutls_x509_crt_set_key_")] public void set_key(PrivateKey key) throws GLib.Error { int err = set_key_(key); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_activation_time")] public int set_activation_time_(time_t act_time); [CCode (cname = "gnutls_x509_crt_set_activation_time_")] public void set_activation_time(time_t act_time) throws GLib.Error { int err = set_activation_time_(act_time); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_expiration_time")] public int set_expiration_time_(time_t exp_time); [CCode (cname = "gnutls_x509_crt_set_expiration_time_")] public void set_expiration_time(time_t exp_time) throws GLib.Error { int err = set_expiration_time_(exp_time); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_set_serial")] public int set_serial_(void* serial, size_t serial_size); [CCode (cname = "gnutls_x509_crt_set_serial_")] public void set_serial(void* serial, size_t serial_size) throws GLib.Error { int err = set_serial_(serial, serial_size); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_sign")] public int sign_(Certificate issuer, PrivateKey issuer_key); [CCode (cname = "gnutls_x509_crt_sign_")] public void sign(Certificate issuer, PrivateKey issuer_key) throws GLib.Error { int err = sign_(issuer, issuer_key); throw_if_error(err); } [CCode (cname = "gnutls_x509_crt_get_fingerprint")] public int get_fingerprint_(DigestAlgorithm algo, void* buf, ref size_t buf_size); [CCode (cname = "gnutls_x509_crt_get_fingerprint_")] public void get_fingerprint(DigestAlgorithm algo, void* buf, ref size_t buf_size) throws GLib.Error { int err = get_fingerprint_(algo, buf, ref buf_size); throw_if_error(err); } } [Compact] [CCode (cname = "struct gnutls_x509_privkey_int", cprefix = "gnutls_x509_privkey_", free_function = "gnutls_x509_privkey_deinit")] public class PrivateKey { private static int init (out PrivateKey key); public static PrivateKey create () throws GLib.Error { PrivateKey result; var ret = init (out result); throw_if_error(ret); return result; } public int generate(PKAlgorithm algo, uint bits, uint flags = 0); } } [CCode (cname = "gnutls_certificate_request_t", cprefix = "GNUTLS_CERT_", has_type_id = false)] public enum CertificateRequest { IGNORE, REQUEST, REQUIRE } [CCode (cname = "gnutls_pk_algorithm_t", cprefix = "GNUTLS_PK_", has_type_id = false)] public enum PKAlgorithm { UNKNOWN, RSA, DSA, ECDSA; } [CCode (cname = "gnutls_digest_algorithm_t", cprefix = "GNUTLS_DIG_", has_type_id = false)] public enum DigestAlgorithm { NULL, MD5, SHA1, RMD160, MD2, SHA224, SHA256, SHA384, SHA512; } [Flags] [CCode (cname = "gnutls_init_flags_t", cprefix = "GNUTLS_", has_type_id = false)] public enum InitFlags { SERVER, CLIENT, DATAGRAM } [CCode (cname = "gnutls_credentials_type_t", cprefix = "GNUTLS_CRD_", has_type_id = false)] public enum CredentialsType { CERTIFICATE, ANON, SRP, PSK, IA } [CCode (cname = "gnutls_x509_crt_fmt_t", cprefix = "GNUTLS_X509_FMT_", has_type_id = false)] public enum CertificateFormat { DER, PEM } [Flags] [CCode (cname = "gnutls_certificate_status_t", cprefix = "GNUTLS_CERT_", has_type_id = false)] public enum CertificateStatus { INVALID, // will be set if the certificate was not verified. REVOKED, // in X.509 this will be set only if CRLs are checked SIGNER_NOT_FOUND, SIGNER_NOT_CA, INSECURE_ALGORITHM } [SimpleType] [CCode (cname = "gnutls_datum_t", has_type_id = false)] public struct Datum { public uint8* data; public uint size; public uint8[] extract() { uint8[] ret = new uint8[size]; for (int i = 0; i < size; i++) { ret[i] = data[i]; } return ret; } } // Gnutls error codes. The mapping to a TLS alert is also shown in comments. [CCode (cname = "int", cprefix = "GNUTLS_E_", lower_case_cprefix = "gnutls_error_", has_type_id = false)] public enum ErrorCode { SUCCESS, UNKNOWN_COMPRESSION_ALGORITHM, UNKNOWN_CIPHER_TYPE, LARGE_PACKET, UNSUPPORTED_VERSION_PACKET, // GNUTLS_A_PROTOCOL_VERSION UNEXPECTED_PACKET_LENGTH, // GNUTLS_A_RECORD_OVERFLOW INVALID_SESSION, FATAL_ALERT_RECEIVED, UNEXPECTED_PACKET, // GNUTLS_A_UNEXPECTED_MESSAGE WARNING_ALERT_RECEIVED, ERROR_IN_FINISHED_PACKET, UNEXPECTED_HANDSHAKE_PACKET, UNKNOWN_CIPHER_SUITE, // GNUTLS_A_HANDSHAKE_FAILURE UNWANTED_ALGORITHM, MPI_SCAN_FAILED, DECRYPTION_FAILED, // GNUTLS_A_DECRYPTION_FAILED, GNUTLS_A_BAD_RECORD_MAC MEMORY_ERROR, DECOMPRESSION_FAILED, // GNUTLS_A_DECOMPRESSION_FAILURE COMPRESSION_FAILED, AGAIN, EXPIRED, DB_ERROR, SRP_PWD_ERROR, INSUFFICIENT_CREDENTIALS, HASH_FAILED, BASE64_DECODING_ERROR, MPI_PRINT_FAILED, REHANDSHAKE, // GNUTLS_A_NO_RENEGOTIATION GOT_APPLICATION_DATA, RECORD_LIMIT_REACHED, ENCRYPTION_FAILED, PK_ENCRYPTION_FAILED, PK_DECRYPTION_FAILED, PK_SIGN_FAILED, X509_UNSUPPORTED_CRITICAL_EXTENSION, KEY_USAGE_VIOLATION, NO_CERTIFICATE_FOUND, // GNUTLS_A_BAD_CERTIFICATE INVALID_REQUEST, SHORT_MEMORY_BUFFER, INTERRUPTED, PUSH_ERROR, PULL_ERROR, RECEIVED_ILLEGAL_PARAMETER, // GNUTLS_A_ILLEGAL_PARAMETER REQUESTED_DATA_NOT_AVAILABLE, PKCS1_WRONG_PAD, RECEIVED_ILLEGAL_EXTENSION, INTERNAL_ERROR, DH_PRIME_UNACCEPTABLE, FILE_ERROR, TOO_MANY_EMPTY_PACKETS, UNKNOWN_PK_ALGORITHM, // returned if libextra functionality was requested but // gnutls_global_init_extra() was not called. INIT_LIBEXTRA, LIBRARY_VERSION_MISMATCH, // returned if you need to generate temporary RSA // parameters. These are needed for export cipher suites. NO_TEMPORARY_RSA_PARAMS, LZO_INIT_FAILED, NO_COMPRESSION_ALGORITHMS, NO_CIPHER_SUITES, OPENPGP_GETKEY_FAILED, PK_SIG_VERIFY_FAILED, ILLEGAL_SRP_USERNAME, SRP_PWD_PARSING_ERROR, NO_TEMPORARY_DH_PARAMS, // For certificate and key stuff ASN1_ELEMENT_NOT_FOUND, ASN1_IDENTIFIER_NOT_FOUND, ASN1_DER_ERROR, ASN1_VALUE_NOT_FOUND, ASN1_GENERIC_ERROR, ASN1_VALUE_NOT_VALID, ASN1_TAG_ERROR, ASN1_TAG_IMPLICIT, ASN1_TYPE_ANY_ERROR, ASN1_SYNTAX_ERROR, ASN1_DER_OVERFLOW, OPENPGP_UID_REVOKED, CERTIFICATE_ERROR, CERTIFICATE_KEY_MISMATCH, UNSUPPORTED_CERTIFICATE_TYPE, // GNUTLS_A_UNSUPPORTED_CERTIFICATE X509_UNKNOWN_SAN, OPENPGP_FINGERPRINT_UNSUPPORTED, X509_UNSUPPORTED_ATTRIBUTE, UNKNOWN_HASH_ALGORITHM, UNKNOWN_PKCS_CONTENT_TYPE, UNKNOWN_PKCS_BAG_TYPE, INVALID_PASSWORD, MAC_VERIFY_FAILED, // for PKCS #12 MAC CONSTRAINT_ERROR, WARNING_IA_IPHF_RECEIVED, WARNING_IA_FPHF_RECEIVED, IA_VERIFY_FAILED, UNKNOWN_ALGORITHM, BASE64_ENCODING_ERROR, INCOMPATIBLE_CRYPTO_LIBRARY, INCOMPATIBLE_LIBTASN1_LIBRARY, OPENPGP_KEYRING_ERROR, X509_UNSUPPORTED_OID, RANDOM_FAILED, BASE64_UNEXPECTED_HEADER_ERROR, OPENPGP_SUBKEY_ERROR, CRYPTO_ALREADY_REGISTERED, HANDSHAKE_TOO_LARGE, UNIMPLEMENTED_FEATURE, APPLICATION_ERROR_MAX, // -65000 APPLICATION_ERROR_MIN; // -65500 [CCode (cname = "gnutls_error_is_fatal")] public bool is_fatal(); [CCode (cname = "gnutls_perror")] public void print(); [CCode (cname = "gnutls_strerror")] public unowned string to_string(); } public void throw_if_error(int err_int) throws GLib.Error { ErrorCode error = (ErrorCode)err_int; if (error != ErrorCode.SUCCESS) { throw new GLib.Error(-1, error, "%s%s", error.to_string(), error.is_fatal() ? " fatal" : ""); } } }dino-0.5.0/plugins/ice/vapi/metadata/0000775000000000000000000000000014776241610016115 5ustar rootrootdino-0.5.0/plugins/ice/vapi/metadata/Nice-0.1.metadata0000664000000000000000000000066414776241610020777 0ustar rootrootNice cheader_filename="nice.h" Address.to_string.dst type="char[]" Agent.new_reliable#constructor name="create_reliable" Agent.attach_recv skip=false Agent.send.buf type="uint8[]" array_length_idx=2 AgentRecvFunc.buf type="uint8[]" array_length_idx=3 PseudoTcpCallbacks#record skip PseudoTcpSocket#class skip # Not yet supported by vapigen # Candidate copy_function="nice_candidate_copy" free_function="nice_candidate_free" type_id="" dino-0.5.0/plugins/ice/vapi/nice.vapi0000664000000000000000000004100014776241610016127 0ustar rootroot/* nice.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Nice", gir_namespace = "Nice", gir_version = "0.1", lower_case_cprefix = "nice_")] namespace Nice { [CCode (cheader_filename = "nice.h", type_id = "nice_agent_get_type ()")] public class Agent : GLib.Object { [CCode (has_construct_function = false)] public Agent (GLib.MainContext ctx, Nice.Compatibility compat); public bool add_local_address (Nice.Address addr); public uint add_stream (uint n_components); public bool attach_recv (uint stream_id, uint component_id, GLib.MainContext ctx, Nice.AgentRecvFunc func); [Version (since = "0.1.16")] public async void close_async (); [CCode (cname = "nice_agent_new_reliable", has_construct_function = false)] [Version (since = "0.0.11")] public Agent.create_reliable (GLib.MainContext ctx, Nice.Compatibility compat); [Version (since = "0.1.6")] public bool forget_relays (uint stream_id, uint component_id); [CCode (has_construct_function = false)] [Version (since = "0.1.15")] public Agent.full (GLib.MainContext ctx, Nice.Compatibility compat, Nice.AgentOption flags); public bool gather_candidates (uint stream_id); [Version (since = "0.1.4")] public string generate_local_candidate_sdp (Nice.Candidate candidate); [Version (since = "0.1.4")] public string generate_local_sdp (); [Version (since = "0.1.4")] public string generate_local_stream_sdp (uint stream_id, bool include_non_ice); [Version (since = "0.1.8")] public Nice.ComponentState get_component_state (uint stream_id, uint component_id); public Nice.Candidate get_default_local_candidate (uint stream_id, uint component_id); [Version (since = "0.1.5")] public GLib.IOStream get_io_stream (uint stream_id, uint component_id); public GLib.SList get_local_candidates (uint stream_id, uint component_id); public bool get_local_credentials (uint stream_id, out string ufrag, out string pwd); public GLib.SList get_remote_candidates (uint stream_id, uint component_id); public bool get_selected_pair (uint stream_id, uint component_id, Nice.Candidate local, Nice.Candidate remote); [Version (since = "0.1.5")] public GLib.Socket? get_selected_socket (uint stream_id, uint component_id); [Version (since = "0.1.4")] public unowned string get_stream_name (uint stream_id); [Version (since = "0.1.4")] public Nice.Candidate parse_remote_candidate_sdp (uint stream_id, string sdp); [Version (since = "0.1.4")] public int parse_remote_sdp (string sdp); [Version (since = "0.1.4")] public GLib.SList parse_remote_stream_sdp (uint stream_id, string sdp, string ufrag, string pwd); [Version (since = "0.1.16")] public bool peer_candidate_gathering_done (uint stream_id); [Version (since = "0.1.5")] public ssize_t recv (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public int recv_messages (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public int recv_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] out unowned Nice.InputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "0.1.5")] public ssize_t recv_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "buf_len", array_length_pos = 3.5, array_length_type = "gsize")] out unowned uint8[] buf, GLib.Cancellable? cancellable = null) throws GLib.Error; public void remove_stream (uint stream_id); public bool restart (); [Version (since = "0.1.6")] public bool restart_stream (uint stream_id); public int send (uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 2.5, array_length_type = "guint", type = "const gchar*")] uint8[] buf); [Version (since = "0.1.5")] public int send_messages_nonblocking (uint stream_id, uint component_id, [CCode (array_length_cname = "n_messages", array_length_pos = 3.5, array_length_type = "guint")] Nice.OutputMessage[] messages, GLib.Cancellable? cancellable = null) throws GLib.Error; public bool set_local_credentials (uint stream_id, string ufrag, string pwd); public void set_port_range (uint stream_id, uint component_id, uint min_port, uint max_port); public bool set_relay_info (uint stream_id, uint component_id, string server_ip, uint server_port, string username, string password, Nice.RelayType type); public int set_remote_candidates (uint stream_id, uint component_id, GLib.SList candidates); public bool set_remote_credentials (uint stream_id, string ufrag, string pwd); public bool set_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation); public bool set_selected_remote_candidate (uint stream_id, uint component_id, Nice.Candidate candidate); [Version (since = "0.0.10")] public void set_software (string software); [Version (since = "0.1.4")] public bool set_stream_name (uint stream_id, string name); [Version (since = "0.0.9")] public void set_stream_tos (uint stream_id, int tos); [NoAccessorMethod] [Version (since = "0.1.8")] public bool bytestream_tcp { get; } [NoAccessorMethod] public uint compatibility { get; construct; } [NoAccessorMethod] public bool controlling_mode { get; set; } [NoAccessorMethod] [Version (since = "0.1.14")] public bool force_relay { get; set; } [NoAccessorMethod] public bool full_mode { get; construct; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool ice_tcp { get; set; } [NoAccessorMethod] [Version (since = "0.1.16")] public bool ice_trickle { get; set; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool ice_udp { get; set; } [NoAccessorMethod] [Version (since = "0.1.8")] public bool keepalive_conncheck { get; set; } [NoAccessorMethod] public void* main_context { get; construct; } [NoAccessorMethod] public uint max_connectivity_checks { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_ip { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_password { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public uint proxy_port { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public uint proxy_type { get; set; } [NoAccessorMethod] [Version (since = "0.0.4")] public string proxy_username { owned get; set; } [NoAccessorMethod] [Version (since = "0.0.11")] public bool reliable { get; construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_initial_timeout { get; set construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_max_retransmissions { get; set construct; } [NoAccessorMethod] public uint stun_pacing_timer { get; set construct; } [NoAccessorMethod] [Version (since = "0.1.15")] public uint stun_reliable_timeout { get; set construct; } [NoAccessorMethod] public string stun_server { owned get; set; } [NoAccessorMethod] public uint stun_server_port { get; set; } [NoAccessorMethod] public bool support_renomination { get; set; } [NoAccessorMethod] [Version (since = "0.0.7")] public bool upnp { get; set construct; } [NoAccessorMethod] [Version (since = "0.0.7")] public uint upnp_timeout { get; set construct; } public signal void candidate_gathering_done (uint stream_id); public signal void component_state_changed (uint stream_id, uint component_id, uint state); public signal void initial_binding_request_received (uint stream_id); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_candidate (uint stream_id, uint component_id, string foundation); [Version (since = "0.1.8")] public signal void new_candidate_full (Nice.Candidate candidate); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_remote_candidate (uint stream_id, uint component_id, string foundation); [Version (since = "0.1.8")] public signal void new_remote_candidate_full (Nice.Candidate candidate); [Version (deprecated = true, deprecated_since = "0.1.8")] public signal void new_selected_pair (uint stream_id, uint component_id, string lfoundation, string rfoundation); [Version (since = "0.1.8")] public signal void new_selected_pair_full (uint stream_id, uint component_id, Nice.Candidate lcandidate, Nice.Candidate rcandidate); [Version (since = "0.0.11")] public signal void reliable_transport_writable (uint stream_id, uint component_id); [Version (since = "0.1.5")] public signal void streams_removed ([CCode (array_length = false, array_null_terminated = true)] uint[] stream_ids); } [CCode (cheader_filename = "nice.h", copy_function = "nice_candidate_copy", free_function = "nice_candidate_free")] [Compact] public class Candidate { public Nice.Address addr; public Nice.Address base_addr; public uint component_id; [CCode (array_length = false)] public weak char foundation[33]; public weak string password; public uint32 priority; public void* sockptr; public uint stream_id; public Nice.CandidateTransport transport; public Nice.TurnServer turn; public Nice.CandidateType type; public weak string username; [CCode (has_construct_function = false)] public Candidate (Nice.CandidateType type); public Nice.Candidate copy (); [Version (since = "0.1.15")] public bool equal_target (Nice.Candidate candidate2); public void free (); } [CCode (cheader_filename = "nice.h", has_type_id = false)] public struct Address { [CCode (cname = "s.addr")] public void* s_addr; [CCode (cname = "s.ip4")] public void* s_ip4; [CCode (cname = "s.ip6")] public void* s_ip6; public void copy_to_sockaddr (void* sin); public bool equal (Nice.Address b); [Version (since = "0.1.8")] public bool equal_no_port (Nice.Address b); public void free (); public uint get_port (); public void init (); public int ip_version (); public bool is_private (); public bool is_valid (); public void set_from_sockaddr (void* sin); public bool set_from_string (string str); public void set_ipv4 (uint32 addr_ipv4); public void set_ipv6 (uint8 addr_ipv6); public void set_port (uint port); public void to_string ([CCode (array_length = false, type = "gchar*")] char[] dst); } [CCode (cheader_filename = "nice.h", has_type_id = false)] [Version (since = "0.1.5")] public struct InputMessage { [CCode (array_length_cname = "n_buffers")] public weak GLib.InputVector[] buffers; public int n_buffers; public Nice.Address from; public size_t length; } [CCode (cheader_filename = "nice.h", has_type_id = false)] [Version (since = "0.1.5")] public struct OutputMessage { [CCode (array_length_cname = "n_buffers")] public weak GLib.OutputVector[] buffers; public int n_buffers; } [CCode (cheader_filename = "nice.h", cname = "TurnServer", has_type_id = false)] public struct TurnServer { public int ref_count; public Nice.Address server; public weak string username; public weak string password; public Nice.RelayType type; } [CCode (cheader_filename = "nice.h", cprefix = "NICE_AGENT_OPTION_", has_type_id = false)] [Flags] [Version (since = "0.1.15")] public enum AgentOption { REGULAR_NOMINATION, RELIABLE, LITE_MODE, ICE_TRICKLE, SUPPORT_RENOMINATION } [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TRANSPORT_", has_type_id = false)] public enum CandidateTransport { UDP, TCP_ACTIVE, TCP_PASSIVE, TCP_SO } [CCode (cheader_filename = "nice.h", cprefix = "NICE_CANDIDATE_TYPE_", has_type_id = false)] public enum CandidateType { HOST, SERVER_REFLEXIVE, PEER_REFLEXIVE, RELAYED } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPATIBILITY_", has_type_id = false)] public enum Compatibility { RFC5245, DRAFT19, GOOGLE, MSN, WLM2009, OC2007, OC2007R2, LAST } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_STATE_", has_type_id = false)] public enum ComponentState { DISCONNECTED, GATHERING, CONNECTING, CONNECTED, READY, FAILED, LAST; [Version (since = "0.1.6")] public unowned string to_string (); } [CCode (cheader_filename = "nice.h", cprefix = "NICE_COMPONENT_TYPE_", has_type_id = false)] public enum ComponentType { RTP, RTCP } [CCode (cheader_filename = "nice.h", cprefix = "NICE_NOMINATION_MODE_", has_type_id = false)] [Version (since = "0.1.15")] public enum NominationMode { REGULAR, AGGRESSIVE } [CCode (cheader_filename = "nice.h", cprefix = "NICE_PROXY_TYPE_", has_type_id = false)] [Version (since = "0.0.4")] public enum ProxyType { NONE, SOCKS5, HTTP, LAST } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpDebugLevel", cprefix = "PSEUDO_TCP_DEBUG_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpDebugLevel { NONE, NORMAL, VERBOSE } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpShutdown", cprefix = "PSEUDO_TCP_SHUTDOWN_", has_type_id = false)] [Version (since = "0.1.8")] public enum PseudoTcpShutdown { RD, WR, RDWR } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpState", cprefix = "PSEUDO_TCP_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpState { LISTEN, SYN_SENT, SYN_RECEIVED, ESTABLISHED, CLOSED, FIN_WAIT_1, FIN_WAIT_2, CLOSING, TIME_WAIT, CLOSE_WAIT, LAST_ACK } [CCode (cheader_filename = "nice.h", cname = "PseudoTcpWriteResult", cprefix = "WR_", has_type_id = false)] [Version (since = "0.0.11")] public enum PseudoTcpWriteResult { SUCCESS, TOO_LARGE, FAIL } [CCode (cheader_filename = "nice.h", cprefix = "NICE_RELAY_TYPE_TURN_", has_type_id = false)] public enum RelayType { UDP, TCP, TLS } [CCode (cheader_filename = "nice.h", instance_pos = 4.9)] public delegate void AgentRecvFunc (Nice.Agent agent, uint stream_id, uint component_id, [CCode (array_length_cname = "len", array_length_pos = 3.5, array_length_type = "guint", type = "gchar*")] uint8[] buf); [CCode (cheader_filename = "nice.h", cname = "NICE_AGENT_MAX_REMOTE_CANDIDATES")] public const int AGENT_MAX_REMOTE_CANDIDATES; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_ACTIVE")] public const int CANDIDATE_DIRECTION_MS_PREF_ACTIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_DIRECTION_MS_PREF_PASSIVE")] public const int CANDIDATE_DIRECTION_MS_PREF_PASSIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_MAX_FOUNDATION")] public const int CANDIDATE_MAX_FOUNDATION; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_TCP")] public const int CANDIDATE_TRANSPORT_MS_PREF_TCP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TRANSPORT_MS_PREF_UDP")] public const int CANDIDATE_TRANSPORT_MS_PREF_UDP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_HOST")] public const int CANDIDATE_TYPE_PREF_HOST; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_NAT_ASSISTED")] public const int CANDIDATE_TYPE_PREF_NAT_ASSISTED; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_PEER_REFLEXIVE")] public const int CANDIDATE_TYPE_PREF_PEER_REFLEXIVE; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED")] public const int CANDIDATE_TYPE_PREF_RELAYED; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_RELAYED_UDP")] public const int CANDIDATE_TYPE_PREF_RELAYED_UDP; [CCode (cheader_filename = "nice.h", cname = "NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE")] public const int CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE; [CCode (cheader_filename = "nice.h")] public static void debug_disable (bool with_stun); [CCode (cheader_filename = "nice.h")] public static void debug_enable (bool with_stun); [CCode (cheader_filename = "nice.h")] public static string? interfaces_get_ip_for_interface (string interface_name); [CCode (cheader_filename = "nice.h")] public static GLib.List interfaces_get_local_interfaces (); [CCode (cheader_filename = "nice.h")] public static GLib.List interfaces_get_local_ips (bool include_loopback); [CCode (cheader_filename = "nice.h", cname = "pseudo_tcp_set_debug_level")] [Version (since = "0.0.11")] public static void pseudo_tcp_set_debug_level (Nice.PseudoTcpDebugLevel level); } dino-0.5.0/plugins/meson.build0000664000000000000000000000016014776241610014775 0ustar rootrootsubdir('http-files') subdir('ice') subdir('notification-sound') subdir('omemo') subdir('openpgp') subdir('rtp') dino-0.5.0/plugins/notification-sound/0000775000000000000000000000000014776241610016452 5ustar rootrootdino-0.5.0/plugins/notification-sound/meson.build0000664000000000000000000000141214776241610020612 0ustar rootrootdependencies = [ dep_dino, dep_gdk_pixbuf, dep_gee, dep_glib, dep_gmodule, dep_libcanberra, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/plugin.vala', 'src/register_plugin.vala', ) vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] lib_notification_sound = shared_library('notification-sound', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_notification_sound = declare_dependency(link_with: lib_notification_sound, include_directories: include_directories('.')) summary('Sound for chat notifications (notification-sound)', dep_notification_sound, section: 'Plugins')dino-0.5.0/plugins/notification-sound/src/0000775000000000000000000000000014776241610017241 5ustar rootrootdino-0.5.0/plugins/notification-sound/src/plugin.vala0000664000000000000000000000114314776241610021403 0ustar rootrootnamespace Dino.Plugins.NotificationSound { public class Plugin : RootInterface, Object { public Dino.Application app; private Canberra.Context sound_context; public void registered(Dino.Application app) { this.app = app; Canberra.Context.create(out sound_context); app.stream_interactor.get_module(NotificationEvents.IDENTITY).notify_content_item.connect((item, conversation) => { sound_context.play(0, Canberra.PROP_EVENT_ID, "message-new-instant", Canberra.PROP_EVENT_DESCRIPTION, "New Dino message"); }); } public void shutdown() { } } } dino-0.5.0/plugins/notification-sound/src/register_plugin.vala0000664000000000000000000000015214776241610023306 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.NotificationSound.Plugin); } dino-0.5.0/plugins/omemo/0000775000000000000000000000000014776241610013752 5ustar rootrootdino-0.5.0/plugins/omemo/data/0000775000000000000000000000000014776241610014663 5ustar rootrootdino-0.5.0/plugins/omemo/data/contact_details_dialog.ui0000664000000000000000000003747314776241610021717 0ustar rootroot left 10 10 10 10 False dino-0.5.0/plugins/omemo/data/encryption_preferences_entry.ui0000664000000000000000000000624714776241610023227 0ustar rootroot left 10 10 10 10 False dino-0.5.0/plugins/omemo/data/gresource.xml0000664000000000000000000000037614776241610017411 0ustar rootroot contact_details_dialog.ui encryption_preferences_entry.ui manage_key_dialog.ui dino-0.5.0/plugins/omemo/data/manage_key_dialog.ui0000664000000000000000000002260614776241610020647 0ustar rootroot dino-0.5.0/plugins/omemo/meson.build0000664000000000000000000000672014776241610016121 0ustar rootrootsubdir('po') dependencies = [ dep_libadwaita, dep_crypto_vala, dep_dino, dep_gee, dep_glib, dep_gmodule, dep_gtk4, dep_libgcrypt, dep_libqrencode, dep_libomemo_c, dep_m, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/dtls_srtp_verification_draft.vala', 'src/file_transfer/file_decryptor.vala', 'src/file_transfer/file_encryptor.vala', 'src/jingle/jet_omemo.vala', 'src/jingle/jingle_helper.vala', 'src/logic/database.vala', 'src/logic/decrypt.vala', 'src/logic/encrypt.vala', 'src/logic/manager.vala', 'src/logic/pre_key_store.vala', 'src/logic/session_store.vala', 'src/logic/signed_pre_key_store.vala', 'src/logic/trust_manager.vala', 'src/plugin.vala', 'src/protocol/bundle.vala', 'src/protocol/message_flag.vala', 'src/protocol/stream_module.vala', 'src/register_plugin.vala', 'src/native/context.vala', 'src/native/helper.c', 'src/native/simple_iks.vala', 'src/native/simple_pks.vala', 'src/native/simple_spks.vala', 'src/native/simple_ss.vala', 'src/native/store.vala', 'src/native/util.vala', 'src/trust_level.vala', 'src/ui/bad_messages_populator.vala', 'src/ui/call_encryption_entry.vala', 'src/ui/contact_details_provider.vala', 'src/ui/device_notification_populator.vala', 'src/ui/encryption_list_entry.vala', 'src/ui/encryption_preferences_entry.vala', 'src/ui/manage_key_dialog.vala', 'src/ui/own_notifications.vala', 'src/ui/util.vala', ) sources += gnome.compile_resources( 'resources', 'data/gresource.xml', source_dir: 'data', ) c_args = [ '-DG_LOG_DOMAIN="OMEMO"', '-DGETTEXT_PACKAGE="dino-omemo"', '-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')), ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', # This is to build internal vapi for tests '--header', meson.current_build_dir() / 'omemo.h', '--internal-vapi', meson.current_build_dir() / 'omemo-internal.vapi', '--internal-header', meson.current_build_dir() / 'omemo-internal.h', ] lib_omemo = shared_library('omemo', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_omemo = declare_dependency(link_with: lib_omemo, include_directories: include_directories('.')) summary('End-to-end encryption using OMEMO (omemo)', dep_omemo, section: 'Plugins') # This is to use the internal vapi instead of the regular vapi_omemo_internal = custom_target('omemo-internal-vapi', command: [find_program('touch'), meson.current_build_dir() / 'omemo-internal.vapi'], output: ['omemo-internal.vapi'], depends: lib_omemo) dep_omemo_internal = declare_dependency(link_args: [lib_omemo.full_path()], include_directories: include_directories('.', 'src'), sources: [vapi_omemo_internal]) test_sources = [ 'tests/native/common.vala', 'tests/native/testcase.vala', 'tests/native/curve25519.vala', 'tests/native/hkdf.vala', 'tests/native/session_builder.vala', ] test_vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] exe_omemo_test = executable('omemo-test', test_sources, c_args: c_args, vala_args: test_vala_args, dependencies: dependencies + dep_omemo_internal, build_rpath: '$ORIGIN', install: false) test('Tests for omemo', exe_omemo_test) dino-0.5.0/plugins/omemo/po/0000775000000000000000000000000014776241610014370 5ustar rootrootdino-0.5.0/plugins/omemo/po/LINGUAS0000664000000000000000000000017314776241610015416 0ustar rootrootar ca cs da de el en eo es et eu fa fi fr gl hu id ie it ja lb lt nb nl oc pl pt pt_BR ro ru sq sv ta tr uk vi zh_CN zh_TW dino-0.5.0/plugins/omemo/po/ar.po0000664000000000000000000003217014776241610015335 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-10-17 01:15+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 5.8-rc\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "يستخدم %s جهازًا غير موثوق به. لن ترى رسائل من أجهزة لا تثق بها." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "أدِر الأجهزة" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "لا يثق %s بهذا الجهاز. هذا يعني أنك قد تفقد بعض الرسائل." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "إدارة المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "قارن البصمة حرفًا بحرف مع تلك التي تظهر على جهاز مُراسِلك." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "البصمات غير متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "البصمات متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "ألغِ" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "أكِّد" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "تحقق من المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "ستميز الرسائل القادمة المرسلة بواسطة %s من الجهاز الذي يستخدم هذا المفتاح " "وفقًا لذلك في نافذة الدردشة." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "البصمات غير متطابقة" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "تحقق بأنك تقارن البصمة الصحيحة من فضلك. إن لم تكن البصمات متطابقة فإن حساب " "%s قد يكون مخترقًا، ومن المستحسن التفكير برفض هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "التحقق مِن مفتاح البصمة" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "قارن بصمة هذا المفتاح مع البصمة المعروضة على جهاز مُراسِلك." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "ارفض المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "يحظر الاتصال المشفر مع جهاز المراسل الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "اقبل المفتاح" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "يسمح بالاتصال المشفر بجهاز المراسل الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "هذا المفتاح %s حاليًا." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "مقبول" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "ذلك يعني أنه بإمكان %s أن يستخدمه لتلقي وإرسال رسائل مشفرة." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "مُحَقَّق" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "بالإضافة إلى ذلك، فقد تم التحقق من تطابق المفتاح على جهاز المُراسِل." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "مرفوض" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "هذا يعني أنه لن يكون بمقدور %s استخدامه لفك تشفير رسائلك، ولن ترى الرسائل " "المشفرة به." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "لن ترى الرسائل المشفرة من جهاز %s الذي يستخدم هذا المفتاح. وبخلاف ذلك، فلن " "يتمكن هذا الجهاز من فك تشفير رسائلك بعد الآن." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "سيكون بمقدورك تبادل الرسائل المشفرة مع جهاز %s الذي يستخدم هذا المفتاح." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "عُد" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "أدِر" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "لدى هذا المُراسِل أجهزة جديدة" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "قرار ثقة OMEMO إلزامي" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "هل قمت بإضافة جهاز جديد على حساب %s؟" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO مبدئيًا" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "فَعِّل تعمية OMEMO للمحادثات الجديدة" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "شَفِّر للأجهزة الجديدة" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "شَفِّر تلقائيًا للأجهزة الجديدة من هذا المراسِل." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "المفاتيح الجديدة" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "سَتُقبَل مفاتيح التشفير الجديدة من أجهزتك الأخرى تلقائيًا." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "قُبِل" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "رُفِض" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "مُحَقَّق" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "جهاز جديد" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "إدارة مفاتيح OMEMO" #~ msgid "Encryption" #~ msgstr "التعمية" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "لا أجهزة OMEMO" #~ msgstr[1] "جهاز OMEMO واحد" #~ msgstr[2] "جهازا OMEMO" #~ msgstr[3] "%d أجهزة OMEMO" #~ msgstr[4] "%d جهاز OMEMO" #~ msgstr[5] "%d جهاز OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "اقبّل المفاتيح الجديدة تلقائيًا" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "ستقبل مفاتيح التشفير الجديدة لهذا المراسل تلقائيًا." #~ msgid "Own key" #~ msgstr "مفتاحك الخاص" #~ msgid "Associated keys" #~ msgstr "المفاتيح المقترِنة" #~ msgid "Inactive keys" #~ msgstr "المفاتيح غير النشِطة" #~ msgid "Unused" #~ msgstr "غير مستخدَم" #~ msgid "Own fingerprint" #~ msgstr "بصمتك الخاصة" #~ msgid "Will be generated on first connection" #~ msgstr "سيتم توليدها عند أول اتصال" #~ msgid "Not matching" #~ msgstr "غير مطابقة" #~ msgid "Matching" #~ msgstr "مطابقة" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "بمجرد تأكيد ذلك ، سيتم تمييز أية رسائل مستقبلية مرسلة مِن %s باستخدام هذا " #~ "المفتاح وفقا لذلك في نافذة الدردشة." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "توقف عن قبول هذا المفتاح أثناء الاتصال مع المُراسِل المرتبط به." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "ابدأ في قبول هذا المفتاح أثناء الاتصال مع المُراسِل المرتبط به" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "هذا يعني أنه لا يمكن استخدامه مِن طرف %s لاستقبال الرسائل و سيتم تجاهل أية " #~ "رسائل بُعِثت به." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "بمجرد التأكيد ، سيتم تجاهل أي رسائل مستقبلية يقوم بإرسالها %s باستخدام " #~ "هذا المفتاح ويتصبح كافة رسائلك غير قابلة للقراءة باستخدام هذا المفتاح." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "بمجرد تأكيد هذا المفتاح سوف يكون قابلا للاستخدام من قِبل %s لتلقي وإرسال " #~ "الرسائل." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "عند قيام جهة الاتصال هذه باضافة مفاتيح تشفير جديدة إلى حسابها ، قم " #~ "بقبولها تلقائيا." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "عند إضافة مفاتيح تشفير جديدة إلى حسابك ، اقبلها تلقائيا." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "جهاز مجهول (0x%.8x)" #~ msgid "Other devices" #~ msgstr "أجهزة أخرى" #~ msgid "- None -" #~ msgstr "- لا شيء -" dino-0.5.0/plugins/omemo/po/ca.po0000664000000000000000000002424614776241610015323 0ustar rootroot# Catalan translation for dino-omemo. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018. # msgid "" msgstr "" "Project-Id-Version: dino-omemo 20180123\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-29 12:09+0000\n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s ha estat utilitzant un dispositiu no verificat. No veureu missatges de " "dispositius en què no confieu." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gestionar dispositius" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s no confia en aquest dispositiu. Això vol dir que és possible que us " "faltin missatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestiona la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compareu l'empremta, caràcter per caràcter, amb la que es mostra al vostre " "dispositiu de contactes." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Les empremtes són diferents" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Les empremtes coincideixen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancel·la" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirma" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Comproveu la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Els missatges futurs enviats per %s des del dispositiu que utilitza aquesta " "clau es ressaltaran en conseqüència a la finestra de xat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Les empremtes no es corresponen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Cercioreu-vos que estigueu verificant l'empremta correcta. Si no en " "coincideixen, el compte de %s podria trobar-se en perill i heu de considerar " "rebutjar aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar l'empremta de la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compareu l'empremta digital d'aquesta clau amb l'empremta digital que es " "mostra al dispositiu del contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rebutja la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloqueja la comunicació xifrada amb el dispositiu del contacte que utilitza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Accepta la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permet la comunicació xifrada amb el dispositiu del contacte que utilitza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Actualment, aquesta clau ha estat %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Això vol dir que %s pot utilitzar-la per rebre i enviar missatges xifrats." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "A més, s'ha verificat que coincideix amb la clau del dispositiu del contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rebutjada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Això vol dir que %s no pot utilitzar-la per desxifrar els vostres missatges " "i no veureu els missatges xifrats amb ella." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "No veureu missatges xifrats del dispositiu de %s que utilitza aquesta clau. " "Per contra, aquest dispositiu ja no podrà desxifrar els vostres missatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Podreu intercanviar missatges xifrats amb el dispositiu de %s que utilitza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Enrere" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestiona" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Aquest contacte té dispositius nous" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Cal una decisió de confiança d'OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Heu afegit un dispositiu nou per al compte %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO per defecte" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Activa l'encriptació OMEMO per a converses noves" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Encripta a nous dispositius" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Encripta automàticament a dispositius nous des d'aquest contacte." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Claus noves" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Les claus de xifratge noves dels altres dispositius s'acceptaran " "automàticament." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Acceptada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rebutjada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Nou dispositiu" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gestió de claus OMEMO" #~ msgid "Encryption" #~ msgstr "Xifratge" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispositiu OMEMO" #~ msgstr[1] "%d dispositius OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Accepta claus noves automàticament" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Les claus de xifratge noves d'aquest contacte s'acceptaran automàticament." #~ msgid "Own key" #~ msgstr "Clau pròpia" #~ msgid "Associated keys" #~ msgstr "Claus associades" #~ msgid "Inactive keys" #~ msgstr "Claus inactives" #~ msgid "Unused" #~ msgstr "No utilitzada" #~ msgid "Own fingerprint" #~ msgstr "Empremta pròpia" #~ msgid "Will be generated on first connection" #~ msgstr "Es generarà durant la primera connexió" #~ msgid "Not matching" #~ msgstr "No coincideixen" #~ msgid "Matching" #~ msgstr "Coincideixen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Després de la confirmació, qualssevol missatges que enviï %s amb aquesta " #~ "clau es ressaltarà a la finestra del xat." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositiu desconegut (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Altres dispositius" #~ msgid "- None -" #~ msgstr "- Cap -" dino-0.5.0/plugins/omemo/po/cs.po0000664000000000000000000002303514776241610015340 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-07-05 16:32+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s používá nedůvěryhodné zařízení. Zprávy z nedůvěryhodných zařízení nebudou " "zobrazeny." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Správa zařízení" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nedůvěřuje tomuto zařízení. Z tohoto důvodu nemusí vidět některé zprávy." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Spravovat klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Porovnejte otisk klíče, znak po znaku, s tím, který je zobrazen na zařízení " "vašeho kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Otisky klíčů se liší" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Otisky klíčů odpovídají" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Zrušit" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Potvrdit" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Ověřit klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Budoucí zprávy, s odesílatelem %s, ze zařízení, které používá tento klíč, " "budou odpovídajícím způsobem zvýrazněny v okně chatu." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Otisky klíčů se neshodují" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ověřte, zda porovnáváte správný otisk klíče. Pokud se otisky neshodují, účet " "%s může být ohrožen a měli byste zvážit zamítnutí tohoto klíče." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Ověření otisku klíče" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Porovnejte otisk tohoto klíče s otiskem zobrazeným na zařízení kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Odmítnout klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Zablokujte šifrovanou komunikaci se zařízením kontaktu, které tento klíč " "používá." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Přijmout klíč" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Povolit šifrovanou komunikaci se zařízením kontaktu, které používá tento " "klíč." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Tento klíč je v současné době %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akceptován" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "To znamená, že jej %s může používat k přijímání a odesílání šifrovaných " "zpráv." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "ověřen" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Navíc bylo ověřeno, že odpovídá klíči v zařízení kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "odmítnut" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "To znamená, že jej %s nemůže použít k dešifrování zpráv a vy neuvidíte " "zprávy šifrované tímto klíčem." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Ze zařízení %s, které používá tento klíč, se šifrované zprávy nezobrazí. " "Toto zařízení už také nebude schopno rozluštit vaše zprávy." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Budete si moci vyměňovat šifrované zprávy se zařízením %s, které tento klíč " "používá." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zpět" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Spravovat" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Tento kontakt má nová zařízení" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Vyžaduje se rozhodnutí o důvěře OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Přidali jste nové zařízení pro účet %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nové klíče" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nové šifrovací klíče z vašich ostatních zařízení budou automaticky přijímány." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Přijat" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Odmítnut" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Ověřen" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO Správa klíčů" #~ msgid "Encryption" #~ msgstr "Šifrování" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO zařízení" #~ msgstr[1] "%d OMEMO zařízení" #~ msgstr[2] "%d OMEMO zařízení" #~ msgid "Automatically accept new keys" #~ msgstr "Automaticky přijímat nové klíče" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nové šifrovací klíče od tohoto kontaktu budou automaticky přijímány." #~ msgid "Own key" #~ msgstr "Vlastní klíč" #~ msgid "Associated keys" #~ msgstr "Přidružené klíče" #~ msgid "Inactive keys" #~ msgstr "Neaktivní klíče" #~ msgid "Unused" #~ msgstr "Nepoužit" #~ msgid "Own fingerprint" #~ msgstr "Otisk vlastního klíče" #~ msgid "Will be generated on first connection" #~ msgstr "Budou generovány při prvním připojení" dino-0.5.0/plugins/omemo/po/da.po0000664000000000000000000001526014776241610015320 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-01-06 01:07+0000\n" "Language-Team: none\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "Encryption" #~ msgstr "Kryptering" dino-0.5.0/plugins/omemo/po/de.po0000664000000000000000000003123314776241610015322 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s hat ein nicht akzeptiertes Gerät benutzt. Du wirst Nachrichten von nicht " "akzeptierten Geräten nicht sehen." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Geräte verwalten" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s akzeptiert dieses Gerät nicht. Deshalb verpasst du vielleicht Nachrichten." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Schlüssel verwalten" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergleiche den Fingerabdruck, Zeichen für Zeichen, mit dem der auf dem Gerät " "deines Kontakts angezeigt wird." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingerabdrücke unterscheiden sich" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingerabdrücke stimmen überein" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Abbrechen" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bestätigen" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Schlüssel verifizieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Zukünftige Nachrichten, die %s von dem Gerät sendet, welches diese Schlüssel " "verwendet, werden im Chatfenster entsprechend hervorgehoben." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingerabdrücke stimmen nicht überein" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Bitte überprüfe, ob du den richtigen Fingerabdruck vergleichst. Wenn die " "Fingerabdrücke nicht übereinstimmen, ist das Konto von %s möglicherweise " "kompromittiert und du solltest in Betracht ziehen, den Schlüssel abzulehnen." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Fingerabdruck überprüfen" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Diesen Fingerabdruck mit dem Fingerabdruck vergleichen, der auf dem Gerät " "des Kontaktes angezeigt wird." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Schlüssel ablehnen" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockiert die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das " "diesen Schlüssel verwendet." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Schlüssel akzeptieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Erlaubt die verschlüsselte Kommunikation mit dem Gerät des Kontakts, das " "diesen Schlüssel verwendet." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Dieser Schlüssel ist aktuell %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akzeptiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Das bedeutet, er kann von %s zum Empfangen und Senden verschlüsselter " "Nachrichten verwendet werden." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "überprüft" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Darüber hinaus wurde überprüft, dass er mit dem Schlüssel auf dem Gerät des " "Kontaktes übereinstimmt." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "abgelehnt" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Das bedeutet %s kann ihn nicht benutzen, um deine Nachrichten zu " "entschlüsseln, und du wirst damit verschlüsselte Nachrichten nicht sehen." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du wirst verschlüsselte Nachrichten von dem Gerät von %s, welches diesen " "Schlüssel benutzt nicht mehr sehen. Umgekehrt kann dieses Gerät deine " "Nachrichten nicht mehr entschlüsseln." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du wirst verschlüsselte Nachrichten mit dem Gerät von %s, welches diesen " "Schlüssel verwendet, austauschen können." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zurück" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Verwalten" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Dieser Kontakt hat neue Geräte" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-Vertrauensentscheidung erforderlich" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hast du ein neues Gerät für dein Konto %s hinzugefügt?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO als Standard" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "OMEMO-Verschlüsselung für neue Unterhaltungen aktivieren" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Verschlüsselung für neue Geräte" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automatische Verschlüsselung für neue Geräte von diesem Kontakt." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Neue Schlüssel" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Neue Schlüssel von deinen anderen Geräten werden automatisch akzeptiert." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Dieses Gerät" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Anderes Gerät" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Gerät" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Akzeptiert" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Zurückgewiesen" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verifiziert" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Neues Gerät" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Jedes Gerät hat seinen eigenen OMEMO-Schlüssel. Nachrichten können von einem " "Gerät nur entschlüsselt werden, wenn sie mit diesem Schlüssel verschlüsselt " "sind. Nachrichten werden nur an akzeptierte Geräte verschlüsselt." #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO-Schlüssel Verwaltung" #~ msgid "Encryption" #~ msgstr "Verschlüsselung" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO Gerät" #~ msgstr[1] "%d OMEMO Geräte" #~ msgid "Automatically accept new keys" #~ msgstr "Neue Schlüssel automatisch akzeptieren" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Neue Schlüssel von diesem Kontakt werden automatisch akzeptiert." #~ msgid "Own key" #~ msgstr "Eigener Schlüssel" #~ msgid "Associated keys" #~ msgstr "Zugehörige Schlüssel" #~ msgid "Inactive keys" #~ msgstr "Inaktive Schlüssel" #~ msgid "Unused" #~ msgstr "Unbenutzt" #~ msgid "Own fingerprint" #~ msgstr "Eigener Fingerabdruck" #~ msgid "Will be generated on first connection" #~ msgstr "Wird beim ersten Verbinden erzeugt" #~ msgid "Your contact" #~ msgstr "Dein Kontakt" #~ msgid "Not matching" #~ msgstr "Nicht übereinstimmend" #~ msgid "Matching" #~ msgstr "Stimmt überein" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Nach der Bestätigung werden alle mit diesem Schlüssel verschlüsselten " #~ "Nachrichten von %s hervorgehoben." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Diesen Schlüssel nicht mehr für die Kommunikation mit dem " #~ "Schlüsselbesitzer akzeptieren." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Akzeptiere diesen Schlüssel für die Kommunikation mit dem zugehörigen " #~ "Kontakt" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Das bedeutet, dass es von %s nicht verwendet werden kann, um Nachrichten " #~ "zu empfangen und alle von ihm gesendeten Nachrichten werden ignoriert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Nach der Bestätigung werden alle zukünftigen Nachrichten, die von %s mit " #~ "diesem Schlüssel gesendet werden, ignoriert und keine Ihrer Nachrichten " #~ "ist mit diesem Schlüssel lesbar." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Sobald bestätigt, kann %s diesen Schlüssel zum Senden und Empfangen von " #~ "Nachrichten verwenden." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Wenn dieser Kontakt neue Verschlüsselungscodes zu seinem Konto hinzufügt, " #~ "werden sie automatisch akzeptiert." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Wenn du einen neuen Schüssel zu deinem Account hinzufügst, wird dieser " #~ "automatisch akzeptiert." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Diesen Schlüssel für die Kommunikation mit dem entsprechenden Kontakt " #~ "akzeptieren." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Unbekanntes Gerät (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andere Geräte" #~ msgid "- None -" #~ msgstr "- Keine -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Datenbank Fehler" dino-0.5.0/plugins/omemo/po/dino-omemo.pot0000664000000000000000000001517414776241610017167 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" dino-0.5.0/plugins/omemo/po/el.po0000664000000000000000000002756014776241610015342 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-02-04 09:55+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s χρησιμοποιεί μια μη αξιόπιστη συσκευή. Δεν θα βλέπετε μηνύματα από " "συσκευές που δεν εμπιστεύεστε." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Διαχείριση συσκευών" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s δεν εμπιστεύεται αυτήν τη συσκευή. Αυτό σημαίνει ότι μπορεί να χάσετε " "μηνύματα." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Διαχείριση Κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint), χαρακτήρα προς χαρακτήρα, " "με αυτό που εμφανίζεται στη συσκευή της επαφής σας." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) διαφέρουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) ταιριάζουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Ακύρωση" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Επιβεβαίωση" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Επαλήθευση κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Μελλοντικά μηνύματα που αποστέλλονται από %s από τη συσκευή που χρησιμοποιεί " "αυτό το κλειδί θα επισημαίνονται ανάλογα στο παράθυρο συνομιλίας." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Τα δακτυλικά αποτυπώματα (fingerprints) δεν ταιριάζουν" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Επαληθεύστε ότι συγκρίνετε το σωστό δακτυλικό αποτύπωμα (fingerprint). Εάν " "τα δακτυλικά αποτυπώματα (fingerprints) δεν ταιριάζουν, ο λογαριασμός του %s " "μπορεί να παραβιαστεί και θα πρέπει να σκεφτείτε να απορρίψετε αυτό το " "κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Επαλήθευση δακτυλικού αποτυπώματος (fingerprint) κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Συγκρίνετε το δακτυλικό αποτύπωμα (fingerprint) αυτού του κλειδιού με το " "δακτυλικό αποτύπωμα που εμφανίζεται στη συσκευή της επαφής." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Απόρριψη κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Αποκλεισμός κρυπτογραφημένης επικοινωνίας με τη συσκευή της επαφής που " "χρησιμοποιεί αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Αποδοχή κλειδιού" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Να επιτρέπεται η κρυπτογραφημένη επικοινωνία με τη συσκευή της επαφής που " "χρησιμοποιεί αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Αυτό το κλειδί είναι αυτή τη στιγμή %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "δεκτό" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Αυτό σημαίνει ότι μπορεί να χρησιμοποιηθεί από το %s για λήψη και αποστολή " "κρυπτογραφημένων μηνυμάτων." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "επαληθευμένο" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Επιπλέον, έχει επαληθευτεί ότι ταιριάζει με το κλειδί στη συσκευή της επαφής." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "απορριφθέν" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Αυτό σημαίνει ότι δεν μπορεί να χρησιμοποιηθεί από %s για την " "αποκρυπτογράφηση των μηνυμάτων σας και δεν θα βλέπετε μηνύματα " "κρυπτογραφημένα με αυτό." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Δεν θα βλέπετε κρυπτογραφημένα μηνύματα από τη συσκευή των %s που " "χρησιμοποιούν αυτό το κλειδί. Αντίθετα, αυτή η συσκευή δεν θα μπορεί πλέον " "να αποκρυπτογραφήσει τα μηνύματά σας." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Θα μπορείτε να ανταλλάσσετε κρυπτογραφημένα μηνύματα με τη συσκευή των %s " "που χρησιμοποιούν αυτό το κλειδί." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Πίσω" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Διαχείρηση" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Αυτή η επαφή έχει νέες συσκευές" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Απαιτείται απόφαση εμπιστοσύνης OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Προσθέσατε νέα συσκευή για τον λογαριασμό %s;" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Νέα κλειδιά" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Τα νέα κλειδιά κρυπτογράφησης από τις άλλες συσκευές σας θα γίνονται δεκτά " "αυτόματα." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Αποδεκτά" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Απορριφθέντα" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Επαληθευμένα" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Διαχείριση κλειδιών OMEMO" #~ msgid "Encryption" #~ msgstr "Κρυπτογράφηση" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO συσκευή" #~ msgstr[1] "%d OMEMO συσκευές" #~ msgid "Automatically accept new keys" #~ msgstr "Αυτόματη αποδοχή νέων κλειδιών" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Τα νέα κλειδιά κρυπτογράφησης από αυτήν την επαφή θα γίνονται δεκτά " #~ "αυτόματα." #~ msgid "Own key" #~ msgstr "Το κλειδί μου" #~ msgid "Associated keys" #~ msgstr "Συσχετισμένα κλειδιά" #~ msgid "Inactive keys" #~ msgstr "Ανενεργά κλειδιά" #~ msgid "Unused" #~ msgstr "Αχρησιμοποίητα" #~ msgid "Own fingerprint" #~ msgstr "Το δακτυλικό μου αποτύπωμα (fingerprint)" #~ msgid "Will be generated on first connection" #~ msgstr "Θα δημιουργηθεί με την πρώτη σύνδεση" dino-0.5.0/plugins/omemo/po/en.po0000664000000000000000000001445714776241610015345 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" dino-0.5.0/plugins/omemo/po/eo.po0000664000000000000000000002265714776241610015347 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-18 10:09+0000\n" "Language-Team: none\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s uzas nefidatan aparaton. Vi ne vidos mesaĝojn el aparatoj, kiujn vi ne " "fidas." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Administri aparatojn" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s ne fidas ĉi tiun aparaton. Tio signifas, ke eble mankas al vi mesaĝoj." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Administri Ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Komparu la fingropremaĵon, signon post signo, kun la fingropremaĵo montrata " "sur la aparato de via kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingropremaĵoj diferencas" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingropremaĵoj kongruas" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Nuligi" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Konfirmi" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Konfirmi ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesaĝoj sendotaj de %s per la aparato kiu uzas ĉi tiun ŝlosilon estos " "emfazitaj laŭe en la babila fenestro." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingropremaĵoj ne kongruas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Bonvolu konfirmi, ke vi komparas la ĝustan fingropremaĵon. Se fingropremaĵoj " "ne kongruas, la konto de %s eble estas kompromitita, kaj vi konsideru " "rifuzon de ĉi tiu ŝlosilo." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Konfirmi ŝlosilan fingropremaĵon" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Komparu la fingropremaĵon de ĉi tiu ŝlosilo kun la fingropremaĵo montrata " "sur la aparato de la kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rifuzi ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Rifuzi ĉifritan komunikadon al la aparato de la kontakto, kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Akcepti ŝlosilon" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Akcepti ĉifritan komunikadon al la aparato de la kontakto, kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ĉi tiu ŝlosilo estas aktuale %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akceptata" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Ĉi tio signifas, ke ĝi povas esti uzata de %s por ricevi kaj sendi ĉifritajn " "mesaĝojn." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "konfirmita" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Krome ĝi estis kontrolita pri kongruo kun la ŝlosilo ĉe la aparato de la " "kontakto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rifuzita" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Tio signifas, ke ĝi ne povas esti uzata de %s por malĉifri viajn mesaĝojn, " "kaj vi ne vidos mesaĝojn ĉifritajn per ĝi." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Vi ne vidos ĉifritajn mesaĝojn el la aparato de %s kiu uzas ĉi tiun " "ŝlosilon. Male, tiu aparato ne plu povos malĉifri viajn mesaĝojn." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Vi povos interŝanĝi ĉifritajn mesaĝojn kun la aparato de %s kiu uzas ĉi tiun " "ŝlosilon." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Reen" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Administri" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ĉi tiu kontakto havas novajn aparatojn" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Necesas OMEMO-decido pri fidindeco" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ĉu vi aldonis novan aparaton por la konto %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Novaj ŝlosiloj" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novaj ĉifraj ŝlosiloj de viaj aliaj aparatoj estos aŭtomate akceptitaj." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Akceptita" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rifuzita" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Konfirmita" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Administrado de OMEMO-Ŝlosiloj" #, fuzzy #~ msgid "Encryption" #~ msgstr "Kodo" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-aparato" #~ msgstr[1] "%d OMEMO-aparatoj" #~ msgid "Automatically accept new keys" #~ msgstr "Aŭtomate akcepti novajn ŝlosilojn" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Novaj ĉifraj ŝlosiloj de ĉi tiu kontakto estos aŭtomate akceptitaj." #~ msgid "Own key" #~ msgstr "Propra ŝlosilo" #~ msgid "Associated keys" #~ msgstr "Asociitaj ŝlosiloj" #~ msgid "Inactive keys" #~ msgstr "Neaktivaj ŝlosiloj" #~ msgid "Unused" #~ msgstr "Ne uzata" #~ msgid "Own fingerprint" #~ msgstr "Propra fingropremaĵo" #~ msgid "Will be generated on first connection" #~ msgstr "Estos farita dum unua konekto" dino-0.5.0/plugins/omemo/po/es.po0000664000000000000000000003040514776241610015341 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-01-29 01:02+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s ha estado usando un dispositivo poco fiable. No verá mensajes de " "dispositivos en los que no confíe." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gestionar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s no confía en este dispositivo. Eso significa que puede que te falten " "mensajes." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestionar Clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compara la huella digital, carácter a carácter, con la huella digital " "mostrada en el dispositivo de tus contactos." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Las huellas digitales son diferente" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Las huellas digitales son idénticas" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Los mensajes futuros enviados por %s desde el dispositivo que usa esta clave " "serán resaltado en consecuencia en la ventana de conversación." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Huellas digitales no coinciden" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor, verifica que estás comparando la huella digital correcta. Si la " "huella digital no coincide con la cuenta %s puede que la clave esté " "comprometida y deberías considerar rechazar esta clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar Huella Digital" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compara esta huella digital con la huella digital mostrada en el dispositivo " "de tu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rechazar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquear comunicación cifrada con el dispositivo del contacto que usa esta " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Aceptar clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permitir comunicación cifrada con el dispositivo del contacto que usa esta " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Esta clave está actualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Esto significa que puede ser usada por %s para recibir y enviar mensajes " "cifrados." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Además, ha sido verificado que las claves coinciden con la clave del " "dispositivo de tu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rechazada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Esto significa que no puede ser usada por %s para descifrar tus mensajes, y " "tú no verás los mensajes cifrados con ella." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "No verás los mensajes cifrados desde el dispositivo de %s que usa esta " "clave. A la inversa, ese dispositivo ya no podrá descifrar tus mensajes." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Tu podrás intercambiar mensajes cifrados con el dispositivo de %s que usa " "esta clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Volver" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestionar" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Este contacto tiene un nuevo dispositivo" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisión de confianza requerida para clave OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "¿Añadiste un nuevo dispositivo para la cuenta %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO por defecto" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Habilitar el cifrado de OMEMO para nuevas conversaciones" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Cifrar para nuevos dispositivos" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Cifrar automáticamente los nuevos dispositivos desde este contacto." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nuevas claves" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Las nuevas claves de cifrado de tus otros dispositivos serán aceptadas " "automáticamente." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Aceptada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rechazada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Dispositivo nuevo" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Huellas digitales OMEMO" #~ msgid "Encryption" #~ msgstr "Encriptación" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispositivo OMEMO" #~ msgstr[1] "%d dispositivos OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Automáticamente aceptar nuevas claves" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Las nuevas claves de cifrados para este contacto serán aceptadas " #~ "automáticamente." #~ msgid "Own key" #~ msgstr "Mi clave" #~ msgid "Associated keys" #~ msgstr "Claves asociadas" #~ msgid "Inactive keys" #~ msgstr "Claves inactivas" #~ msgid "Unused" #~ msgstr "Sin usar" #~ msgid "Own fingerprint" #~ msgstr "Tu huella digital" #~ msgid "Will be generated on first connection" #~ msgstr "Será generada en la primera conexión" #~ msgid "Your contact" #~ msgstr "Tu contacto" #~ msgid "Not matching" #~ msgstr "No coincide" #~ msgid "Matching" #~ msgstr "Coincide" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Una vez confirmada, cualquier futuro mensaje enviado por %s usando esta " #~ "clave será resaltado en el ventana de conversación." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dejar de aceptar esta clave durante la comunicación con este contacto." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Aceptar esta clave durante la comunicación con este contacto" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Esto significa que no puede ser usada por %s para recibir mensajes, y " #~ "cualquier mensaje enviado con esta clave será ignorado." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Una vez confirmado, cualquier futuro mensaje enviado por %s usando esta " #~ "clave será ignorado y ninguno de tus mensajes será legible usando esta " #~ "clave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Una vez confirmada esta clave será usable por %s para recibir y enviar " #~ "mensajes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Cuando este contacto añada nuevas claves de cifrado a su cuenta, " #~ "automáticamente aceptarlas." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Cuando añades nuevas claves de cifrado a tu cuenta, aceptarlas " #~ "automáticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "Aceptar esta clave durante la comunicación con este contacto." #~ msgid "Reject Key" #~ msgstr "Rechazar clave" #~ msgid "Accept Key" #~ msgstr "Aceptar clave" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo (0x%.8x) desconocido" #~ msgid "Other devices" #~ msgstr "Otros dispositivos" #~ msgid "- None -" #~ msgstr "- Ninguno -" #~ msgid "OMEMO" #~ msgstr "OMEMO" dino-0.5.0/plugins/omemo/po/et.po0000664000000000000000000002331314776241610015342 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: none\n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s kasutab seadet, mille suhtes puudub usaldus. Sa ei näe sõnumeid " "seadmetest, mida sa ei usalda." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Halda seadmeid" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s ei usalda seda seadet. See tähendab, et sa ilmselt ei näe kõiki sõnumeid." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Halda võtmeid" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Võrdle tähemärkide haaval seda sõrmejälge tollega, mida näidatakse teise " "osapoole seadmes." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Sõrmejäljed erinevad" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Sõrmejäljed on samad" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Katkesta" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Kinnita" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifitseeri võtmed" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Kui kasutaja %s saadab tulevikus sõnumeid seadmest, mis kasutab seda " "krüptovõtit, siis need on ka vestlusaknas vastavalt märgitud." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Sõrmejäljed on erinevad" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Palun kontrolli, et sa kindlasti võrdleid õigeid sõrmejälgi. Kui sõrmejäljed " "siiski ei klapi, siis võib %s kasutajakonto olla rääderdatud ja sa peaksid " "sellise võtme lubamisest keelduma." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifitseeri võtme sõrmejälge" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Võrdle seda sõrmejälge tollega, mida näidatakse teise osapoole seadmes." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Keeldu võtme kasutamisest" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Sellega sa keelad krüptitud suhtluse sinu vestluspartneri seadmega, mis " "kasutab seda krüptovõtit." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Nõustu võtme kasutamisega" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Sellega sa lubad krüptitud suhtluse sinu vestluspartneri seadmega, mis " "kasutab seda krüptovõtit." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "See krüptovõti on praegu %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "tunnustatud" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "See tähendab, et %s võib teda kasutada krüptitud sõnumite saatmisel ja " "vastuvõtmisel." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verifitseeritud" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Lisaks oled sa kontrollinud, et ta vastab krüptovõtmele teise osapoole " "seadmes." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "keelatud" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "See tähendab, et %s ei saa teda kasutada sinu krüptitud sõnumite lugemisel " "ning ka sina ei näe selle võtmega krüptitud sõnumeid." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Sa ei näe %s krüptitud sõnumeid seadmetest, mis kasutavad seda krüptovõtit. " "Ja teistpidi pole ka seal seadmes võimalik lugeda sinu krüptitud sõnumeid." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Sa saad nüüd vahetada krüptitud sõnumeid %s seadmetega, kus on kasutusel see " "krüptovõti." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tagasi" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Halda" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Sellel kasutajal on uusi seadmeid" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Pead otsustama OMEMO-põhise usalduse osas" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Kas sa lisasid uue seadme kasutajakontole %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO kasutamine vaikimisi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Kasuta OMEMO krüptimist uute vestluste puhul" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Krüpti uute seadmete jaoks" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Krüpti automaatselt selle kasutaja uute seadmete puhul." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Uued krüptovõtmed" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Sinu muude seadmete uute krüptovõtmetega nõustutakse automaatselt." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "See seade" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Muu seade" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Seade" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Tunnustatud" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Keelatud" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verifitseeritud" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Uus seade" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Igal seadmel on oma OMEMO-võti. Sõnumeid on võimalik dekrüptida vaid " "konkreetse sihtvõtme jaoks. Sõnumeid on võimalik krüptida vaid seadmete " "jaoks, millega oled nõustunud." #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO võtmehaldus" #~ msgid "Encryption" #~ msgstr "Krüptimine" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO seade" #~ msgstr[1] "%d OMEMO seadet" #~ msgid "Automatically accept new keys" #~ msgstr "Nõustu uute võtmetega automaatselt" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Sa nõustud selle kasutaja uute krüptovõtmetega automaatselt." #~ msgid "Own key" #~ msgstr "Oma krüptovõti" #~ msgid "Associated keys" #~ msgstr "Seotud krüptovõtmed" #~ msgid "Inactive keys" #~ msgstr "Mitteaktiivsed krüptovõtmed" #~ msgid "Unused" #~ msgstr "Kasutamata" dino-0.5.0/plugins/omemo/po/eu.po0000664000000000000000000002775314776241610015357 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-01-17 17:56+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s erabiltzaileak fidagarritasun gutxiko gailu bat erabiltzen egon da. Ez " "duzu mezurik ikusiko konfiantza ematen ez dizuten gailuengandik." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gailuak kudeatu" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s erabiltzailea ez da gailu honetaz fidatzen. Honek mezu batzuk gal " "ditzakezula esan nahi du." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gakoa kudeatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Hatz marka konparatu, hizkiz hizki, zure kontaktuaren gailuan ageri " "denarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Hatz markak ez datoz bat" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Hatz markak bat datoz" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Utzi" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Baieztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Gakoa egiaztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "%s erabiltzaileak etorkizunean gako hau erabiltzen duen gailutik bidalitako " "mezuak nabarmenduko dira txat leihoan." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Hatz markak ez datoz bat" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Mesedez egiaztatu ezazu hatz marka egokia konparatzen ari zarela. Hatz " "markak bat ez badatoz baliteke %s(e)ren kontua arriskuan egotea eta gako hau " "ez onartzea kontuan hartu beharko zenuke." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Gakoaren hatz marka egiaztatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Konpara ezazu gako honen hatz marka kontaktuaren gailuan agertzen den hatz " "markarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Gakoa ukatu" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Enkriptatutako komunikazioa blokeatu gako hau erabiltzen duen kontaktuaren " "gailuarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Gakoa onartu" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Enkriptatutako komunikazioa baimendu gako hau erabiltzen duen kontaktuaren " "gailuarekin." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Gako hau %s dago une honetan." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "onartuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Horrek esan nahi du %s-ek erabil dezakeela mezu zifratuak jasotzeko eta " "bidaltzeko." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "egiaztatuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Gainera zure kontaktuaren gailuan ageri den gakoarekin bat datorrela " "egiaztatu da." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ukatuta" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Honek esan nahi du %s erabiltzaileak ezin duela erabili zure mezuak " "deszifratzeko, eta zuk ez dituzula gako honekin enkriptatutako mezuak " "ikusiko." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Ez dituzu ikusiko gako hau erabiltzen duen %s kontaktuaren gailutik " "igorritako mezuak. Era berean, hemendik aurrera gailu horrek ez da zure " "mezuak deszifratzeko gai izango." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Gako hau erabiltzen duen %s kontaktuaren gailuarekin enkriptatutako mezuak " "elkar-trukatzeko gai izango zara." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atzera" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Kudeatu" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Kontaktu honek gailu berriak ditu" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO konfiantza erabakia behar da" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Gailu berri gehitu duzu %s konturako?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Gako berriak" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Zure beste gailuen enkriptatze gako berriak onartuak izango dira " "automatikoki." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Onartuta" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Ukatuta" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Egiaztatuta" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO gakoen kudeaketa" #~ msgid "Encryption" #~ msgstr "Enkriptazioa" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "OMEMO gailu %d" #~ msgstr[1] "%d OMEMO gailu" #~ msgid "Automatically accept new keys" #~ msgstr "Gako berriak automatikoki onartu" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Kontaktu honen enkriptatze gako berriak onartuak izango dira automatikoki." #~ msgid "Own key" #~ msgstr "Norberaren gakoa" #~ msgid "Associated keys" #~ msgstr "Lotutako gakoak" #~ msgid "Inactive keys" #~ msgstr "Gako ez aktiboak" #~ msgid "Unused" #~ msgstr "Ez erabilita" #~ msgid "Own fingerprint" #~ msgstr "Norberaren hatz marka" #~ msgid "Will be generated on first connection" #~ msgstr "Lehen konexioan sortuko da" #~ msgid "Not matching" #~ msgstr "Ez datoz bat" #~ msgid "Matching" #~ msgstr "Bat datoz" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Egiaztatu ondoren, %s(e)k gako honekin etorkizunean bidalitako mezu " #~ "guztiak behar bezala nabarmenduko dira txat leihoan." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Utzi gako hau onartzeari lotuta dagoen kontaktuarekin hitz egiten duzun " #~ "bitartean." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Hasi gako hau onartzen lotuta dagoen kontaktuarekin hitz egiten duzun " #~ "bitartean" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Honek esan nahi du %s(e)k ezin duela gakoa mezuak jasotzeko erabili, eta " #~ "berak bidalitako edozein mezu alde batera utziko da." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Egiaztatu ondoren, etorkizunean %s(e)k gako hau erabiliz bidalitako " #~ "edozein mezu alde batera utziko da eta zure mezuetako bat ere ez da " #~ "irakurgarria izango gako hau erabiliz." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Egiaztatu ondoren %s(e)k gako hau mezuak jaso eta bidaltzeko erabili ahal " #~ "izango du." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Kontaktu honek enkriptatzeko gako berriak bere kontura gehitzen " #~ "dituenean, automatikoki onartu." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Zure kontura enkriptazio gako berriak gehitzen dituzunean, automatikoki " #~ "onartu." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Hasi gako hau onartzen lotuta dagoen kontaktuarekin hitz giten duzun " #~ "bitartean." #~ msgid "Reject Key" #~ msgstr "Gakoa baztertu" #~ msgid "Accept Key" #~ msgstr "Gakoa onartu" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Gailu ezezaguna (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Beste gailuak" #~ msgid "- None -" #~ msgstr "- Bat ere ez -" dino-0.5.0/plugins/omemo/po/fa.po0000664000000000000000000002545314776241610015327 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-08-10 03:32+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s از دستگاهی استفاده می‌کند که اعتبارش را تأیید نکرده‌اید، پیام‌ها از چنین " "دستگاه‌هایی قابل مشاهده نیستند." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "مدیریت دستگاه‌ها" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s اعتبار این دستگاه را تأیید نکرده است. این یعنی، ممکن است پیام‌هایی را " "دریافت نکنید/نکرده باشید." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "مدیریت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "اثرانگشت را با آنی که در دستگاه مخاطبتان است مقایسه کنید، کاراکتر به کاراکتر." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "اثرانگشت‌ها متفاوت‌اند" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "اثرانگشت‌ها تطابق دارند" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "لغو" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "تأیید" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "تأیید صحت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "پیام‌های ارسالی توسط %s از دستگاهی که از این کلید استفاده می‌کند، مطابق آن در " "پنجرهٔ گپ مشخص می‌شوند." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "اثرانگشت‌ها تطابق ندارند" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "لطفاً مطمئن شوید که در حال مقایسهٔ اثرانگشت صحیح هستید. اگه اثرانگشت‌ها تطابق " "نداشته باشند، ممکن است اطلاعات حساب %s لو رفته باشد و باید رد کردن این کلید " "را در نظر بگیرید." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "تأیید صحت اثرانگشت کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "اثرانگشت این کلید را با اثرانگشت نمایش‌داده‌شده در دستگاه مخاطب مقایسه کنید." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "رد کردن کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "ارتباط رمزگذاری‌شده را با دستگاهی که از این کلید استفاده می‌کند، مسدود کنید." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "پذیرش کلید" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "اجازهٔ ارتباط رمزگذاری‌شده را با دستگاهی که از این کلید استفاده می‌کند، بدهید." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "این کلید در حال حاضر %s است." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "پذیرفته شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "این یعنی %s می‌تواند از آن برای ارسال و دریافت پیام رمزگذاری‌شده استفاده کند." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "تأیید صحت شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "به علاوه تأیید شده که با کلید دستگاه مخاطب تطابق دارد." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "رد شده" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "این یعنی %s نمی‌تواند با آن پیام‌های شما را رمزگشایی کند، و شما پیام‌های " "رمزگذاری‌شده با آن را نخواهید دید." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "شما پیام‌های رمزگذاری‌شده را از آن دستگاهِ %s که از این کلید استفاده می‌کند، " "نخواهید دید. از سوی دیگر، آن دستگاه نیز پس از این قادر به رمزگشایی پیام‌های " "شما نخواهد بود." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "شما قادر به تبادل پیام‌های رمزگذاری‌شده با آن دستگاهِ %s که از این کلید استفاده " "می‌کند، هستید." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "بازگشت" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "مدیریت" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "این مخاطب دستگاه‌های جدیدی دارد" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "تصمیم اعتبار اُمیمو لازم است" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "آیا برای حساب %s یک دستگاه جدید اضافه کرده‌اید؟" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "کلیدهای جدید" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "کلیدهای رمزگذاری جدید از دستگاه‌های دیگرتان به طور خودکار پذیرفته می‌شوند." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "پذیرفته شده" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "رد شده" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "تأیید صحت شده" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "مدیریت کلید اُمیمو" #~ msgid "Encryption" #~ msgstr "رمزگذاری" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d دستگاه OMEMO" #~ msgstr[1] "%d دستگاه OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "پذیرش خودکار کلیدهای جدید" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "کلیدهای جدید این مخاطب به طور خودکار پذیرفته می‌شوند." #~ msgid "Own key" #~ msgstr "کلید خود" #~ msgid "Associated keys" #~ msgstr "کلیدهای مرتبط" #~ msgid "Inactive keys" #~ msgstr "کلیدهای غیرفعال" #~ msgid "Unused" #~ msgstr "استفاده نشده" #~ msgid "Own fingerprint" #~ msgstr "اثرانگشت خود" #~ msgid "Will be generated on first connection" #~ msgstr "در اولین اتصال تولید خواهد شد" dino-0.5.0/plugins/omemo/po/fi.po0000664000000000000000000002311414776241610015327 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-02 11:09+0000\n" "Language-Team: none\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s on käyttänyt laitetta, johon ei luoteta. Et näe viestejä laitteilta, " "joihin et luota." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Hallitse laitteita" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s ei luota tähän laitteeseen. Sen seurauksena viestejä saattaa jäädä " "puuttumaan." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Hallitse avainta" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vertaa sormenjälkeä merkki kerrallaan vastapuolen laitteella näkyvään " "sormenjälkeen." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Sormenjäljet eroavat" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Sormenjäljet vastaavat" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Peru" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Vahvista" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Vahvista avain" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Tulevat viestit käyttäjän %s laitteelta, joka käyttää tätä avainta, " "korostetaan asianmukaisesti keskusteluikkunassa." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Sormenjäljet eivät vastaa toisiaan" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Varmista, että vertaat oikeita sormenjälkiä. Jos sormenjäljet eivät täsmää, " "käyttäjän %s tili saattaa olla vaarantunut, ja sinun tulisi hylätä tämä " "avain." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Vahvista avaimen sormenjälki" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Vertaa tämän avaimen sormenjälkeä vastapuolen laitteella näkyvään " "sormenjälkeen." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Hylkää avain" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Estä salattu yhteydenpito käyttäjän tätä avainta käyttävän laitteen kanssa." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Hyväksy avain" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Salli salattu yhteydenpito käyttäjän tätä avainta käyttävän laitteen kanssa." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Tämä avain on tällä hetkellä %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "hyväksytty" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Se tarkoittaa, että %s voi käyttää avainta salattujen viestien " "vastaanottamiseen ja lähettämiseen." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "vahvistettu" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Lisäksi sen on vahvistettu vastaavan vastapuolen laitteen avainta." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "hylätty" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Se tarkoittaa, ettei %s voi käyttää avainta viestien salauksen purkamiseksi, " "etkä sinä näe tällä avaimella salattuja viestejä." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Et näe käyttäjän %s laitteelta, joka käyttää tätä avainta, lähetettyjä " "salattuja viestejä. Vastavuoroisesti kyseinen laite ei pysty enää purkamaan " "viestiesi salausta." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Pystyt viestimään salatuilla viesteillä käyttäjän %s tätä avainta käyttävän " "laitteen kanssa." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Takaisin" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Hallitse" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Tällä henkilöllä on uusia laitteita" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-luottamuspäätös vaaditaan" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Lisäsitkö uuden laitteen tilille %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO oletuksena" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Käytä OMEMO-salausta uusissa keskusteluissa" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Salaa uusille laitteille" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Salaa automaattisesti tämän henkilön uusille laitteille." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Uudet avaimet" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Uudet salausavaimet muilta laitteiltasi hyväksytään automaattisesti." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Hyväksytty" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Hylätty" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Vahvistettu" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Uusi laite" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO-avainhallinta" #~ msgid "Encryption" #~ msgstr "Salaus" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-laite" #~ msgstr[1] "%d OMEMO-laitetta" #~ msgid "Automatically accept new keys" #~ msgstr "Hyväksy uudet avaimet automaattisesti" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Uudet salausavaimet tältä henkilöltä hyväksytään automaattisesti." #~ msgid "Own key" #~ msgstr "Oma avain" #~ msgid "Associated keys" #~ msgstr "Liittyvät avaimet" #~ msgid "Inactive keys" #~ msgstr "Epäaktiiviset avaimet" #~ msgid "Unused" #~ msgstr "Käyttämätön" #~ msgid "Own fingerprint" #~ msgstr "Oma sormenjälki" #~ msgid "Will be generated on first connection" #~ msgstr "Luodaan ensimmäisellä yhdistyskerralla" dino-0.5.0/plugins/omemo/po/fr.po0000664000000000000000000003041314776241610015340 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-03-11 16:12+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.10.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilisé un client inconnu. Vous ne verrez pas les messages provenant de " "clients en lesquels vous n’avez pas confiance." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gérer les appareils" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s n’accorde pas sa confiance à cet appareil. Cela signifie que vous manquez " "peut-être des messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gérer la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparez l’empreinte, lettre par lettre, avec celle affichée sur l'appareil " "de votre contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "L’empreinte ne correspond pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "L’empreinte correspond" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annuler" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmer" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Vérifier la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Les prochains messages envoyés par %s depuis l'appareil utilisant cette clé " "seront mis en valeur dans la fenêtre de discussion." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Les empreintes ne correspondent pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Veuillez vérifier que vous comparez la bonne empreinte. Si les empreintes ne " "correspondent pas, le compte de %s pourrait être compromis et vous devriez " "envisager de rejeter cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Vérifier l’empreinte de cette clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparez cette empreinte avec celle affichée sur l'appareil du contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rejeter cette clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquer les communications chiffrées avec l'appareil du contact qui utilise " "cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Accepter la clé" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Autoriser les communications chiffrées avec l'appareil du contact qui " "utilise cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Cette clé est actuellement %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptée" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Cela signifie qu’elle peut être utilisée par %s pour recevoir et envoyer des " "messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "vérifiée" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "De plus, vous avez précédemment vérifié que cette clé coïncide avec celle de " "l'appareil de ce contact." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejetée" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Cela signifie qu’elle ne peut pas être utilisée par %s pour déchiffrer vos " "messages, et que vous ne verrez pas les messages chiffrés avec." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Vous ne verrez pas les messages de %s provenant de l'appareil qui utilise " "cette clé. En retour, ce client ne pourra plus déchiffrer vos messages." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Vous pourrez échanger des messages chiffrés avec l'appareil de %s qui " "utilise cette clé." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Retour" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gérer" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ce contact a de nouveaux appareils" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Décision de confiance OMEMO requise" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Avez-vous ajouté un nouvel appareil pour le compte %s ?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO par défaut" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Activer le chiffrement OMEMO pour les nouvelles discussions" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Chiffrer pour les nouveaux appareils" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Chiffrer automatiquement pour les nouveaux appareils de ce contact." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nouvelles clés" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Les nouvelles clés de chiffrement de vos autres appareils seront acceptées " "automatiquement." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Acceptée" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rejetée" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Vérifiée" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Nouvel appareil" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gestion des clés OMEMO" #~ msgid "Encryption" #~ msgstr "Chiffrement" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d appareil OMEMO" #~ msgstr[1] "%d appareils OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Accepter automatiquement les nouvelles clés" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Les nouvelles clés de chiffrement de ce contact seront acceptées " #~ "automatiquement." #~ msgid "Own key" #~ msgstr "Clé publique personnelle" #~ msgid "Associated keys" #~ msgstr "Clés associées" #~ msgid "Inactive keys" #~ msgstr "Clés inactives" #~ msgid "Unused" #~ msgstr "Inutilisée" #~ msgid "Own fingerprint" #~ msgstr "Empreinte publique" #~ msgid "Will be generated on first connection" #~ msgstr "Sera générée lors de la première connexion" #~ msgid "Your contact" #~ msgstr "Votre contact" #~ msgid "Not matching" #~ msgstr "Ne correspond pas" #~ msgid "Matching" #~ msgstr "Correspond" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Après confirmation, tout futur message envoyé par %s utilisant cette clé " #~ "sera surligné dans la fenêtre de conversation." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Arrêter d'accepter cette clé durant la communication avec son contact " #~ "associé." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Accepter cette clé durant la communication avec son contact associé" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Cela signifie qu'elle ne peut pas être utilisée par %s pour recevoir des " #~ "messages, et que les messages envoyés seront ignorés." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Après confirmation, tout futur message envoyé par %s utilisant cette clé " #~ "sera ignoré et vos messages ne seront pas lisibles en utilisant cette clé." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Après confirmation, cette clé sera utilisable par %s pour recevoir et " #~ "envoyer des messages." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quand ce contact ajoute une nouvelle clé de chiffrement à son compte, " #~ "l'accepter automatiquement." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Accepter automatiquement les nouvelles clés de chiffrements que vous " #~ "ajoutez à votre compte." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepter cette clé durant la communication avec son contact associé." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Périphérique inconnu (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Autres périphériques" #~ msgid "- None -" #~ msgstr "- Aucun -" dino-0.5.0/plugins/omemo/po/gl.po0000664000000000000000000003050314776241610015333 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s estivo utilizando un dispositivo non verificado. Non verás mensaxes " "procedentes de dispositivos nos que non confiaches." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Xestionar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s non mostrou confianza neste dispositivo. Porén, poderías botar a faltar " "algunha mensaxe." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Xestionar Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparar impresión dixital, caracter a caracter, coa mostrada no dispositivo " "do teu contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impresións dixitais son diferentes" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impresións coinciden" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "As mensaxes futuras enviadas por %s desde o dispositivo que usa esta chave " "será resaltadas na xanela da conversa." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Non coinciden as impresións dixitais" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Comproba que estás comparando a impresión correcta. Se as impresións " "dixitais non coinciden, a conta de %s podería estar comprometida e deberías " "considerar rexeitar esta chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar a impresión dixital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compara a impresión dixital desta chave coa impresión mostrada no " "dispositivo do contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rexeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloquear a comunicación cifrada co dispositivo do contacto que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Aceptar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permitir a comunicación cifrada co dispositivo do contacto que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Esta chave está actualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Esto significa que pode ser utilizada por %s para recibir e enviar mensaxes " "cifradas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verficada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Adicionalmente foi verificada e coincide coa chave no dispositivo do " "contacto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rexeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Esto significa que non pode ser usada por %s para descifrar as túas " "mensaxes, e non verás as mensaxes cifradas con ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Non verás as mensaxes cifradas desde o dispositivo de %s que usa esta chave. " "De xeito recíproco, ese dispositivo non poderá descifrar as túas." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Poderás intercambiar mensaxes cifradas co dispositivo de %s que usa esta " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atrás" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Xestionar" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Este contacto ten novos dispositivos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Precisa decidir sobre a confianza OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Engadiches un novo dispositivo para a conta %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO por defecto" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Activar cifraxe OMEMO para as novas conversas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Cifrar para novos dispositivos" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Cifrar automaticamente para os novos dispositivos de este contacto." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Novas chaves" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Aceptaranse de xeito automático novas chaves de cifraxe do teus dispositivos." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Este dispositivo" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Outro dispositivo" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Dispositivo" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Aceptada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rexeitada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Novo dispositivo" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Cada dispositivo ten a súa propia clave OMEMO. As mensaxes só se poden " "descifrar se están cifradas coas claves deles. As mensaxes só se cifran para " "os dispositivos aceptados." #~ msgid "OMEMO Key Management" #~ msgstr "Xestión das chaves OMEMO" #~ msgid "Encryption" #~ msgstr "Cifraxe" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispositivo OMEMO" #~ msgstr[1] "%d dispositivos OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Aceptar automáticamente novas chaves" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Serán aceptadas de xeito automático novas chaves de cifraxe deste " #~ "contacto." #~ msgid "Own key" #~ msgstr "Chave propia" #~ msgid "Associated keys" #~ msgstr "Chaves asociadas" #~ msgid "Inactive keys" #~ msgstr "Chaves non activas" #~ msgid "Unused" #~ msgstr "Sen usar" #~ msgid "Own fingerprint" #~ msgstr "Impresión dixital propia" #~ msgid "Will be generated on first connection" #~ msgstr "Crearase na primeira conexión" #~ msgid "Your contact" #~ msgstr "O teu contacto" #~ msgid "Not matching" #~ msgstr "Non coinciden" #~ msgid "Matching" #~ msgstr "Coinciden" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Unha vez confirmada, as futuras mensaxes enviadas por %s utilizando esta " #~ "chave será resaltada acorde na ventá de conversa." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Deixar de aceptar esta chave durante a comunicación co contacto asociado " #~ "a ela." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Comezar aceptando esta chave durante a comunicación co seu contacto " #~ "asociado" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Esto significa que non pode ser utilizada por %s para recibir mensaxes, e " #~ "calquera mensaxe enviada con ela será ignorada." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Unha vez confirmada, calquera mensaxe futura enviada por %s utilizando " #~ "esta chave será ignorada e ningunha das súas mensaxes serán lexibles " #~ "usando esta chave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Unha vez confirmada esta chave será utilizable por %s para recibir e " #~ "enviar mensaxes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Cando este contacto engade novas chaves de cifrado a súa conta, aceptalas " #~ "automáticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Cando engades novas chaves de cifrado a túa conta, aceptalas " #~ "automáticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Comezar a aceptar esta chave nas comunicacións co contacto asociado a ela." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo descoñecido (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Outros dispositivos" #~ msgid "- None -" #~ msgstr "-Nada-" dino-0.5.0/plugins/omemo/po/hu.po0000664000000000000000000002456514776241610015360 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s nem megbízható eszközt használt. Nem fogja látni az olyan eszközökről " "érkező üzeneteket, amelyekben nem bízik meg." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Eszközök kezelése" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nem bízik meg ebben az eszközben. Ez azt jelenti, hogy lemaradhat néhány " "üzenetről." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Kulcs kezelése" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Hasonlítsa össze az ujjlenyomatot karakterről karakterre a partnere eszközén " "láthatóval." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Az ujjlenyomatok eltérnek" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Az ujjlenyomatok egyeznek" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Mégse" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Megerősítés" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Kulcs ellenőrzése" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "%s által az ezt a kulcsot használó eszközről küldött jövőbeli üzenetek ennek " "megfelelően lesznek kiemelve a csevegésablakban." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Az ujjlenyomatok nem egyeznek" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ellenőrizze, hogy a megfelelő ujjlenyomatot hasonlítja-e össze. Ha az " "ujjlenyomatok nem egyeznek, akkor %s fiókja veszélybe kerülhetett, és " "fontolóra kellene vennie a kulcs elutasítását." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Kulcs ujjlenyomatának ellenőrzése" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Hasonlítsa össze ennek a kulcsnak az ujjlenyomatát a partner eszközén " "megjelenített ujjlenyomattal." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Kulcs elutasítása" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "A titkosított kommunikáció tiltása a partner eszközével, amely ezt a kulcsot " "használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Kulcs elfogadása" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "A titkosított kommunikáció engedélyezése a partner eszközével, amely ezt a " "kulcsot használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ez a kulcs jelenleg %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "elfogadott" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Ez azt jelenti, hogy %s használhatja titkosított üzenetek fogadásához és " "küldéséhez." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "ellenőrzött" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Ezenkívül ellenőrizve lett, hogy a kulcs egyezik a partner eszközén." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "elutasított" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Ez azt jelenti, hogy %s nem használhatja az Ön üzeneteinek visszafejtésére, " "és Ön nem fogja látni az azzal titkosított üzeneteket." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nem fogja látni %s azon eszközéről érkező titkosított üzeneteket, amely ezt " "a kulcsot használja. Fordítva, a másik eszköz nem lesz képes többé " "visszafejteni az Ön üzeneteit." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Ön képes lesz titkosított üzeneteket váltani %s azon eszközével, amely ezt a " "kulcsot használja." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Vissza" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Kezelés" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ennek a partnernek új eszközei vannak" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO megbízhatósági döntés szükséges" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hozzáadott egy új eszközt a(z) %s fiókhoz?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO alapértelmezetten" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "OMEMO-titkosítás engedélyezése az új beszélgetésekhez" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Titkosítás az új eszközökre" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automatikus titkosítás az ettől a partnertől származó új eszközökre." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Új kulcsok" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Az egyéb eszközeitől származó új titkosítási kulcsok automatikusan el " "lesznek fogadva." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Ez az eszköz" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Másik eszköz" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Eszköz" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Elfogadva" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Elutasítva" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Ellenőrizve" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Új eszköz" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Minden eszköznek saját OMEMO-kulcsa van. Egy eszköz csak akkor tudja " "visszafejteni az üzeneteket, ha azok a saját kulcsával lettek titkosítva. Az " "üzenetek csak az elfogadott eszközökre vannak titkosítva." #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO kulcskezelés" #~ msgid "Encryption" #~ msgstr "Titkosítás" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-eszköz" #~ msgstr[1] "%d OMEMO eszköz" #~ msgid "Automatically accept new keys" #~ msgstr "Új kulcsok automatikus elfogadása" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Az ettől a partnertől származó új titkosítási kulcsok automatikusan el " #~ "lesznek fogadva." #~ msgid "Own key" #~ msgstr "Saját kulcs" #~ msgid "Associated keys" #~ msgstr "Hozzárendelt kulcsok" #~ msgid "Inactive keys" #~ msgstr "Inaktív kulcsok" #~ msgid "Unused" #~ msgstr "Nem használt" #~ msgid "Own fingerprint" #~ msgstr "Saját ujjlenyomat" #~ msgid "Will be generated on first connection" #~ msgstr "Az első kapcsolódáskor lesz előállítva" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Ismeretlen eszköz (0x%.8x)" #~ msgid "Other devices" #~ msgstr "További eszközök" #~ msgid "- None -" #~ msgstr "- Nincs -" dino-0.5.0/plugins/omemo/po/id.po0000664000000000000000000002244514776241610015333 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-01-10 15:32+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s telah menggunakan perangkat tidak dikenal. Anda tidak akan melihat pesan " "dari perangkat yang tidak Anda kenal." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Atur perangkat" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s tidak mengenali perangkat ini. Artinya, Anda mungkin kehilangan pesan." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Atur kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Bandingkan sidik jari karakter demi karakter, dengan yang tampil di " "perangkat kontak Anda." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Sidik jari berbeda" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Sidik jari cocok" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Batal" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Konfirmasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifikasi kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Pesan mendatang yang dikirim oleh %s dari perangkat yang menggunakan kunci " "ini akan ditandai di halaman percakapan." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Sidik jari tidak cocok" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Harap Anda memverifikasi sidik jari dengan benar. Jika sidik jari tidak " "cocok, akun %s mungkin telah disusupi dan pertimbangkan untuk menolak kunci " "ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifikasi sidik jari kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Bandingkan sidik jari kunci ini dengan sidik jari yang ditampilkan perangkat " "kontak." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Tolak kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokir komunikasi terenkripsi dengan perangkat kontak yang menggunakan kunci " "ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Terima kunci" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Izinkan komunikasi terenkripsi dengan perangkat kontak yang menggunakan " "kunci ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Status kunci saat ini %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "diterima" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Artinya dapat digunakan oleh %s untuk menerima dan mengirim pesan " "terenkripsi." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "Terverifikasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Telah terverifikasi dengan mencocokkan kunci pada perangkat kontak." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ditolak" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Artinya tidak dapat digunakan oleh %s untuk menguraikan pesan Anda, dan Anda " "tidak akan melihat pesan dienkripsi dengannya." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Anda tidak akan melihat pesan terenkripsi dari perangkat %s yang menggunakan " "kunci ini. Sebaliknya, perangkat itu tidak dapat lagi menguraikan pesan Anda." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Anda dapat bertukar pesan terenkripsi dengan perangkat %s yang menggunakan " "kunci ini." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Kembali" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Pengaturan" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Kontak memiliki perangkat baru" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Memerlukan persetujuan atas OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Apakah Anda menambahkan perangkat baru untuk akun %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Kunci baru" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Kunci enkripsi baru dari perangkat Anda yang lain akan diterima secara " "otomatis." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Diterima" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Ditolak" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Terverifikasi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Pengaturan kunci OMEMO" #~ msgid "Encryption" #~ msgstr "Enkripsi" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO perangkat" #~ msgid "Automatically accept new keys" #~ msgstr "Otomatis mengkonfirmasi kunci baru" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Kunci enkripsi baru dari kontak ini akan diterima secara otomatis." #~ msgid "Own key" #~ msgstr "Kunci milik sendiri" #~ msgid "Associated keys" #~ msgstr "Kunci terkait" #~ msgid "Inactive keys" #~ msgstr "Kunci non-aktif" #~ msgid "Unused" #~ msgstr "Tidak terpakai" #~ msgid "Own fingerprint" #~ msgstr "Sidik jari sendiri" #~ msgid "Will be generated on first connection" #~ msgstr "Akan dibuat pada koneksi pertama" #~ msgid "Your contact" #~ msgstr "Kontak anda" dino-0.5.0/plugins/omemo/po/ie.po0000664000000000000000000002531714776241610015335 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-03-01 10:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.5\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gerer aparates" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s ne have fide por ti-ci aparate. Vu fórsan manca alcun missages." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerer li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Cuidosi compara li fingre-print con ti que es monstrat sur li aparate de vor " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingre-printes difere" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingre-printes es identic" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anullar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingre-printes ne es egal" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ples verificar que vu compara li just fingre-print. Si fingre-printes ne es " "egal, li conto de %s posse esser compromettet e vu deve rejecter ti-ci clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar li fingre-print" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparar li fingre-print de ti-ci clave con clave que es monstrat sur li " "aparate del contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rejecter li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocar ciffrat communication con li aparate del contacte quel usa ti-ci " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Acceptar li clave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permisser ciffrat communication con li aparate del contacte quel usa ti-ci " "clave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ti-ci clave es actualmen %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptat" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "To significa que %s posse usar it por reciver e inviar missages." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificat" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Adplu, it ha esset verificat corresponder con li clave sur li aparate del " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejectet" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "To significa que %s posse usar it por deciffrar vor missages e que vu ne va " "vider li missages ciffrat med it." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Retro" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerer" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ti-ci contacte have nov aparates" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Un decision de confidentie OMEMO es besonat" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Esque vu ha adjuntet un nov aparate por li conto %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nov claves" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nov claves de ciffration de vor altri aparates va esser acceptat " "automaticmen." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Acceptat" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rejectet" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificat" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gerentie de claves OMEMO" #~ msgid "Encryption" #~ msgstr "Ciffration" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d aparate OMEMO" #~ msgstr[1] "%d aparates OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Automaticmen acceptar nov claves" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nov claves de ciffration de ti-ci contacte va esser acceptat automaticmen." #~ msgid "Own key" #~ msgstr "Propri clave" #~ msgid "Associated keys" #~ msgstr "Associat claves" #~ msgid "Inactive keys" #~ msgstr "Ínactiv claves" #~ msgid "Unused" #~ msgstr "Ínusat" #~ msgid "Own fingerprint" #~ msgstr "Propri fingre-print" #~ msgid "Will be generated on first connection" #~ msgstr "Va esser generat pos li prim conexion" #~ msgid "Not matching" #~ msgstr "Ne corresponde" #~ msgid "Matching" #~ msgstr "Corresponde" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Pos li confirmation, omni futuri missages inviat per %s con ti-ci clave " #~ "va esser colorat correspondmen in li fenestre de conversation." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Cessar acceptar ti-ci clave por communication con su associat contacte." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Acceptar ti-ci clave por communication con su associat contacte" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "To significa que %s ne posse usar it por reciver missages, e su missages " #~ "inviat va esser ignorat." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Pos confirmation, omni futuri missages inviat per %s con ti-ci clave va " #~ "esser ignorat, e null vor missages va esser leibil con ti-ci clave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Pos confirmation, ti-ci clave va esser usabil per %s por reciver e inviar " #~ "missages." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quande ti-ci contacte adjunte nov ciffre-claves a su conto, acceptar les " #~ "automaticmen." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quande vu adjunte nov ciffre-claves a vor conto, acceptar les " #~ "automaticmen." dino-0.5.0/plugins/omemo/po/it.po0000664000000000000000000003030414776241610015344 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-11-25 11:28+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s sta usando un dispositivo non verificato. Non vedrai messaggi inviati da " "un dispositivo che non hai verificato." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gestisci dispositivi" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s non ha verificato questo dispositivo. Ciò significa che potresti perdere " "dei messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestisci la chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Confronta la fingerprint, carattere per carattere, con quella mostrata sul " "dispositivo del tuo contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Le impronte non combaciano" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Le impronte combaciano" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annulla" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Conferma" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifica la chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "I messaggi futuri inviati da %s dal dispositivo che usa questa chiave " "saranno evidenziati di conseguenza nella finestra della chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Le impronte non corrispondono" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Si prega di verificare che si stia confrontanto la fingerprint corretta. Se " "le fingerprint non corrispondono, l'account di %s potrebbe essere " "compromesso e si dovrebbe rifiutare questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifica la fingerprint della chiave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Confronta la fingerprint di questa chiave con quella mostrata sul " "dispositivo del contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Chiave rifiutata" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocca le comunicazioni crittografate con il dispositivo del contatto che " "sta usando questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Chiave accettata" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permetti la comunicazione crittografata con il dispositivo del contatto che " "sta usando questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "La chiave attualmente è %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "accettata" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Questo vuol dire che può essere usata da %s per ricevere ed inviare messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificata" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Inoltre è stato verificato che corrisponda con la chiave sul dispositivo del " "contatto." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rifiutata" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Questo vuol dire che non può essere usata da %s per decifrare i tuoi " "messaggi, e tu non vedrai i messaggi crittografati con essa." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Non vedrai i messaggi crittografati dal dispositivo di %s che usa questa " "chiave. Al contrario, quel dispositivo non sarà più in grado di decifrare i " "tuoi messaggi." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Sarai in grado di scambiare messaggi crittografati con il dispositivo di %s " "che usa questa chiave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Indietro" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestisci" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Questo contatto ha dei nuovi dispositivi" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Una decisione sulla fiducia è necessaria per la chiave OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hai aggiunto un nuovo dispositivo per l'account %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nuove chiavi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Le nuove chiavi di crittografia dei tuoi altri dispositivi verranno " "accettate automaticamente." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Accettata" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rifiutata" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificata" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gestione delle chiavi OMEMO" #~ msgid "Encryption" #~ msgstr "Crittografia" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispositivo OMEMO" #~ msgstr[1] "%d dispositivi OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Accetta automaticamente le nuove chiavi" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Le nuove chiavi di crittografia di questo contatto verranno accettate " #~ "automaticamente." #~ msgid "Own key" #~ msgstr "Proprie chiavi" #~ msgid "Associated keys" #~ msgstr "Chiavi associate" #~ msgid "Inactive keys" #~ msgstr "Chiavi inattive" #~ msgid "Unused" #~ msgstr "Inutilizzata" #~ msgid "Own fingerprint" #~ msgstr "Propria impronta" #~ msgid "Will be generated on first connection" #~ msgstr "Verrà generata alla prima connessione" #~ msgid "Your contact" #~ msgstr "Il tuo contatto" #~ msgid "Not matching" #~ msgstr "Non corrispondenti" #~ msgid "Matching" #~ msgstr "Corrispondenti" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Una volta confermato, qualsiasi messaggio futuro inviato da %s usando " #~ "questa chiave sarà evidenziato di conseguenza nella finestra della " #~ "conversazione." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Smetti di accettare questa chiave durante le comunicazioni col contatto " #~ "associato." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Inizia ad accettare questa chiave durante le comunicazioni con il " #~ "contatto ad essa associato" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Questo vuol dire che non può essere usata da %s per ricevere messaggi, ed " #~ "ogni messaggio inviato attraverso di essa sarà ignorato." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Una volta confermato, ogni messaggio futuro inviato da %s usando questa " #~ "chiave sarà ignorato e nessuno dei tuoi messaggi sarà leggibile usando " #~ "questa chiave." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Una volta confermata questa chiave sarà utilizzabile da %s per ricevere " #~ "ed inviare messaggi." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quando questo contatto aggiunge nuove chiavi di cifratura al proprio " #~ "account, accettale automaticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quando aggiungi nuove chiavi di cifratura al tuo account, accettale " #~ "automaticamente." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Inizia ad accettare questa chiave durante le comunicazioni col contatto " #~ "associato" #~ msgid "Reject Key" #~ msgstr "Rifiuta la chiave" #~ msgid "Accept Key" #~ msgstr "Accetta la chiave" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispositivo sconosciuto (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Altri dispositivi" #~ msgid "- None -" #~ msgstr "- Nessuno -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Errore del database" dino-0.5.0/plugins/omemo/po/ja.po0000664000000000000000000003131714776241610015327 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-15 20:09+0000\n" "Language-Team: none\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s は信頼されていないデバイスを使用しています。信頼していないデバイスから送信" "されたメッセージは閲覧できません。" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "デバイスを管理" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s はこのデバイスを信頼していません。相手がメッセージを正しく受信できない可能" "性があります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "キーを管理" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "以下のフィンガープリントと、お使いのデバイスに表示されているフィンガープリン" "トを、1文字ずつ比較してください。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "フィンガープリントが一致しません" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "フィンガープリントが一致します" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "キャンセル" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "確認" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "キーを確認" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "今後このキーを使ったデバイスから %s が送信するメッセージは、チャットウィンド" "ウでハイライト表示されます。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "フィンガープリントが一致しません" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "正しいフィンガープリントを比較しているか確認してください。フィンガープリント" "が一致しない場合、%s のアカウントはなりすましの可能性があるので、このキーを拒" "否することをおすすめします。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "キーのフィンガープリントを検証" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "このキーのフィンガープリントと、連絡先のデバイスに表示されたフィンガープリン" "トを比較します。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "キーを拒否" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "このキーを使った連絡先のデバイスとの暗号化された会話をブロックします。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "キーを許可" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "このキーを使った連絡先のデバイスとの暗号化された会話を許可します。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "このキーは現在%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "許可されています" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "このキーを使って、%s は暗号化されたメッセージを送受信できます。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "検証済みです" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "加えて、連絡先のデバイスのキーと一致することを検証済みです。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "拒否されています" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "このキーを使って、%s はあなたのメッセージを解読できなくなります。また、あなた" "は、このキーで暗号化されたメッセージを確認できなくなります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "このキーを使った %s のデバイスから、暗号化されたメッセージを確認できなくなり" "ます。逆に、今後そのデバイスでは、あなたのメッセージを解読できなくなります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "このキーを使った %s のデバイスと、暗号化されたメッセージをやり取りできるよう" "になります。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "戻る" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "この連絡先に新しいデバイスが追加されました" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO 信頼選択が必要です" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "アカウント %s に新しいデバイスを追加しましたか?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "既定でOMEMO" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "新しい会話にOMEMO暗号化を有効にする" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "新しいデバイスに対して暗号化" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "この連絡先から新しいデバイスに対して自動的に暗号化を行います。" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "新しいキー" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "あなたのほかのデバイスから発行された新しい暗号化キーが自動的に許可されます。" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "許可" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "拒否" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "検証済み" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "新しいデバイス" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO キー管理" #~ msgid "Encryption" #~ msgstr "暗号化" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d 台の OMEMO デバイス" #~ msgid "Automatically accept new keys" #~ msgstr "自動的に新しいキーを許可" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "この連絡先から発行された新しい暗号化キーが自動的に許可されます。" #~ msgid "Own key" #~ msgstr "自身のキー" #~ msgid "Associated keys" #~ msgstr "関連付けられたキー" #~ msgid "Inactive keys" #~ msgstr "非アクティブなキー" #~ msgid "Unused" #~ msgstr "未使用" #~ msgid "Own fingerprint" #~ msgstr "自身のフィンガープリント" #~ msgid "Will be generated on first connection" #~ msgstr "最初の接続で生成されます" #~ msgid "Your contact" #~ msgstr "あなたの連絡先" #~ msgid "Not matching" #~ msgstr "一致しない" #~ msgid "Matching" #~ msgstr "一致" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一度確認すると、今後このキーを使って %s から送信されるメッセージは、チャッ" #~ "トウィンドウでハイライト表示されます。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "関連付けられた連絡先とトークをする際に、このキーを許可しないようにします。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "関連付けられた連絡先とトークをする際に、このキーを許可するようにします" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "このキーを使って %s からのメッセージを受信できません。また、当該アドレスか" #~ "らのメッセージはすべて無視されます。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一度確認すると、今後このキーを使って %s から送信されるメッセージは無視さ" #~ "れ、このキーを使ったご自身のメッセージも読み取れなくなります。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "一度確認すると、このキーを使って %s からのメッセージを送受信可能になりま" #~ "す。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "この連絡先の相手がアカウントに追加する新しい暗号化キーを自動的に許可しま" #~ "す。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "アカウントに追加した新しい暗号化キーを自動的に許可します。" dino-0.5.0/plugins/omemo/po/lb.po0000664000000000000000000003016614776241610015333 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-04-12 20:01+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.0-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Geräter managen" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Schlëssel verwalten" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergläich de Fangerofdrock, Zeeche fir Zeechen, mat dem den um Gerät vum " "Kontakt ugewise gëtt." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fangerofdréck stemmen net iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fangerofdréck stemmen iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Ofbriechen" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirméieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Schlëssel verifizéieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Zukünfteg Noriichten, déi vum Apparat vum %s, den dëse Schlëssel benotzt, " "geschéckt ginn, wäerten deementspriechend am Chatfenster ervirgehuewen ginn." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fangerofdréck stëmmen net iwwerteneen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Wann ech gelift iwwerpréif, ob's du mat dem " "korrekte Fangerofdrock vergläichs. Wann Fangerofdréck net iwwerteneestëmmen, " "dann ass dem %s säi Konto villäicht kompromettéiert an du solls dëse " "Schlëssel verweigeren." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Iwwerpréif Schlëssel Fangerofdrock" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Vergläich de Fangerofdrock vun dësem Schlëssel mam Fangerofdrock den um " "Gerät vun dësem Kontakt steet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Schlëssel ofleenen" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockéiert verschlësselt Kommunikatioun mam Apparat vum Kontakt deen dëse " "Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Schlëssel acceptéieren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Erlaabt verschlësselt Kommunikatioun mam Apparat vum Kontakt, deen dëse " "Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Den aktuelle Schlëssel ass %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "akzeptéiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dat bedeit, dass et vum %s ka benotzt gi fir Messagen ze empfänken an ze " "verschécken." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verifizéiert" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "De Schlëssel ass zousätzlech iwwerpréift gi mam Schlëssel um Gerät vum " "Kontakt iwwerteneen ze stëmmen." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "ofgeleent" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Dëst bedeit datt et net vum %s ka benotzt ginn fir är Messagen ze " "entschlësselen, an Dir gesitt keng Messagen déi mat dësem verschlësselt sinn." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Dir gesitt net verschlësselt Message vum Apparat vum %s deen dëse Schlëssel " "benotzt. Ëmgekéiert kann dëse Apparat net méi Är Messagen entschlësselen." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Dir kënnt verschlësselte Messagen austausche mat dem Apparat vum %s deen " "dësen Schlëssel benotzt." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Zeréck" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Verwalten" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Dëse Kontakt huet nei Geräter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO Vertrauens Entscheedung noutwenneg" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Hues du een neit Gerät fir %s dobäi gemaach?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nei Schlësselen" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nei Verschlësslungs Schlëssele vun ären aneren Apparater ginn automatesch " "akzeptéiert." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Akzeptéiert" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Ofgeleent" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verifizéiert" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO Schlësselenverwaltung" #~ msgid "Encryption" #~ msgstr "Verschlësselung" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO Gerät" #~ msgstr[1] "%d OMEMO Geräter" #~ msgid "Automatically accept new keys" #~ msgstr "Automatesch nei Schlësselen akzeptéieren" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nei Verschlësslungs Schlëssele vun dem Kontakt ginn automatesch " #~ "akzeptéiert." #~ msgid "Own key" #~ msgstr "Eegene Schlëssel" #~ msgid "Associated keys" #~ msgstr "Zougehéiereg Schlësselen" #~ msgid "Inactive keys" #~ msgstr "Inaktiv Schlësselen" #~ msgid "Unused" #~ msgstr "Onbenotzt" #~ msgid "Own fingerprint" #~ msgstr "Eegene Fangerofdrock" #~ msgid "Will be generated on first connection" #~ msgstr "Gett bei der éischter Connectioun generéiert" #~ msgid "Not matching" #~ msgstr "Stemmt net iwwerteneen" #~ msgid "Matching" #~ msgstr "Stemmt iwwerteneen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Wann konforméiert, all zukünfteg Messagen déi vum %s mat dësem Schlëssel " #~ "geschéckt ginn, ginn deementspriechend am Chatfenster markéiert." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dëse Schlëssel net méi akzeptéiere wärend der Kommunikatioun mat sengem " #~ "verbonnene Kontakt." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Dëse Schlëssel wärend de Conversatioun mat sengem Kontakt benotzen" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dat bedeit, dass dëse Schlëssel net ka vum %s benotz gi fir Messagen ze " #~ "empfänken an all Message déi geschéckt gi ginn ignoréiert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Wann confirméiert, all zukünfteg Message déi vum %s mat dësem Schlëssel " #~ "geschéckt ginn, ginn ignoréiert a keng vun dengem Messagë wäerte liesbar " #~ "sinn." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Wann konforméiert ass de Schlëssel vum %s benotzbar fir Messagen ze " #~ "empfänken an ze schécken." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Wann dëse Kontakt néi Schlësselen zu sengem Konto bäifüügt, déi " #~ "automatesch akzeptéieren." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Wann s du nei Schlëssele bei däin Account bäifüügs, déi automatesch " #~ "akzeptéieren." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Dëse Schlëssel wärend der Kommunikatioun mat sengem verbonne Kontakt " #~ "akzeptéieren." #~ msgid "Reject Key" #~ msgstr "Schlëssel verweigeren" #~ msgid "Accept Key" #~ msgstr "Schlëssel acceptéieren" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Onbekannt Gerät (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Aner Geräter" #~ msgid "- None -" #~ msgstr "- Keng -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Datebank Feeler" dino-0.5.0/plugins/omemo/po/lt.po0000664000000000000000000002733514776241610015361 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-10 14:09+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s naudoja nepatikimą įrenginį. Jūs nematysite žinučių iš įrenginių, kuriais " "nepasitikite." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Tvarkyti įrenginius" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nepasitiki šiuo įrenginiu. Tai reiškia, kad galite nematyti žinučių." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Tvarkyti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Palyginkite kontrolinį kodą, simbolis po simbolio, su tuo, kuris yra rodomas " "jūsų adresato įrenginyje." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Kodas skiriasi" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Kodas atitinka" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Atsisakyti" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Patvirtinti" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Patikrinkite raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Nuo šiol žinutės nuo %s išsiųstos iš įrenginio naudojančio šį kontrolinį " "kodą bus atitinkamai paryškintos." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Kontroliniai kodai nesutampa" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Įsitikinkite, kad lyginate teisingą kontrolinį kodą. Jei kontroliniai kodai " "nesutampa, gali būti, kad %s paskyra yra sukompromituota ir turėtumėte " "apsvarstyti galimybę atmesti šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Patikrinti rakto kontrolinį kodą" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Palyginti šio rakto kontrolinį kodą su adresato įrenginyje rodomu " "kontroliniu kodu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Atmesti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokuoti šifruotą bendravimą su kontakto įrenginiu, kuris naudoja šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Priimti raktą" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Leisti šifruotą bendravimą su kontakto įrenginiu, kuris naudoja šį raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Šiuo metu šis raktas yra %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "priimtas" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Tai reiškia, kad %s gali jį naudoti šifruotų žinučių siuntimui ir gavimui." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "patikrintas" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Be to, jis buvo patikrintas ir sutampa su adresato įrenginyje rodomu raktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "atmestas" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Tai reiškia, kad %s negali jo naudoti jūsų žinučių iššifravimui ir jūs " "nematysite žinučiu užšifruotų šiuo raktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Jūs nematysite šifruotų žinučių išsiustų iš %s įrenginio, kuris naudoją šį " "raktą. Tuo pačiu tas įrenginys nebegalės iššifruoti jūsų žinučių." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Jūs galite keistis šifruotomis žinutėmis su %s įrenginiu, kuris naudoja šį " "raktą." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Atgal" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Tvarkyti" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Šis adresatas turi naujų įrenginių" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO reikia pasitikėjimo sprendimo" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ar savo %s paskyrai pridėjote naują įrenginį?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO pagal numatymą" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Įjungti naujiems pokalbiams OMEMO šifravimą" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Šifruoti į naujus įrenginius" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automatiškai šifruoti į naujus įrenginius iš šio adresato." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nauji raktai" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nauji šifravimo raktai iš jūsų kitų įrenginių bus priimti automatiškai." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Priimtas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Atmestas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Patikrintas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Naujas įrenginys" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO raktų tvarkymas" #~ msgid "Encryption" #~ msgstr "Šifravimas" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO įrenginys" #~ msgstr[1] "%d OMEMO įrenginiai" #~ msgstr[2] "%d OMEMO įrenginių" #~ msgid "Automatically accept new keys" #~ msgstr "Automatiškai priimti naujus raktus" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Nauji šifravimo raktai iš šio kontakto bus priimti automatiškai." #~ msgid "Own key" #~ msgstr "Nuosavas raktas" #~ msgid "Associated keys" #~ msgstr "Susieti raktai" #~ msgid "Inactive keys" #~ msgstr "Neaktyvūs raktai" #~ msgid "Unused" #~ msgstr "Nenaudojamas" #~ msgid "Own fingerprint" #~ msgstr "Nuosavas kontrolinis kodas" #~ msgid "Will be generated on first connection" #~ msgstr "Bus sugeneruotas pirmojo prisijungimo metu" #~ msgid "Your contact" #~ msgstr "Jūsų adresatas" #~ msgid "Not matching" #~ msgstr "Nesutampa" #~ msgid "Matching" #~ msgstr "Sutampa" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Patikrinus, bet kokios būsimos žinutės, kurias %s išsiųs naudodamas šį " #~ "raktą, bus atitinkamai paryškintos pokalbių kambaryje." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Nustoti priiminėti šį raktą bendravimo su susietu adresatu metu." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Pradėti priiminėti šį raktą bendravimo su susietu adresatu metu" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Tai reiškia, kad %s negali jo naudoti, kad gautų žinutes, o bet kurių " #~ "adresato siunčiamų žinučių bus nepaisoma." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Patvirtinus, bet kurių būsimų žinučių, kurias %s išsiųs naudodamas šį " #~ "raktą, bus nepaisoma ir jokių jūsų žinučių nebus įmanoma perskaityti " #~ "naudojant šį raktą." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Patvirtinus, šis raktas taps tinkamu naudoti %s, kad gautų ir siųstų " #~ "žinutes." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Kai šis adresatas prie savo paskyros pridės naujus šifravimo raktus, " #~ "automatiškai juos priimti." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Kai pridedate naujus šifravimo raktus prie savo paskyros, automatiškai " #~ "juos priimti." dino-0.5.0/plugins/omemo/po/meson.build0000664000000000000000000000003314776241610016526 0ustar rootrooti18n.gettext('dino-omemo') dino-0.5.0/plugins/omemo/po/nb.po0000664000000000000000000002761214776241610015337 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-10-10 10:49+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s har brukt en ubetrodd enhet. Du vil ikke se meldinger fra enheter du ikke " "har tiltro til." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Håndter enheter" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s har ikke tiltro til denne enheten. Det betyr at du kan gå glipp av " "meldinger." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Håndter nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Sammenlign fingeravtrykket, tegn for tegn, med den vist på din kontakts " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingeravtrykkene stemmer ikke overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingeravtrykkene stemmer overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Avbryt" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bekreft" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Bekreft nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Fremtidige meldinger sendt av %s fra enheten som benytter denne nøkkelen vil " "framheves i så måte i sludrevinduet." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingeravtrykkene stemmer ikke overens" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Sjekk at du sammenligner riktig fingeravtrykk. Hvis de ikke stemmer overens, " "kan det hende %s sin konto har falt i gale hender, og du bør overveie å " "avvise denne nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Sjekk nøkkelfingeravtrykk" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Sammenlign dette fingeravtrykket med fingeravtrykket vist på kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Avslå nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokker kryptert kommunikasjon med kontaktens enhet som benytter denne " "nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Godta nøkkel" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Tillat kryptert kommunikasjon med kontaktens enhet som benytter denne " "nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Denne nøkkelen er i øyeblikket %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "godtatt" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dette betyr at den kan brukes av %s til å motta og sende krypterte meldinger." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "bekreftet" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "I tillegg har den blitt bekreftet å samsvare med nøkkelen på kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "avslått" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Dette betyr den ikke kan brukes av %s for å fortolke meldingene dine, og du " "vil ikke kunne se meldinger kryptert med den." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du vil ikke se krypterte meldinger fra %s sin enhet som bruker denne " "nøkkelen. Denne enheten vil heller ikke kunne fortolke dine meldinger lenger." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du vil kunne utveksle krypterte meldinger med %s sin enhet som benytter " "denne nøkkelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tilbake" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Behandle" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Denne kontakten har nye enheter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO tillitsbeslutning kreves" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "La du til en ny enhet for kontoen %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nye nøkler" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Nye krypteringsnøkler fra dine andre enheter vil godtas automatisk." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Godtatt" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Avslått" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Bekreftet" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO-nøkkelhåndtering" #~ msgid "Encryption" #~ msgstr "Kryptering" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-enhet" #~ msgstr[1] "%d OMEMO-enheter" #~ msgid "Automatically accept new keys" #~ msgstr "Godta nye nøkler automatisk" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Nye krypteringsnøkler fra denne kontakten vil godtas automatisk." #~ msgid "Own key" #~ msgstr "Egen nøkkel" #~ msgid "Associated keys" #~ msgstr "Tilknyttede nøkler" #~ msgid "Inactive keys" #~ msgstr "Inaktive nøkler" #~ msgid "Unused" #~ msgstr "Ubrukt" #~ msgid "Own fingerprint" #~ msgstr "Eget fingeravtrykk" #~ msgid "Will be generated on first connection" #~ msgstr "Vil bli generert ved første tilkobling" #~ msgid "Your contact" #~ msgstr "Din kontakt" #~ msgid "Not matching" #~ msgstr "Samsvarer ikke" #~ msgid "Matching" #~ msgstr "Samsvarer" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Når først bekreftet, vil fremtidige meldinger sendt av %s ved bruk av " #~ "denne nøkkelen bli fremhevet tilsvarende i sludrevinduet." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Slutt å godta denne nøkkelen under kommunikasjon med dens tilknyttede " #~ "kontakt." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Aksepter heretter denne nøkkelen i kommunikasjon med dens tilhørende " #~ "kontakt" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dette betyr at den ikke kan brukes av %s til å motta meldinger, og enhver " #~ "melding sendt på vegne av den vil bli ignorert." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Etter bekreftelsen vil fremtidige meldinger sendt av %s der denne " #~ "nøkkelen brukes bli ignorert, og ingen av dine meldinger vil kunne leses " #~ "med den." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Når først bekreftet vil denne nøkkelen kunne brukes av %s til å motta og " #~ "sende meldinger." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Når denne kontakten legger nye krypteringsnøkler til kontoen sin, godta " #~ "dem automatisk." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Godta dem automatisk når du legger nye krypteringsnøkler til kontoen din." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Begynn å godta denne nøkkelen under kommunikasjon med dens tilknyttede " #~ "kontakt." #~ msgid "Reject Key" #~ msgstr "Avslå nøkkel" #~ msgid "Accept Key" #~ msgstr "Godta nøkkel" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Ukjent enhet (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andre enheter" #~ msgid "- None -" #~ msgstr "- Ingen -" #~ msgid "OMEMO" #~ msgstr "OMEMO" dino-0.5.0/plugins/omemo/po/nl.po0000664000000000000000000003014414776241610015343 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-02-25 04:41+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.2-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s is een onvertrouwd apparaat. Berichten van onvertrouwde apparaten worden " "niet getoond." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Apparaten beheren" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s vertrouwt dit apparaat niet. Je mist hierdoor mogelijk berichten." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Sleutel beheren" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Vergelijk de vingerafdruk, letter voor letter, met die op het apparaat van " "je contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Vingerafdrukken wijken af" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Vingerafdrukken komen overeen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Annuleren" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bevestigen" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Sleutel verifiëren" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Toekomstige berichten van %s van het apparaat dat deze sleutel gebruikt, " "worden gemarkeerd in het gespreksvenster." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Vingerafdrukken komen niet overeen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Zorg dat je de juiste vingerafdrukken vergelijkt. Als de vingerafdrukken " "niet overeenkomen, kan het zijn dat het account van %s gehackt is. Overweeg " "in dat geval deze sleutel te weigeren." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Vingerafdruk van sleutel verifiëren" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Vergelijk de vingerafdruk van deze sleutel met de vingerafdruk die wordt " "getoond op het apparaat van je contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Sleutel weigeren" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokkeer versleutelde communicatie met het apparaat van deze contactpersoon " "dat deze sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Sleutel accepteren" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Sta versleutelde communicatie toe met het apparaat van deze contactpersoon " "dat deze sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Deze sleutel is momenteel %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "geaccepteerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Dit betekent dat %s deze kan gebruiken om berichten te ontvangen en " "versturen." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "geverifieerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Er is vastgesteld dat deze sleutel overeenkomt met de sleutel op het " "apparaat van de contactpersoon." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "geweigerd" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "De sleutel kan niet door %s worden gebruikt om berichten te ont- en " "versleutelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Je ziet geen versleutelde berichten van het apparaat van %s dat deze sleutel " "gebruikt. Daarnaast kan het apparaat jouw berichten niet meer ontsleutelen." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Je kunt versleutelde berichten uitwisselen met het apparaat van %s dat deze " "sleutel gebruikt." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Terug" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Beheren" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Deze contactpersoon heeft nieuwe apparaten" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-vertrouwenskeuze vereist" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Heb je een nieuw apparaat toegevoegd aan %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "Standaard OMEMO" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Zet OMEMO versleuteling aan voor nieuwe gesprekken" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Versleut nieuwe apparaten" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automatisch versleutelen van nieuwe apparaten van dit contact." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nieuwe sleutels" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nieuwe sleutels van je andere apparaten worden automatisch geaccepteerd." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Geaccepteerd" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Geweigerd" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Geverifieerd" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Nieuwe apparaat" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO-sleutelbeheer" #~ msgid "Encryption" #~ msgstr "Encryptie" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-apparaat" #~ msgstr[1] "%d OMEMO-apparaten" #~ msgid "Automatically accept new keys" #~ msgstr "Nieuwe sleutels automatisch accepteren" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nieuwe sleutels van deze contactpersoon worden automatisch geaccepteerd." #~ msgid "Own key" #~ msgstr "Mijn sleutel" #~ msgid "Associated keys" #~ msgstr "Geassocieerde sleutels" #~ msgid "Inactive keys" #~ msgstr "Inactieve sleutels" #~ msgid "Unused" #~ msgstr "Ongebruikt" #~ msgid "Own fingerprint" #~ msgstr "Mijn vingerafdruk" #~ msgid "Will be generated on first connection" #~ msgstr "Wordt bij de eerste verbinding gegenereerd" #~ msgid "Your contact" #~ msgstr "Je contactpersoon" #~ msgid "Not matching" #~ msgstr "Komen niet overeen" #~ msgid "Matching" #~ msgstr "Komen overeen" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Zodra bevestigd, worden nieuwe berichten verstuurd door %s met deze " #~ "sleutel uitgelicht in het chatvenster." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepteer niet langer deze sleutel tijdens communicatie met het " #~ "bijbehorende contact." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Accepteer deze sleutel tijdens communicatie met het bijbehorende contact" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Dit betekent dat het niet gebruikt kan worden door %s om berichten te " #~ "ontvangen. Berichten die hiermee verstuurd worden worden genegeerd." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Zodra bevestigd, worden toekomstige berichten die verstuurd worden door " #~ "%s met deze sleutel genegeerd en zullen geen van je berichten leesbaar " #~ "zijn met deze sleutel." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Zodra bevestigd kan deze sleutel gebruikt worden door %s om berichten te " #~ "ontvangen en te versturen." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Indien dit contact nieuwe encryptiesleutels toevoegt aan hun account, " #~ "automatisch accepteren." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Nieuwe encryptiesleutels die aan het account worden toegevoegd " #~ "automatisch accepteren." #, fuzzy #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Accepteer deze sleutel tijdens communicatie met het bijbehorende contact" #~ msgid "Reject Key" #~ msgstr "Sleutel Afwijzen" #~ msgid "Accept Key" #~ msgstr "Sleutel Aanvaarden" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Onbekend apparaat (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Andere apparaten" #~ msgid "- None -" #~ msgstr "- Geen -" #~ msgid "OMEMO" #~ msgstr "OMEMO" #~ msgid "Database error" #~ msgstr "Databasefout" dino-0.5.0/plugins/omemo/po/oc.po0000664000000000000000000002313314776241610015333 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-02-24 11:39+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.16-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilizat un periferic desconegut. Veiretz pas los messatges venents de " "client que vos fisatz pas." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gerir los periferics" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s se fisa pas d’aqueste client. Significa que mancaretz benlèu de messatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestion de las claus" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparatz l’emprenta, caractèr per caractèr, amb aquela qu’es afichada al " "client de vòstre contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "L’emprenta es diferenta" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Las emprentas correspondon" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anullar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Los messatges venents enviat per %s a partir del client utilizant aquesta " "clau seràn meses en davant dins la fenèstra del chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Las emprentas correspondon pas" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Mercés de verificar que sètz a comparar l’emprenta corrècta. Se l’emprenta " "correspond pas, lo compte de %s pòt èsser compromés e deuriatz regetar " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar l’emprenta d’aquesta clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparatz aquesta clau amb l’emprenta afichada al periferic de vòstre " "contacte." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Regetar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocar las comunicacion chifradas amb lo client del contacte qu’utiliza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Acceptar la clau" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Autorizar las comunicacions chifradas amb lo client del contacte qu’utiliza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "La clau es actualament %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptada" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Aquò significa que èsser utilizada per %s per recebre e enviar de messatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Amai, la verificacion de la correspondéncia d’aquesta clau amb la del client " "del contacte es estada facha." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "regetada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Aquò significa que pòt pas èsser utilizada per %s per deschifrar vòstres " "messatges, veiretz pas mai los messatges chifrats amb ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Veiretz pas mai los messatges que venon de l’aparelh de %s qu’utiliza " "aquesta clau. Al contrari, aqueste aparelh poirà pas mai deschifrar vòstres " "messatges." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Poiretz escambiar de messatges chifrats amb l’aparelh de %s qu’utiliza " "aquesta clau." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tornar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerir" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Aqueste contacte a d’aparelhs novèls" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decision de fisança OMEMO requerida" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Avètz apondut un aparelh novèl pel compte de %s ?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Claus novèlas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Las claus novèlas de deschiframent de vòstres autres aparelhs seràn " "automaticament acceptadas." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Acceptada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Regetada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gestion de las claus OMEMO" #~ msgid "Encryption" #~ msgstr "Chiframent" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d periferic OMEMO" #~ msgstr[1] "%d periferics OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Acceptar automaticament las claus novèlas" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Las claus novèlas de deschiframent d’aqueste contacte seràn " #~ "automaticament acceptadas." #~ msgid "Own key" #~ msgstr "Ma clau publica" #~ msgid "Associated keys" #~ msgstr "Claus associadas" #~ msgid "Inactive keys" #~ msgstr "Claus inactivas" #~ msgid "Unused" #~ msgstr "Pas utilizada" #~ msgid "Own fingerprint" #~ msgstr "Emprenta publica" #~ msgid "Will be generated on first connection" #~ msgstr "Serà generat a la primièra connexion" #~ msgid "Your contact" #~ msgstr "Vòstre contacte" dino-0.5.0/plugins/omemo/po/pl.po0000664000000000000000000003010214776241610015337 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-24 13:15+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s używa niezaufanego urządzenia. Nie będziesz widzieć wiadomości wysłanych " "z urządzeń, którym nie ufasz." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Zarządzaj urządzeniami" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nie ufa temu urządzeniu. Oznacza to, że wiadomości mogą do ciebie nie " "docierać." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Zarządzaj kluczem" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Porównaj odcisk klucza, znak po znaku, z tym pokazanym na urządzeniu twojego " "kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Odcisk klucza różni się" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Odcisk klucza pasuje" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anuluj" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Potwierdź" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Zweryfikuj klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Przyszłe wiadomości wysyłane przez %s z urządzenia używającego tego klucza " "zostaną odpowiednio podświetlone w oknie czatu." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Odcisk klucza nie zgadza się" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Proszę zweryfikować, że porównujesz prawidłowy odcisk klucza. Jeśli odciski " "nie zgadzają się, konto %s mogło być zaatakowane i należy rozważyć " "odrzucenie tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Zweryfikuj odcisk klucza" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Porównaj odcisk tego klucza z odciskiem wyświetlonym na urządzeniu kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Odrzuć klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blokuj szyfrowaną komunikację z urządzeniem kontaktu, które korzysta z tego " "klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Zaakceptuj klucz" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Zezwól na szyfrowaną komunikację z urządzeniem kontaktu, które korzysta z " "tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ten klucz jest obecnie %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "zaakceptowany" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "To znaczy, że może być używany przez %s do odbierania i wysyłania " "szyfrowanych wiadomości." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "zweryfikowany" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Dodatkowo zostało sprawdzone, że zgadza się z kluczem na urządzeniu kontaktu." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "odrzucony" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Oznacza to, że %s nie może go użyć do odszyfrowania wiadomości i nie " "zobaczysz wiadomości zaszyfrowanych przy jego pomocy." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nie zobaczysz zaszyfrowanych wiadomości z urządzenia %s, które używa tego " "klucza. I odwrotnie, to urządzenie nie będzie już w stanie odszyfrować " "Twoich wiadomości." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Będziesz mógł wymieniać zaszyfrowane wiadomości z urządzeniem %s, które " "używa tego klucza." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Wstecz" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Zarządzaj" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ten kontakt ma nowe urządzenia" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decyzja o zaufaniu OMEMO jest potrzebna" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Czy dodałeś nowe urządzenie dla konta %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "Domyślnie OMEMO" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Włącz szyfrowanie za pomocą OMEMO dla nowych konwersacji" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Szyfrowanie nowych urządzeń" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automatycznie szyfruj na nowych urządzeniach od tego kontaktu." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Nowe klucze" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Nowe klucze szyfrowania z innych urządzeń będą akceptowane automatycznie." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Zaakceptowany" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Odrzucony" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Zweryfikowany" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Nowe urządzenie" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Zarządzanie kluczami OMEMO" #~ msgid "Encryption" #~ msgstr "Szyfrowanie" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d urządzenie OMEMO" #~ msgstr[1] "%d urządzenia OMEMO" #~ msgstr[2] "%d urządzeń OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Automatycznie akceptuj nowe klucze" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nowe klucze szyfrowania od tego kontaktu będą akceptowane automatycznie." #~ msgid "Own key" #~ msgstr "Własny klucz" #~ msgid "Associated keys" #~ msgstr "Pozostałe klucze" #~ msgid "Inactive keys" #~ msgstr "Nieaktywne klucze" #~ msgid "Unused" #~ msgstr "Nieużywany" #~ msgid "Own fingerprint" #~ msgstr "Własny odcisk klucza" #~ msgid "Will be generated on first connection" #~ msgstr "Zostanie wygenerowany przy pierwszym połączeniu" #~ msgid "Not matching" #~ msgstr "Odciski nie zgadzają się" #~ msgid "Matching" #~ msgstr "Odciski zgadzają się" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Przyszłe wiadomości wysłane przez %s i szyfrowane tym kluczem będą " #~ "odpowiednio zaznaczone w oknie czatu." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Zablokuj szyfrowaną komunikację ze sprzętem kontaktu, który używa ten " #~ "klucz." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Włącz szyfrowaną komunikację ze sprzętem kontaktu, który używa ten klucz." #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "To znaczy, że nie może być używany przez %s do rozszyfrowania twoich " #~ "wiadomości, a wiadomości szyfrowane nim nie będą u ciebie wyświetlane." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Wiadomości wysłane przez %s ze sprzętu używającego ten klucz nie będą " #~ "wyświetlane. Podobnie sprzęt tej osoby nie będzie mógł rozszyfrować " #~ "twoich wiadomości." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "To umożliwi wymianę szyfrowanych wiadomości ze sprzętem należącym do %s, " #~ "który używaja ten klucz." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Gdy ten kontakt doda nowe klucze szyfrowania do swojego konta, zaakceptuj " #~ "je automatycznie." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Gdy dodam nowe klucze szyfrowania do mojego konta, zaakceptuj je " #~ "automatycznie." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Nieznane urządzenie (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Pozostałe urządzenia" #~ msgid "- None -" #~ msgstr "- Wybierz -" dino-0.5.0/plugins/omemo/po/pt.po0000664000000000000000000002304514776241610015357 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-05-10 09:33+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.7-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s esteve a usar um aparelho não confiável. Não verá mensagens de aparelhos " "nos quais não confia." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gerir aparelhos" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s não confia neste aparelho. Isso significa que pode estar a perder " "mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerir Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compare a impressão digital, caractere a caractere, com aquela mostrada no " "aparelho de seu contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impressões digitais não correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impressões digitais correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mensagens futuras enviadas por %s do aparelho que usa essa chave serão " "destacadas no janela de bate papo." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Impressões digitais não correspondem" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor verifique se está a comparar as impressões digitais corretas. Caso " "elas não correspondam, a conta do %s pode estar comprometida e deve " "considerar rejeitar essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar impressão digital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compare a impressão digital dessa chave com a impressão digital exibida no " "aparelho do contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rejeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloqueie comunicação criptografada com o aparelho do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Aceitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permita comunicação criptografada com o aparelho do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Essa chave está atualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceita" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Isso significa que ela pode ser usada por %s para enviar e receber mensagens " "criptografadas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Além disso, verificou-se que ela corresponde com a chave no aparelho do " "contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Isso significa que ela não pode ser usada por %s para decifrar suas " "mensagens, e não irá visualizar mensagens criptografadas com ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Não irá visualizar mensagens criptografas pelo aparelho de %s que usa essa " "chave. Além disso, esse aparelho não será mais capaz de decifrar suas " "mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Poderá trocar mensagem criptografadas com o aparelho de %s que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Voltar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerir" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Esse contato possui novos aparelhos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisão de confiança OMEMO necessária" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Adicionou um novo aparelho para a conta %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Chaves novas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novas chaves de criptografia de seus outros aparelhos serão aceitas " "automaticamente." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Aceita" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rejeitada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gestão de chave OMEMO" #~ msgid "Encryption" #~ msgstr "Criptografia" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d aparelho OMEMO" #~ msgstr[1] "%d aparelhos OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Aceitar novas chaves automaticamente" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Novas chaves de criptografia desse contato serão aceitas automaticamente." #~ msgid "Own key" #~ msgstr "Chave própria" #~ msgid "Associated keys" #~ msgstr "Chaves associadas" #~ msgid "Inactive keys" #~ msgstr "Chaves inativas" #~ msgid "Unused" #~ msgstr "Não usada" #~ msgid "Own fingerprint" #~ msgstr "Impressão digital própria" #~ msgid "Will be generated on first connection" #~ msgstr "Será gerada na primeira conexão" #~ msgid "Your contact" #~ msgstr "O seu contacto" dino-0.5.0/plugins/omemo/po/pt_BR.po0000664000000000000000000002736314776241610015751 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-15 20:09+0000\n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s tem usado um dispositivo não confiável. Você não verá mensagens de " "dispositivos que você não confia." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gerenciar dispositivos" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s não confia nesse dispositivo. Isso significa que você pode estar perdendo " "algumas mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gerenciar Chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Compare a impressão digital, caractere a caractere, com aquela mostrada no " "dispositivo de seu contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "As impressões digitais não correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "As impressões digitais correspondem uma a outra" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Cancelar" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmar" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mensagens futuras enviadas por %s do dispositivo que usa essa chave serão " "destacadas no janela de bate papo." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Impressões digitais não correspondem" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Por favor verifique se você está comparando as impressões digitais corretas. " "Caso elas não correspondam, a conta do %s pode estar comprometida e você " "deve considerar rejeitar essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificar impressão digital da chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Compare a impressão digital dessa chave com a impressão digital exibida no " "dispositivo do contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Rejeitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Bloqueie comunicação criptografada com o dispositivo do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Aceitar chave" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permita comunicação criptografada com o dispositivo do contato que usa essa " "chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Essa chave está atualmente %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "aceita" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Isso significa que ela pode ser usada por %s para enviar e receber mensagens " "criptografadas." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificada" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Além disso, verificou-se que ela corresponde com a chave no dispositivo do " "contato." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "rejeitada" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Isso significa que ela não pode ser usada por %s para decifrar suas " "mensagens, e você não irá visualizar mensagens criptografadas com ela." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Você não irá visualizar mensagens criptografas pelo dispositivo de %s que " "usa essa chave. Além disso, esse dispositivo não será mais capaz de decifrar " "suas mensagens." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Você poderá trocar mensagem criptografadas com o dispositivo de %s que usa " "essa chave." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Voltar" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gerenciar" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Esse contato possui novos dispositivos" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Decisão de confiança OMEMO necessária" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Você adicionou um novo dispositivo para a conta %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO por padrão" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Ativar a criptografia OMEMO em novas conversas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Criptografar para novos dispositivos" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Automaticamente criptografar para novos dispositivos deste contato." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Chaves novas" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Novas chaves de criptografia de seus outros dispositivos serão aceitas " "automaticamente." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Aceita" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Rejeitada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificada" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Dispositivo novo" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Gerenciamento de Chave OMEMO" #~ msgid "Encryption" #~ msgstr "Criptografia" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispositivo OMEMO" #~ msgstr[1] "%d dispositivos OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Aceitar novas chaves automaticamente" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Novas chaves de criptografia desse contato serão aceitas automaticamente." #~ msgid "Own key" #~ msgstr "Chave própria" #~ msgid "Associated keys" #~ msgstr "Chaves associadas" #~ msgid "Inactive keys" #~ msgstr "Chaves inativas" #~ msgid "Unused" #~ msgstr "Não usada" #~ msgid "Own fingerprint" #~ msgstr "Impressão digital própria" #~ msgid "Will be generated on first connection" #~ msgstr "Será gerada na primeira conexão" #~ msgid "Your contact" #~ msgstr "Seu contato" #~ msgid "Not matching" #~ msgstr "Não correspondem" #~ msgid "Matching" #~ msgstr "Correspondem" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Uma vez confirmada, toda mensagem enviada por %s no futuro será destacada " #~ "na janela da conversa." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Pare de aceitar essa chave ao se comunicar com o contato associado." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Aceite essa chave ao se comunicar com o contato associado" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Isso significa que ela não pode ser usada por %s para receber mensagens e " #~ "qualquer mensagem enviada com ela será ignorada." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Uma vez confirmada, toda mensagem enviada por %s usando essa chave será " #~ "ignorada e nenhuma de suas mensagens com ela serão legíveis." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Uma vez confirmada, essa chave poderá ser usada por %s para receber e " #~ "enviar mensagens." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Quando esse contato adicionar novas chaves de criptografia à conta, " #~ "aceite-as automaticamente." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Quando você adicionar novas chaves de criptografia para sua conta, aceite-" #~ "as automaticamente." dino-0.5.0/plugins/omemo/po/ro.po0000664000000000000000000003136014776241610015353 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s a utilizat un dispozitiv care nu este de încredere. Nu veți vedea mesaje " "de pe dispozitive în care nu aveți încredere." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Gestionare dispozitive" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nu are încredere în acest dispozitiv. Asta înseamnă că s-ar putea să-ți " "lipsească mesaje." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Gestionare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Comparați amprenta, caracter cu caracter, cu amprenta afișată pe " "dispozitivul contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Amprentele diferă" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Amprentele se potrivesc" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anulare" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Confirmare" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verificare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesajele viitoare trimise de %s de pe dispozitivul care utilizează această " "cheie vor fi evidențiate în mod corespunzător în fereastra de chat." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Amprentele nu se potrivesc" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Vă rugäm să verificați dacă se compară amprentele corecte. Dacă nu se " "potrivesc amprentele s-ar putea ca %s să aibä un cont compromis și ar trebui " "să luați în considerare respingerea acestei chei." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verificare amprentă cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Comparați amprenta cheii cu amprenta afișată pe dispozitivul contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Respingere cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blocați comunicarea criptată cu dispozitivul persoanei de contact care " "utilizează această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Acceptare cheie" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Permiteți comunicarea criptată cu dispozitivul persoanei de contact care " "utilizează această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Cheia este în prezent %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "acceptată" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Aceasta înseamnă că poate fi utilizată de %s pentru a primi și a trimite " "mesaje criptate." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verificată" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "În plus, a fost verificată că se potrivește cu cheia de pe dispozitivul " "contactului." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "respinsă" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Aceasta înseamnă că nu poate fi utilizată de %s pentru a vă descifra " "mesajele și nu veți vedea mesajele criptate cu aceasta." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Nu veți vedea mesaje criptate de la dispozitivul %s care utilizează această " "cheie. În schimb, dispozitivul respectiv nu va mai putea descifra mesajele " "dumneavoastră." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Veți putea schimba mesaje criptate cu dispozitivul %s care utilizează " "această cheie." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Înapoi" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Gestionare" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Acest contact are dispozitive noi" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Este necesara luarea unei decizii în privința OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Ați adăugat un nou dispozitiv pentru contul %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO activat implicit" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Activează criptarea OMEMO pentru conversațiile noi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Criptează pentru dispozitive noi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Criptează automat pentru dispozitivele noi ale acestui contact." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Chei noi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Noile chei de criptare de pe celelalte dispozitive ale dumneavoastră vor fi " "acceptate automat." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Acest dispozitiv" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Alt dispozitiv" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Dispozitiv" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Acceptată" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Respinsă" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verificată" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Dispozitiv nou" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Fiecare dispozitiv are propria cheie OMEMO. Mesajele pot fi decriptate de un " "dispozitiv numai dacă sunt criptate pentru cheia sa. Mesajele sunt criptate " "numai pentru dispozitivele acceptate." #~ msgid "OMEMO Key Management" #~ msgstr "Administrare chei OMEMO" #~ msgid "Encryption" #~ msgstr "Criptare" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d dispozitiv OMEMO" #~ msgstr[1] "%d dispozitive OMEMO" #~ msgstr[2] "%d de dispozitive OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Acceptă automat chei noi" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Noile chei de criptare de la această persoană de contact vor fi acceptate " #~ "automat." #~ msgid "Own key" #~ msgstr "Cheie proprie" #~ msgid "Associated keys" #~ msgstr "Chei asociate" #~ msgid "Inactive keys" #~ msgstr "Chei inactive" #~ msgid "Unused" #~ msgstr "Neutilizatä" #~ msgid "Own fingerprint" #~ msgstr "Amprentă proprie" #~ msgid "Will be generated on first connection" #~ msgstr "Se va genera la prima conectare" #~ msgid "Your contact" #~ msgstr "Persoana de contact" #~ msgid "Not matching" #~ msgstr "Nu se potrivesc" #~ msgid "Matching" #~ msgstr "Se potrivesc" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Odată confirmată, orice viitoare mesaje trimise de %s folosind această " #~ "cheie vor fi evidenţiate în consecință în fereastra de discuție." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Nu mai accepta această cheie în timpul comunicärii cu acest contact " #~ "asociat." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Se va accepta această cheie în timpul comunicării cu acest contact asociat" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Aceasta înseamnă că nu poate fi folosită de %s ca să primească mesaje, " #~ "iar orice mesaj trimis cu ea v-a fi ignorat." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Odată confirmată, orice viitoare mesaje trimise de %s folosind această " #~ "cheie vor fi ignorate și nici un mesaj trimis de dumneavoastră nu se va " #~ "putea citii folosid aceastä cheie." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Odată confirmată, aceastä cheie va fi utilizatä de %s pentru a primi și " #~ "trimite mesaje." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Când acest contact adäugă chei de criptare noi în contul lor, acestea vor " #~ "fi acceptate în mod automat." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Când adäugați chei de criptare noi pentru acest cont, acestea vor fi " #~ "acceptate în mod automat." #~ msgid "" #~ "Start accepting this key during communication with its associated contact." #~ msgstr "" #~ "Acceptă această cheie în timpul comunicării cu acest contact asociat." #~ msgid "Reject Key" #~ msgstr "Respingere cheie" #~ msgid "Accept Key" #~ msgstr "Acceptare cheie" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Dispozitiv necunoscut (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Alte dispozitive" #~ msgid "- None -" #~ msgstr "- Nici unul -" dino-0.5.0/plugins/omemo/po/ru.po0000664000000000000000000003445114776241610015365 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-12-30 22:56+0000\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.10-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s использовал(а) ненадёжное устройство. Вы не будете видеть сообщения от " "устройств, которым не доверяете." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Управление устройствами" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s не доверяет этому устройству. Это означает, что у вас могут отсутствовать " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Управление ключом" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Сравните отпечаток, символ за символом, с тем, который показан на устройстве " "вашего контакта." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Отпечатки отличаются" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Отпечатки схожи" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Отмена" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Подтвердить" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Подтвердить ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Будущие сообщения, отправленные %s с устройства, использующего этот ключ, " "будут соответствующим образом выделены в окне чата." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Отпечатки не совпадают" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Пожалуйста, проверьте совпадение отпечатков. Если они не совпадают, аккаунт " "\"%s\" может быть скомпрометирован, и вам стоит отклонить этот ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Подтвердить отпечаток" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "Сверить этот отпечаток с ключом, отображаемым у контакта." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Отклонить ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Заблокировать зашифрованную связь с устройством контакта, использующего этот " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Принять ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Разрешает зашифрованную связь с устройством контакта, использующего этот " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ключ был успешно %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "принят" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Это означает, что %s теперь может отправлять и принимать зашифрованные " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "проверен" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Также была проведена проверка совпадения ключа." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "отклонён" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Это означает что он не сможетт использоваться %s для расшифровки ваших " "сообщений, и вы также не увидите сообщений зашифрованных им." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Вы не увидите зашифрованных сообщений от устройства %s, которое использует " "этот ключ. И наоборот, это устройство больше не сможет расшифровывать ваши " "сообщения." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Вы сможете обмениваться зашифрованными сообщениями с устройством %s, которое " "использует этот ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Назад" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Настроить" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Этот контакт воспользовался новым устройством" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Требуется решение OMEMO о доверии" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Вы добавляли аккаунт %s на новое устройство?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO по умолчанию" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Включить OMEMO-шифрование для новых бесед" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Шифрование для новых устройств" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Автоматическое шифрование для новых устройств этого контакта." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Новые ключи" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Новые ключи шифрования от других ваших устройств будут приняты автоматически." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Принят" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Отклонён" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Подтверждён" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Новое устройство" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Управление ключами OMEMO" #~ msgid "Encryption" #~ msgstr "Шифрование" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d устройство OMEMO" #~ msgstr[1] "%d устройства OMEMO" #~ msgstr[2] "%d устройств OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Автоматически принимать новые ключи" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Новые ключи шифрования от этого контакта будут приняты автоматически." #~ msgid "Own key" #~ msgstr "Собственный ключ" #~ msgid "Associated keys" #~ msgstr "Связанные ключи" #~ msgid "Inactive keys" #~ msgstr "Неактивные ключи" #~ msgid "Unused" #~ msgstr "Не используется" #~ msgid "Own fingerprint" #~ msgstr "Отпечаток этого устройства" #~ msgid "Will be generated on first connection" #~ msgstr "Будет сгенерирован при первом подключении" #~ msgid "Your contact" #~ msgstr "Ваш контакт" #~ msgid "Not matching" #~ msgstr "Не совпадает" #~ msgid "Matching" #~ msgstr "Совпадает" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "После подтверждения все последующие сообщения, отправленные с %s при " #~ "помощи этого ключа, будут соответственно подсвечены в окне чата." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "Не принимать ключ этого контакта." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "Принять ключ этого контакта" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Это значит, что %s не может отправлять сообщения — все они будут " #~ "игнорироваться." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "После подтверждения все будущие сообщения, отправленные %s с " #~ "использованием этого ключа, будут игнорироваться и ни одно из ваших " #~ "сообщений не сможет быть прочитано с использованием этого ключа." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "После подтверждения этот ключ будет использоваться %s для получения и " #~ "отправки сообщений." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Когда этот контакт добавляет новые ключи шифрования в свою учетную " #~ "запись, автоматически принимайте их." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Когда вы добавляете новые ключи шифрования в свою учетную запись, " #~ "автоматически принимайте их." #~ msgid "Unknown device (0x%.8x)" #~ msgstr "Неизвестное устройство (0x%.8x)" #~ msgid "Other devices" #~ msgstr "Другие устройства" #~ msgid "- None -" #~ msgstr "- Нет -" dino-0.5.0/plugins/omemo/po/sq.po0000664000000000000000000002373214776241610015362 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s ka përdorur një pajisje jo të besuar. S’do të shihni mesazhe prej " "pajisjesh që nuk i besoni." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Administroni pajisje" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s nuk e beson këtë pajisje. Kjo do të thotë, mund të jeni duke humbur " "mesazhe." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Administroni Kyç" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Krahasoni shenjën e gishtave, shenjë pas shenje, me atë të shfaqur në " "pajisjen e kontaktit tuaj." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Shenjat e gishtave janë të ndryshme" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Shenjat e gishtave përputhen" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Anuloje" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Ripohojeni" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifikoni kyç" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Mesazhe të ardhshme dërguar nga %s prej pajisjes që përdor këtë kyç do të " "theksohen te dritarja e fjalosjes." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Shenjat e gishtave s’përputhen" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Ju lutemi, verifikoni se po krahasoni shenjat e sakta të gishtave. Nëse " "shenjat e gishtave s’përputhen, llogaria e %s mund të komprometohet dhe " "duhet të shihni mundësinë e hedhjes poshtë të këtij kyçi." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifikoni shenja gishtash kyçi" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Krahasoni shenjën e gishtit të këtij kyçi me shenjën e gishtit të shfaqur në " "pajisjen e kontaktit." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Hidhe tej kyçin" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blloko komunikim të fshehtëzuar me pajisjen e kontaktit që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Pranoje kyçin" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Lejo komunikim të fshehtëzuar me pajisjen e kontaktit që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Ky kyç aktualisht është %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "i pranuar" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Kjo do të thotë se mund të përdoret nga %s për të marrë dhe dërguar mesazhe " "të fshehtëzuara." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "i verifikuar" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Për më tepër, është verifikuar për t’u përputhur me kyçin në pajisjen e " "kontaktit." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "i hedhur poshtë" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Kjo do të thotë se s’mund të përdoret nga %s për të deshifruar mesazhet " "tuaja dhe s’do të shihni dot mesazhe të fshehtëzuara me të." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "S’do të shihni dot mesazhe të fshehtëzuar prej pajisjes së %s që përdor këtë " "kyç. Më anë tjetër, ajo pajisje s’do të jetë në gjendje të deshifrojë më " "mesazhet tuaja." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Do të jeni në gjendje të shkëmbeni mesazhe të fshehtëzuar me pajisjen e %s " "që përdor këtë kyç." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Mbrapsht" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Administroni" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Ky kontakt ka pajisje të reja" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Lypset vendim besimi OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Shtuat një pajisje të re për llogarinë %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO, si parazgjedhje" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Aktivizo fshehtëzim OMEMO për biseda të reja" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Fshehtëzo në pajisje të reja" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Fshehtëzo automatikisht në pajisje të reja nga ky kontakt." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Kyçe të rinj" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Kyçe të rinj fshehtëzimi prej pajisjesh tuaja të tjera do të pranohen " "automatikisht." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Këtë pajisje" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Tjetër pajisje" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Pajisje" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Të pranuar" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Të hedhur tej" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Të verifikuar" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Pajisje e re" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Çdo pajisje ka kyçin e vet OMEMO. Mesazhet mund të shfshehtëzohen vetëm nga " "një pajisje, nëse janë fshehtëzuar me kyçin e saj." #~ msgid "OMEMO Key Management" #~ msgstr "Administrim Kyçesh OMEMO" #~ msgid "Encryption" #~ msgstr "Fshehtëzim" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d pajisje OMEMO" #~ msgstr[1] "%d pajisje OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Prano automatikisht kyçe të rinj" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Kyçe të rinj fshehtëzimi nga ky kontakt do të pranohen automatikisht." #~ msgid "Own key" #~ msgstr "Kyç i vet" #~ msgid "Associated keys" #~ msgstr "Kyçe të përshoqëruar" #~ msgid "Inactive keys" #~ msgstr "Kyçe jo aktivë" #~ msgid "Unused" #~ msgstr "Të papërdorur" #~ msgid "Own fingerprint" #~ msgstr "Shenjë e vet gishtash" #~ msgid "Will be generated on first connection" #~ msgstr "Do të prodhohet gjatë lidhjes së parë" dino-0.5.0/plugins/omemo/po/sv.po0000664000000000000000000002677014776241610015374 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-16 05:09+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s har använt en ej betrodd enhet. Du kommer inte se meddelanden från " "enheter du inte litar på." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Hantera enheter" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s litar inte på den här enheten. Du kanske inte ser alla meddelanden." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Hantera nyckel" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Jämför fingeravtrycket, tecken för tecken, med det som visas i din kontakts " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingeravtrycken matcher ej" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingeravtrycken matchar" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Avbryt" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Bekräfta" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Verifiera nyckel" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Framtida meddelanden skickade av %s från enheten som använder denna nyckel " "kommer tydligt att markeras i chattfönstret." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingeravtrycken överensstämmer inte" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Kontrollera att du jämför rätt fingeravtryck. Om fingeravtrycken inte " "stämmer så kan %s konto vara underminerat och du bör överväga att avvisa " "denna nyckel." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Verifiera nyckelns fingeravtryck" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Jämför nyckelns fingeravtryck med det fingeravtryck som visas i kontaktens " "enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Avvisa nyckeln" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Blockera krypterad kommunikation med kontaktens enhet som använder den här " "nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Acceptera nyckeln" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Tillåt krypterad kommunikation med kontaktens enhet som använder den här " "nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Denna nyckel är för närvarande %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "accepterad" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Det betyder att den kan användas av %s för att skicka och ta emot krypterade " "meddelanden." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "verifierad" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Dessutom har den verifierats så att den överensstämmer med nyckeln på " "kontaktens enhet." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "avvisad" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Det innebär att den inte kan användas av %s för att dechiffrera dina " "meddelanden och du ser inte meddelanden som krypterats med den." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Du ser inte krypterade meddelanden från enheten %s som använder den här " "nyckeln. Omvänt kan den enheten inte dechiffrera dina meddelanden längre." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Du kommer att kunna utbyta krypterade meddelanden med enheten %s som " "använder den här nyckeln." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Tillbaka" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Hantera" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Denna kontakt har nya enheter" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO-tillitsbeslut krävs" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Har du lagt till en ny enhet för kontot %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO som standard" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Aktivera OMEMO kryptering för nya konversationer" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Kryptera nya enheter" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Kryptera automatiskt för nya enheter från den här kontakten." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Ny nyckel" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "Nya krypteringsnycklar från dina andra enheter accepteras automatiskt." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Accepterad" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Avvisad" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Verifierad" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Ny enhet" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO-nyckelhantering" #~ msgid "Encryption" #~ msgstr "Kryptering" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO-enhet" #~ msgstr[1] "%d OMEMO-enheter" #~ msgid "Automatically accept new keys" #~ msgstr "Acceptera automatiskt nya nycklar" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Nya krypteringsnycklar från den här kontakten accepteras automatiskt." #~ msgid "Own key" #~ msgstr "Egen nyckel" #~ msgid "Associated keys" #~ msgstr "Kopplade nycklar" #~ msgid "Inactive keys" #~ msgstr "Inaktiva nycklar" #~ msgid "Unused" #~ msgstr "Oanvänd" #~ msgid "Own fingerprint" #~ msgstr "Eget fingeravtryck" #~ msgid "Will be generated on first connection" #~ msgstr "Genereras vid första anslutningstillfället" #~ msgid "Your contact" #~ msgstr "Din kontakt" #~ msgid "Not matching" #~ msgstr "Stämmer ej" #~ msgid "Matching" #~ msgstr "Stämmer" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Om du går vidare så kommer framtida meddelanden från %s med denna nyckel " #~ "att markeras i enlighet med nyckelns nya status." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "Sluta acceptera denna nyckel vid kommunikation med den tillhörande " #~ "kontakten." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "" #~ "Börja acceptera nyckeln för kommunikation med den tillhörande kontakten" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Det betyder att den inte kan användas av %s för att ta emot meddelanden, " #~ "och att alla skickade meddelanden kommer att ignoreras." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Om du går vidare så kommer framtida meddelanden från %s som använder " #~ "denna nyckel att ignoreras, och meddelanden från dig blir oläsbara med " #~ "denna nyckel." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Om du går vidare så kommer nyckeln kunna användas av %s för att skicka " #~ "och ta emot meddelanden." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "Acceptera automatiskt nya nycklar som läggs till av denna kontakt." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "Acceptera automatiskt nya nycklar som du läggs till ditt konto." dino-0.5.0/plugins/omemo/po/ta.po0000664000000000000000000003362614776241610015346 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-12-20 07:11+0000\n" "Language-Team: none\n" "Language: ta\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.9.2-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s நம்பத்தகாத சாதனத்தைப் பயன்படுத்துகின்றன. நீங்கள் நம்பாத சாதனங்களிலிருந்து செய்திகளை " "நீங்கள் காண மாட்டீர்கள்." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "சாதனங்களை நிர்வகிக்கவும்" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "இந்த சாதனத்தை %s நம்பவில்லை. அதாவது, நீங்கள் செய்திகளைக் காணவில்லை." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "விசையை நிர்வகிக்கவும்" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "உங்கள் தொடர்பின் சாதனத்தில் காட்டப்பட்டுள்ள கைரேகை, எழுத்துக்குறி எழுத்துக்குறியை ஒப்பிடுக." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "கைரேகைகள் வேறுபடுகின்றன" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "கைரேகைகள் பொருந்துகின்றன" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "ரத்துசெய்" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "உறுதிப்படுத்தவும்" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "விசையை சரிபார்க்கவும்" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "இந்த விசையைப் பயன்படுத்தும் சாதனத்திலிருந்து %s அனுப்பிய எதிர்கால செய்திகள் அரட்டை " "சாளரத்தில் அதற்கேற்ப முன்னிலைப்படுத்தப்படும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "கைரேகைகள் பொருந்தவில்லை" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "நீங்கள் சரியான கைரேகையை ஒப்பிடுகிறீர்கள் என்பதை சரிபார்க்கவும். கைரேகைகள் பொருந்தவில்லை " "என்றால், %s இன் கணக்கு வேறுபாடின்மை செய்யப்படலாம், மேலும் இந்த விசையை நிராகரிப்பதை நீங்கள் " "கருத்தில் கொள்ள வேண்டும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "முக்கிய கைரேகையை சரிபார்க்கவும்" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "இந்த விசையின் கைரேகையை தொடர்பின் சாதனத்தில் காட்டப்படும் கைரேகையுடன் ஒப்பிடுக." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "விசையை நிராகரிக்கவும்" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "இந்த விசையைப் பயன்படுத்தும் தொடர்புகளின் சாதனத்துடன் மறைகுறியாக்கப்பட்ட தகவல்தொடர்புகளைத் " "தடுக்கவும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "சாவியை ஏற்றுக்கொள்" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "இந்த விசையைப் பயன்படுத்தும் தொடர்புகளின் சாதனத்துடன் மறைகுறியாக்கப்பட்ட தகவல்தொடர்புகளை " "அனுமதிக்கவும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "இந்த விசை தற்போது %s ஆகும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "ஏற்றுக்கொள்ளப்பட்டது" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "இதன் பொருள் மறைகுறியாக்கப்பட்ட செய்திகளைப் பெறுவதற்கும் அனுப்புவதற்கும் %s ஆல் " "பயன்படுத்தப்படலாம்." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "சரிபார்க்கப்பட்டது" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "கூடுதலாக, தொடர்பின் சாதனத்தில் விசையுடன் பொருந்த இது சரிபார்க்கப்பட்டது." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "நிராகரிக்கப்பட்டது" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "இதன் பொருள் உங்கள் செய்திகளைப் புரிந்துகொள்ள %s ஆல் பயன்படுத்த முடியாது, மேலும் அதனுடன் " "குறியாக்கம் செய்யப்பட்ட செய்திகளை நீங்கள் காண மாட்டீர்கள்." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "இந்த விசையைப் பயன்படுத்தும் %s சாதனத்திலிருந்து மறைகுறியாக்கப்பட்ட செய்திகளை நீங்கள் காண " "மாட்டீர்கள். மாறாக, அந்த சாதனம் உங்கள் செய்திகளை இனி புரிந்துகொள்ள முடியாது." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "இந்த விசையைப் பயன்படுத்தும் %s சாதனத்துடன் மறைகுறியாக்கப்பட்ட செய்திகளை நீங்கள் " "பரிமாறிக்கொள்ள முடியும்." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "பின்" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "நிர்வகிக்கவும்" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "இந்த தொடர்புக்கு புதிய சாதனங்கள் உள்ளன" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "ஓம்மோ நம்பிக்கை முடிவு தேவை" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "கணக்கு %s க்கு புதிய சாதனத்தைச் சேர்த்தீர்களா?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "இயல்பாக ஓம்மோ" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "புதிய உரையாடல்களுக்கான ஓமமோ குறியாக்கத்தை இயக்கவும்" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "புதிய சாதனங்களுக்கு குறியாக்கம்" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "இந்த தொடர்பிலிருந்து புதிய சாதனங்களுக்கு தானாக குறியாக்கம் செய்யுங்கள்." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "புதிய விசைகள்" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "உங்கள் பிற சாதனங்களிலிருந்து புதிய குறியாக்க விசைகள் தானாக ஏற்றுக்கொள்ளப்படும்." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "ஏற்றுக்கொள்ளப்பட்டது" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "நிராகரிக்கப்பட்டது" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "சரிபார்க்கப்பட்டது" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "புதிய சாதனம்" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "ஓமமோ முக்கிய மேலாண்மை" #~ msgid "Encryption" #~ msgstr "குறியாக்கம்" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO சாதனம்" #~ msgstr[1] "%d OMEMO சாதனங்கள்" #~ msgid "Automatically accept new keys" #~ msgstr "புதிய விசைகளை தானாக ஏற்றுக்கொள்ளுங்கள்" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "இந்த தொடர்பிலிருந்து புதிய குறியாக்க விசைகள் தானாக ஏற்றுக்கொள்ளப்படும்." #~ msgid "Own key" #~ msgstr "சொந்த விசை" #~ msgid "Associated keys" #~ msgstr "தொடர்புடைய விசைகள்" #~ msgid "Inactive keys" #~ msgstr "செயலற்ற விசைகள்" #~ msgid "Unused" #~ msgstr "பயன்படுத்தப்படாதது" dino-0.5.0/plugins/omemo/po/tr.po0000664000000000000000000002715514776241610015367 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-02 11:09+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s güvenilmeyen bir cihaz kullanıyor. Güvenmediğiniz cihazlardan gelen " "mesajları görmezsiniz." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Cihazları yönet" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s bu cihaza güvenmiyor. Bu, eksik mesajlarınız olabileceği anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Anahtarı Yönet" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Parmak izinin her bir karakterini, kişinizin cihazında gösterilen karakterle " "karşılaştırın." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Parmak izleri farklı" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Parmak izleri eşleşiyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "İptal" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Onayla" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Anahtarı doğrula" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Bu anahtarı kullanan cihazdan %s tarafından gönderilen gelecekteki mesajlar, " "sohbet penceresinde vurgulanacaktır." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Parmak izleri uyuşmuyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Lütfen doğru parmak izini karşılaştırdığınızdan emin olun. Parmak izleri " "eşleşmezse, %s hesabının güvenliği ihlal edilmiş olabilir ve bu anahtarı " "reddetmenizi öneririz." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Anahtar parmak izini doğrulayın" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Bu anahtarın parmak izini, kişinin cihazında görüntülenen parmak izi ile " "karşılaştırın." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Anahtarı reddet" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "Bu anahtarı kullanan kişinin cihazıyla şifreli iletişimi engelle." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Kabul" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "Bu anahtarı kullanan kişinin cihazıyla şifreli iletişime izin ver." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Bu anahtar şu anda %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "kabul ediliyor" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Bu, şifreli mesajlar almak ve göndermek için %s tarafından kullanılabileceği " "anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "onaylı" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Ayrıca, kişinin cihazındaki anahtarla eşleştiği doğrulandı." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "reddedildi" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Bu, mesajlarınızın şifresini çözmek için %s tarafından kullanılamayacağı ve " "onunla şifrelenmiş mesajları göremeyeceğiniz anlamına gelir." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Bu anahtarı kullanan %s cihazından gelen şifreli mesajları görmeyeceksiniz. " "Diğer taraftan, o cihaz artık mesajlarınızın şifresini çözemeyecek." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Bu anahtarı kullanan %s cihazıyla şifreli mesaj alışverişi yapabileceksiniz." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Geri" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Yönet" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Bu kişinin yeni cihazları var" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "OMEMO güven kararı gerekli" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "%s hesabı için yeni bir cihaz eklediniz mi?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "Öntanımlı olarak OMEMO" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Yeni sohbetler için OMEMO şifrelemesini etkinleştir" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Yeni aygıtlara şifrele" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Bu kişiden yeni aygıtlara otomatik olarak şifrele." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Yeni anahtarlar" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Diğer cihazlarınızdan gelen yeni şifreleme anahtarları otomatik olarak kabul " "edilecek." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Kabul edildi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Reddedildi" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Onaylandı" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Yeni aygıt" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO Anahtar Yönetimi" #~ msgid "Encryption" #~ msgstr "Şifreleme" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d OMEMO aygıtı" #~ msgstr[1] "%d OMEMO aygıtı" #~ msgid "Automatically accept new keys" #~ msgstr "Yeni anahtarları otomatik olarak kabul et" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Bu kişiden gelen yeni şifreleme anahtarları otomatik olarak kabul " #~ "edilecek." #~ msgid "Own key" #~ msgstr "Kendi anahtarınız" #~ msgid "Associated keys" #~ msgstr "İlişkili anahtarlar" #~ msgid "Inactive keys" #~ msgstr "Etkin olmayan anahtarlar" #~ msgid "Unused" #~ msgstr "Kullanılmamış" #~ msgid "Own fingerprint" #~ msgstr "Kendi parmak iziniz" #~ msgid "Will be generated on first connection" #~ msgstr "İlk bağlantıda oluşturulacak" #~ msgid "Your contact" #~ msgstr "Kişiniz" #~ msgid "Not matching" #~ msgstr "Eşleşmiyor" #~ msgid "Matching" #~ msgstr "Eşleşti" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "Onaylandıktan sonra, bu anahtarı kullanarak %s tarafından gönderilen " #~ "gelecek iletiler sohbet penceresinde buna göre vurgulanır." #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "" #~ "İlişkili kişiyle iletişim kurarken bu anahtarı kabul etmeyi durdurun." #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "İlişkili kişiyle iletişim için bu anahtarı kabul edin" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "" #~ "Bu, %s tarafından ileti almak için kullanılamayacağı ve gönderdiği " #~ "iletilerin yok sayılacağı anlamına gelir." #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "Onaylandıktan sonra, bu anahtarı kullanarak %s tarafından gönderilen " #~ "gelecek iletiler yok sayılır ve iletilerinizden hiçbiri bu anahtar " #~ "kullanılarak okunmaz." #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "" #~ "Onaylandıktan sonra bu anahtar %s tarafından mesaj almak ve göndermek " #~ "için kullanılabilir." #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "" #~ "Bu kişi hesaplarına yeni şifreleme anahtarları eklediğinde, otomatik " #~ "olarak kabul edilir." #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "" #~ "Hesabınıza yeni şifreleme anahtarları eklediğinizde, bunları otomatik " #~ "olarak kabul edin." dino-0.5.0/plugins/omemo/po/uk.po0000664000000000000000000002765014776241610015361 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s використовував(ла) ненадійний пристрій. Ви не будете бачити повідомлення " "від пристроїв, яким не довіряєте." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Керування пристроями" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s не довіряє цьому пристрою. Це означає, що ви можете не отримувати " "повідомлення." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Керування ключем" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "Порівняйте відбиток, літеру за літерою з тим, що відібражається на пристрої " "вашого контакту." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Відбитки відрізняються" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Відбитки збігаються" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Скасувати" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Підтвердити" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Підтвердити ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Майбутні повідомлення надіслані %s з пристрія, який використовує цей ключ, " "будуть відповідно виделені у вікні чату." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Відбитки не збігаються" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Будьласка перевірте, що ви порівнюєте саме той відбиток. Якщо відбитки не " "збігаються, обліковий запис %s може бути скомпрометовано, і вам слід " "відхилити цей ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Підтвердити відбиток ключа" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "Порівняйте цей відбиток ключа з відбитком, який відображається у пристрої " "контакта." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Відхилити ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "" "Заблокувати шифрований зв'язок з пристроєм контакта, який викорустовує цей " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Прийняти ключ" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "" "Дозволити шифрований зв'язок з пристроєм контакта, який викорустовує цей " "ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Цей ключ зараз є %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "прийнято" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Це означає, що %s тепер може приймати та відсилати шифровані повідомлення." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "перевірено" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "Додатково була підтверджена відповідність ключа на пристрої контакту." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "відхилено" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Це означає, що він не може бути задіян %s щоб дешифрувати ваші повідомлення " "і ви не зможете побачити повідомлень зашифрованих ним." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Ви не побачете шифровані повідомлення з пристрія %s, що використовує цей " "ключ. І навпаки, цей пристрій більше не зможе розшифрувати ваші повідомлення." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Ви можете обмінюватися шифрованими повідомленнями з пристріем %s, що " "використовує цей ключ." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Назад" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Налаштувати" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "У цього контакту з'явилися нові пристрої" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Потрібне рішення OMEMO про довіру" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Чи додавали ви новий пристрій до облікового запису %s?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "OMEMO за замовчуванням" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "Увімкнути шифрування OMEMO для нових розмов" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "Шифрувати до нових пристроїв" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "Одразу шифрувати до нових пристроїв цього контакту." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Нові ключи" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Нові ключи шифрування з ваших інших пристроїв будуть прийматися автоматично." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "Цей пристрій" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "Інший пристрій" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "Пристрій" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Прийнятий" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Відхилений" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Перевірений" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "Новий пристрій" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "Кожен пристрій має власний ключ OMEMO. Повідомлення можуть бути розшифровані " "пристроєм, лише якщо вони зашифровані його ключем. Повідомлення шифруються " "лише для підтримуваних пристроїв." #~ msgid "OMEMO Key Management" #~ msgstr "Управління ключами OMEMO" #~ msgid "Encryption" #~ msgstr "Шифрування" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d пристрій OMEMO" #~ msgstr[1] "%d пристрої OMEMO" #~ msgstr[2] "%d пристроїв OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Автоматично прийняти нові ключи" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "" #~ "Нові ключи шифрування від цього контакта будуть прийматися автоматично." #~ msgid "Own key" #~ msgstr "Власний ключ" #~ msgid "Associated keys" #~ msgstr "Асоційовані ключі" #~ msgid "Inactive keys" #~ msgstr "Неактивні ключи" #~ msgid "Unused" #~ msgstr "Невикористаний" #~ msgid "Own fingerprint" #~ msgstr "Власний відбиток" #~ msgid "Will be generated on first connection" #~ msgstr "Буде згенеровано при першому з'єднанні" #~ msgid "Your contact" #~ msgstr "Ваш контакт" dino-0.5.0/plugins/omemo/po/vi.po0000664000000000000000000002377214776241610015361 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-04-28 07:51+0000\n" "Language-Team: none\n" "Language: vi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.18-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "" "%s đang sử dụng một thiết bị chưa được tin tưởng. Bạn sẽ không nhìn thấy tin " "nhắn từ các thiệt bị bạn không tin tưởng." #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "Quản lý thiết bị" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "" "%s không tin tưởng thiết bị này. Điều đó có nghĩa là bạn có thể bị thiếu tin " "nhắn." #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "Quản lý khóa" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "" "So sánh chuỗi fingerprint từng ký tự một với chuỗi được hiển thị trên thiết " "bị của liên hệ của bạn." #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "Fingerprints khác nhau" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "Fingerprints trùng khớp" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "Huỷ" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "Xác nhận" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "Xác thực khoá" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "" "Tin nhắn được gửi trong tương lai bởi %s từ thiết bị sử dụng khoá này sẽ " "được làm nổi bật trong cửa sổ trò chuyện." #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "Fingerprints không khớp" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "Vui lòng xác minh rằng bạn đang so sánh đúng chuỗi fingerprint. Nếu " "fingerprints không khớp, tài khoản của %s có thể đã bị xâm phạm và bạn nên " "cân nhắc từ chối khoá này." #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "Xác thực khoá fingerprint" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "" "So sáng chuỗi fingerprint của khoá này với chuỗi fingerprint được hiển thị " "trên thiết bị của người liên hệ." #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "Từ chối khoá" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "Chặn liên lạc được mã hóa với thiết bị của liên hệ sử dụng khóa này." #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "Chấp nhận khóa" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "Cho phép giao tiếp mã hóa với thiết bị của liên hệ sử dụng khóa này." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "Khoá này hiện %s." #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "đã chấp nhận" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "" "Điều này có nghĩa là nó có thể được sử dụng bởi %s để nhận và gửi tin nhắn " "được mã hóa." #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "đã xác thực" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "" "Ngoài ra, nó đã được xác thực trùng khớp với khóa trên thiết bị của người " "liên hệ." #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "đã bị từ chối" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "" "Điều này có nghĩa là %s không thể sử dụng nó để giải mã tin nhắn của bạn và " "bạn sẽ không thấy tin nhắn được mã hóa bằng nó." #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "Bạn sẽ không thấy tin nhắn được mã hóa từ thiết bị của %s sử dụng khóa này. " "Ngược lại, thiết bị đó cũng sẽ không thể giải mã tin nhắn của bạn nữa." #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "" "Bạn sẽ có thể trao đổi tin nhắn được mã hóa với thiết bị của %s sử dụng khóa " "này." #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "Quay lại" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "Quản lý" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "Liên hệ này có thiết bị mới" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "Yêu cầu quyết định ủy thác OMEMO" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "Bạn đã thêm một thiết bị mới cho tài khoản %s à?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "Khoá mới" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "" "Khóa mã hóa mới từ các thiết bị khác của bạn sẽ được chấp nhận tự động." #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "Đã chấp nhận" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "Đã từ chối" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "Đã xác thực" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "Quản lý khóa OMEMO" #~ msgid "Encryption" #~ msgstr "Mã hoá" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d thiết bị OMEMO" #~ msgid "Automatically accept new keys" #~ msgstr "Tự động chấp nhận khóa mới" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "Các khóa mã hóa mới từ liên hệ này sẽ được chấp nhận tự động." #~ msgid "Own key" #~ msgstr "Khoá của bạn" #~ msgid "Associated keys" #~ msgstr "Các khóa đã liên kết" #~ msgid "Inactive keys" #~ msgstr "Các khoá không hoạt động" #~ msgid "Unused" #~ msgstr "Chưa được sử dụng" #~ msgid "Own fingerprint" #~ msgstr "Fingerprint đang sở hữu" #~ msgid "Will be generated on first connection" #~ msgstr "Sẽ được tạo trong lần kết nối đầu tiên" dino-0.5.0/plugins/omemo/po/zh_CN.po0000664000000000000000000002577414776241610015750 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "%s 正在使用未受信任的设备。您将无法查看来自您未信任的设备的消息。" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "管理设备" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s 未信任此设备,这意味着您可能会丢失消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "管理密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "请将指纹逐个字符与对方设备上显示的指纹进行比对。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "指纹不匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "指纹匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "取消" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "确认" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "验证密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "%s 今后从使用此密钥的设备发送的消息将在聊天窗口中相应突出显示。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "指纹不匹配" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "请验证您正在比对的指纹是否正确。如果指纹不匹配,%s 的账号可能已被盗用,您应考" "虑拒绝此密钥。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "验证密钥指纹" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "请将此密钥的指纹与对方设备上显示的指纹进行比对。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "拒绝密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "阻止与使用此密钥的对方设备进行加密通信。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "接受密钥" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "允许与使用此密钥的对方设备进行加密通信。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "此密钥当前%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "已接受" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "这意味着 %s 可以使用此密钥来接收和发送加密消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "已验证" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "此外,已验证其与对方设备上的密钥匹配。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "已拒绝" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "这意味着 %s 无法使用它来解密您的消息,您将无法查看用它加密的消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "您将无法查看来自 %s 的使用此密钥的设备的加密消息。反之,该设备将无法再解密您" "的消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "您将能够与 %s 的使用此密钥的设备交换加密消息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "返回" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "此联系人有新设备" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "需要 OMEMO 信任决定" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "您是否为账号 %s 添加了新设备?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "默认使用 OMEMO" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "为新对话启用 OMEMO 加密" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "加密新设备" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "自动加密此联系人的新设备。" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "新密钥" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "将自动接受来自您其他设备的新加密密钥。" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "此设备" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "其他设备" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "设备" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "已接受" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "已拒绝" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "已验证" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "新设备" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" "每个设备都有自己的 OMEMO 密钥。设备只能解密对设备密钥加密的消息。消息仅对已接" "受的设备进行加密。" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO 密钥管理" #~ msgid "Encryption" #~ msgstr "加密" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d 个 OMEMO 设备" #~ msgid "Automatically accept new keys" #~ msgstr "自动接受新密钥" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "将自动接受来自此联系人的新加密密钥。" #~ msgid "Own key" #~ msgstr "自己的密钥" #~ msgid "Associated keys" #~ msgstr "关联的密钥" #~ msgid "Inactive keys" #~ msgstr "非活动密钥" #~ msgid "Unused" #~ msgstr "不再使用" #~ msgid "Own fingerprint" #~ msgstr "自己的指纹" #~ msgid "Will be generated on first connection" #~ msgstr "将在第一次连接时生成" #~ msgid "Your contact" #~ msgstr "您的联系人" #~ msgid "Not matching" #~ msgstr "不匹配" #~ msgid "Matching" #~ msgstr "匹配" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一旦确认,未来由%s使用此密钥发送的任何消息将在聊天窗口中相应突出显示。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "在与其关联的联系人通信期间停止接受此密钥。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "在与其关联的联系人通信期间开始接受此密钥" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "这意味着%s不能使用它来接收消息, 它发送的任何消息都将被忽略。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一旦确认,由%s使用此密钥发送的任何未来消息都将被忽略,并且使用此密钥将无法" #~ "读取任何消息。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "一旦确认,此密钥将被%s用于接收和发送消息。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "当此联系人向其帐户添加新的加密密钥时,自动接受它们。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "当您向帐户添加新的加密密钥时,会自动接受它们。" #~ msgid "Unknown device (0x%.8x)" #~ msgstr "未知设备 (0x%.8x)" #~ msgid "Other devices" #~ msgstr "其他设备" #~ msgid "- None -" #~ msgstr "- 无 -" #~ msgid "Database error" #~ msgstr "数据库错误" dino-0.5.0/plugins/omemo/po/zh_TW.po0000664000000000000000000002543214776241610015771 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-11-24 05:28+0000\n" "Language-Team: Chinese (Traditional) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/omemo/src/ui/bad_messages_populator.vala:168 #, c-format msgid "" "%s has been using an untrusted device. You won't see messages from devices " "that you do not trust." msgstr "%s 使用了未信任的裝置。您不會看見來自未被您信任裝置的訊息。" #: plugins/omemo/src/ui/bad_messages_populator.vala:169 msgid "Manage devices" msgstr "管理裝置" #: plugins/omemo/src/ui/bad_messages_populator.vala:171 #, c-format msgid "" "%s does not trust this device. That means, you might be missing messages." msgstr "%s 不信任這部裝置。這意味著您可能會遺漏訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:34 msgid "Manage Key" msgstr "管理金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:35 msgid "" "Compare the fingerprint, character by character, with the one shown on your " "contact's device." msgstr "將指紋與顯示在聯絡人裝置上的指紋逐個字元地進行比較。" #: plugins/omemo/src/ui/manage_key_dialog.vala:36 msgid "Fingerprints differ" msgstr "指紋不同" #: plugins/omemo/src/ui/manage_key_dialog.vala:37 msgid "Fingerprints match" msgstr "指紋吻合" #: plugins/omemo/src/ui/manage_key_dialog.vala:38 #: plugins/omemo/src/ui/manage_key_dialog.vala:81 #: plugins/omemo/src/ui/manage_key_dialog.vala:87 msgid "Cancel" msgstr "取消" #: plugins/omemo/src/ui/manage_key_dialog.vala:39 msgid "Confirm" msgstr "確定" #: plugins/omemo/src/ui/manage_key_dialog.vala:59 msgid "Verify key" msgstr "核驗金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:60 #, c-format msgid "" "Future messages sent by %s from the device that uses this key will be " "highlighted accordingly in the chat window." msgstr "未來由 %s 從使用了此金鑰的裝置所傳送的訊息都將會在對話視窗內加亮顯示。" #: plugins/omemo/src/ui/manage_key_dialog.vala:70 msgid "Fingerprints do not match" msgstr "指紋不吻合" #: plugins/omemo/src/ui/manage_key_dialog.vala:71 #, c-format msgid "" "Please verify that you are comparing the correct fingerprint. If " "fingerprints do not match, %s's account may be compromised and you should " "consider rejecting this key." msgstr "" "請確定您正在比較正確的指紋。如果指紋不吻合,%s 的帳號可能不安全,同時您應該考" "虑拒絕此金鑰。" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "Verify key fingerprint" msgstr "核驗金鑰指紋" #: plugins/omemo/src/ui/manage_key_dialog.vala:123 msgid "" "Compare this key's fingerprint with the fingerprint displayed on the " "contact's device." msgstr "將此金鑰的指紋與顯示在聯絡人裝置上的指紋進行比較。" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 #: plugins/omemo/src/ui/manage_key_dialog.vala:151 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:290 msgid "Reject key" msgstr "拒絕金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:125 msgid "" "Block encrypted communication with the contact's device that uses this key." msgstr "封鎖與該聯絡人使用此金鑰的裝置之間的加密通訊。" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 #: plugins/omemo/src/ui/manage_key_dialog.vala:159 #: plugins/omemo/src/ui/encryption_preferences_entry.vala:285 msgid "Accept key" msgstr "接收金鑰" #: plugins/omemo/src/ui/manage_key_dialog.vala:127 msgid "" "Allow encrypted communication with the contact's device that uses this key." msgstr "允許與該聯絡人使用此金鑰的裝置之間的加密通訊。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "This key is currently %s." msgstr "此金鑰目前%s。" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 msgid "accepted" msgstr "已接收" #: plugins/omemo/src/ui/manage_key_dialog.vala:131 #: plugins/omemo/src/ui/manage_key_dialog.vala:136 #, c-format msgid "This means it can be used by %s to receive and send encrypted messages." msgstr "這表示 %s 可以使用它來傳送及收受加密訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "verified" msgstr "已核驗" #: plugins/omemo/src/ui/manage_key_dialog.vala:136 msgid "" "Additionally it has been verified to match the key on the contact's device." msgstr "此外,已經核驗它與聯絡人裝置上的金鑰吻合。" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 msgid "rejected" msgstr "已拒絕" #: plugins/omemo/src/ui/manage_key_dialog.vala:140 #, c-format msgid "" "This means it cannot be used by %s to decipher your messages, and you won't " "see messages encrypted with it." msgstr "這表示 %s 無法使用它來解密您的訊息,同時您不會看到使用它加密的訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:152 #, c-format msgid "" "You won't see encrypted messages from the device of %s that uses this key. " "Conversely, that device won't be able to decipher your messages anymore." msgstr "" "您不會看到從 %s 使用此金鑰的裝置所傳送的加密訊息。同時,該裝置亦無法再解密您" "的訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:160 #, c-format msgid "" "You will be able to exchange encrypted messages with the device of %s that " "uses this key." msgstr "您將能夠與 %s 所使用此金鑰的裝置互相傳送加密訊息。" #: plugins/omemo/src/ui/manage_key_dialog.vala:166 msgid "Back" msgstr "返回" #: plugins/omemo/src/ui/device_notification_populator.vala:73 msgid "Manage" msgstr "管理" #: plugins/omemo/src/ui/device_notification_populator.vala:80 msgid "This contact has new devices" msgstr "此連絡人有新的裝置" #: plugins/omemo/src/ui/own_notifications.vala:29 msgid "OMEMO trust decision required" msgstr "需要 OMEMO 信任決策" #: plugins/omemo/src/ui/own_notifications.vala:31 #, c-format msgid "Did you add a new device for account %s?" msgstr "您是否有爲帳號 %s 增加新裝置?" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:54 msgid "OMEMO by default" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:55 msgid "Enable OMEMO encryption for new conversations" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:56 msgid "Encrypt to new devices" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:57 msgid "Automatically encrypt to new devices from this contact." msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:58 msgid "New keys" msgstr "新金鑰" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:102 msgid "" "New encryption keys from your other devices will be accepted automatically." msgstr "來自您其它裝置的新加密金鑰將會被自動接收。" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:162 msgid "This device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Other device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:222 msgid "Device" msgstr "" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:224 msgid "Accepted" msgstr "已接收" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:227 msgid "Rejected" msgstr "已拒絕" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:230 msgid "Verified" msgstr "已核驗" #: plugins/omemo/src/ui/encryption_preferences_entry.vala:279 msgid "New device" msgstr "" #: plugins/omemo/data/encryption_preferences_entry.ui:8 msgid "" "Each device has its own OMEMO key. Messages can only be decrypted by a " "device if they are encrypted to its key. Messages are only encrypted to " "accepted devices." msgstr "" #~ msgid "OMEMO Key Management" #~ msgstr "OMEMO 金鑰管理" #~ msgid "Encryption" #~ msgstr "加密" #, c-format #~ msgid "%d OMEMO device" #~ msgid_plural "%d OMEMO devices" #~ msgstr[0] "%d 個 OMEMO 裝置" #~ msgid "Automatically accept new keys" #~ msgstr "自動接收新金鑰" #~ msgid "" #~ "New encryption keys from this contact will be accepted automatically." #~ msgstr "新的來自此聯絡人的加密金鑰將會被自動接收。" #~ msgid "Own key" #~ msgstr "本裝置金鑰" #~ msgid "Associated keys" #~ msgstr "其它裝置上的金鑰" #~ msgid "Inactive keys" #~ msgstr "長時間未用過的金鑰" #~ msgid "Unused" #~ msgstr "不再使用" #~ msgid "Own fingerprint" #~ msgstr "本裝置指紋" #~ msgid "Will be generated on first connection" #~ msgstr "將會在第一次連線時產生" #~ msgid "Your contact" #~ msgstr "您的聯絡人" #~ msgid "Not matching" #~ msgstr "不吻合" #~ msgid "Matching" #~ msgstr "吻合" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "highlighted accordingly in the chat window." #~ msgstr "" #~ "一經確定,所有未來由 %s 使用此金鑰傳送的訊息都會在對話視窗內被加亮。" #~ msgid "" #~ "Stop accepting this key during communication with its associated contact." #~ msgstr "在與其連結的聯絡人通訊期間停止接收此金鑰。" #~ msgid "" #~ "Start accepting this key during communication with its associated contact" #~ msgstr "在與其連結的聯絡人通訊期間開始接收此金鑰" #~ msgid "" #~ "This means it cannot be used by %s to receive messages, and any messages " #~ "sent by it will be ignored." #~ msgstr "這代表 %s 不能使用它來收受訊息,並且經它傳送的所有訊息都將被忽略。" #~ msgid "" #~ "Once confirmed, any future messages sent by %s using this key will be " #~ "ignored and none of your messages will be readable using this key." #~ msgstr "" #~ "一經確定,所有未來由 %s 使用此金鑰傳送的訊息都會被忽略,並且無法使用此金鑰" #~ "讀取您的任何訊息。" #~ msgid "" #~ "Once confirmed this key will be usable by %s to receive and send messages." #~ msgstr "一經確定,此金鑰將會被 %s 用於收受及傳送訊息。" #~ msgid "" #~ "When this contact adds new encryption keys to their account, " #~ "automatically accept them." #~ msgstr "當此聯絡人新增加密金鑰到其帳號時,自動接收它們。" #~ msgid "" #~ "When you add new encryption keys to your account, automatically accept " #~ "them." #~ msgstr "當您新增加密金鑰到您的帳號時,自動接收它們。" dino-0.5.0/plugins/omemo/src/0000775000000000000000000000000014776241610014541 5ustar rootrootdino-0.5.0/plugins/omemo/src/dtls_srtp_verification_draft.vala0000664000000000000000000003451314776241610023354 0ustar rootrootusing Omemo; using Gee; using Xmpp; namespace Dino.Plugins.Omemo.DtlsSrtpVerificationDraft { public const string NS_URI = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"; public class StreamModule : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "dtls_srtp_omemo_verification_draft"); private VerificationSendListener send_listener = new VerificationSendListener(); private HashMap device_id_by_jingle_sid = new HashMap(); private HashMap device_id_by_muji_member = new HashMap(); private HashMap> content_names_by_jingle_sid = new HashMap>(); private void on_preprocess_incoming_iq_set_get(XmppStream stream, Xmpp.Iq.Stanza iq) { if (iq.type_ != Iq.Stanza.TYPE_SET) return; Gee.List content_nodes = iq.stanza.get_deep_subnodes(Xep.Jingle.NS_URI + ":jingle", Xep.Jingle.NS_URI + ":content"); if (content_nodes.size == 0) return; string? jingle_sid = iq.stanza.get_deep_attribute(Xep.Jingle.NS_URI + ":jingle", "sid"); if (jingle_sid == null) return; Xep.Omemo.OmemoDecryptor decryptor = stream.get_module(Xep.Omemo.OmemoDecryptor.IDENTITY); foreach (StanzaNode content_node in content_nodes) { string? content_name = content_node.get_attribute("name"); if (content_name == null) continue; StanzaNode? transport_node = content_node.get_subnode("transport", Xep.JingleIceUdp.NS_URI); if (transport_node == null) continue; StanzaNode? fingerprint_node = transport_node.get_subnode("fingerprint", NS_URI); if (fingerprint_node == null) continue; StanzaNode? encrypted_node = fingerprint_node.get_subnode("encrypted", Omemo.NS_URI); if (encrypted_node == null) continue; Xep.Omemo.ParsedData? parsed_data = decryptor.parse_node(encrypted_node); if (parsed_data == null || parsed_data.ciphertext == null) continue; if (device_id_by_jingle_sid.has_key(jingle_sid) && device_id_by_jingle_sid[jingle_sid] != parsed_data.sid) { warning("Expected DTLS fingerprint to be OMEMO encrypted from %s %d, but it was from %d", iq.from.to_string(), device_id_by_jingle_sid[jingle_sid], parsed_data.sid); } foreach (Bytes encr_key in parsed_data.our_potential_encrypted_keys.keys) { parsed_data.is_prekey = parsed_data.our_potential_encrypted_keys[encr_key]; parsed_data.encrypted_key = encr_key.get_data(); try { uint8[] key = decryptor.decrypt_key(parsed_data, iq.from.bare_jid); string cleartext = decryptor.decrypt(parsed_data.ciphertext, key, parsed_data.iv); StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", Xep.JingleIceUdp.DTLS_NS_URI).add_self_xmlns() .put_node(new StanzaNode.text(cleartext)); string? hash_attr = fingerprint_node.get_attribute("hash", NS_URI); string? setup_attr = fingerprint_node.get_attribute("setup", NS_URI); if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr); if (setup_attr != null) new_fingerprint_node.put_attribute("setup", setup_attr); transport_node.put_node(new_fingerprint_node); device_id_by_jingle_sid[jingle_sid] = parsed_data.sid; if (!content_names_by_jingle_sid.has_key(content_name)) { content_names_by_jingle_sid[content_name] = new ArrayList(); } content_names_by_jingle_sid[content_name].add(content_name); stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.begin(jingle_sid, (_, res) => { Xep.Jingle.Session? session = stream.get_flag(Xep.Jingle.Flag.IDENTITY).get_session.end(res); if (session == null || !session.contents_map.has_key(content_name)) return; var encryption = new OmemoContentEncryption(NS_URI, "OMEMO", iq.from.bare_jid, device_id_by_jingle_sid[jingle_sid]); session.contents_map[content_name].encryptions[NS_URI] = encryption; if (iq.stanza.get_deep_attribute(Xep.Jingle.NS_URI + ":jingle", "action") == "session-accept") { session.additional_content_add_incoming.connect(on_content_add_received); } }); break; } catch (Error e) { debug("Decrypting message from %s/%d failed: %s", iq.from.bare_jid.to_string(), parsed_data.sid, e.message); } } } } private void on_preprocess_outgoing_iq_set_get(XmppStream stream, Xmpp.Iq.Stanza iq) { if (iq.type_ != Iq.Stanza.TYPE_SET) return; StanzaNode? jingle_node = iq.stanza.get_subnode("jingle", Xep.Jingle.NS_URI); if (jingle_node == null) return; int device_id = -1; string? sid = jingle_node.get_attribute("sid", Xep.Jingle.NS_URI); if (sid != null && device_id_by_jingle_sid.has_key(sid)) { device_id = device_id_by_jingle_sid[sid]; } StanzaNode? muji_node = jingle_node.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node != null) { string muji_room = muji_node.get_attribute("room"); try { Jid muji_jid = new Jid(muji_room); if (device_id_by_muji_member.has_key(@"$(muji_jid.bare_jid)/$(iq.to)")) { device_id = device_id_by_muji_member[@"$(muji_jid.bare_jid)/$(iq.to)"]; } } catch (InvalidJidError e) { // Ignore } } if (device_id == -1) return; Gee.List content_nodes = jingle_node.get_subnodes("content", Xep.Jingle.NS_URI); if (content_nodes.size == 0) return; foreach (StanzaNode content_node in content_nodes) { StanzaNode? transport_node = content_node.get_subnode("transport", Xep.JingleIceUdp.NS_URI); if (transport_node == null) continue; StanzaNode? fingerprint_node = transport_node.get_subnode("fingerprint", Xep.JingleIceUdp.DTLS_NS_URI); if (fingerprint_node == null) continue; string fingerprint = fingerprint_node.get_deep_string_content(); StanzaNode? encrypted_node = null; try { Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY); Xep.Omemo.EncryptionData enc_data = encryptor.encrypt_plaintext(fingerprint); encryptor.encrypt_key(enc_data, iq.to.bare_jid, device_id); encrypted_node = enc_data.get_encrypted_node(); } catch (Error e) { warning("Error while OMEMO-encrypting call keys: %s", e.message); return; } StanzaNode new_fingerprint_node = new StanzaNode.build("fingerprint", NS_URI).add_self_xmlns().put_node(encrypted_node); string? hash_attr = fingerprint_node.get_attribute("hash", Xep.JingleIceUdp.DTLS_NS_URI); string? setup_attr = fingerprint_node.get_attribute("setup", Xep.JingleIceUdp.DTLS_NS_URI); if (hash_attr != null) new_fingerprint_node.put_attribute("hash", hash_attr); if (setup_attr != null) new_fingerprint_node.put_attribute("setup", setup_attr); transport_node.put_node(new_fingerprint_node); transport_node.sub_nodes.remove(fingerprint_node); } } private void on_message_received(XmppStream stream, Xmpp.MessageStanza message) { StanzaNode? proceed_node = message.stanza.get_subnode("proceed", Xep.JingleMessageInitiation.NS_URI); if (proceed_node == null) return; string? jingle_sid = proceed_node.get_attribute("id"); if (jingle_sid == null) return; StanzaNode? device_node = proceed_node.get_subnode("device", NS_URI); if (device_node == null) return; int device_id = device_node.get_attribute_int("id", -1); if (device_id == -1) return; device_id_by_jingle_sid[jingle_sid] = device_id; } private void on_session_initiate_received(XmppStream stream, Xep.Jingle.Session session) { if (device_id_by_jingle_sid.has_key(session.sid)) { foreach (Xep.Jingle.Content content in session.contents) { on_content_add_received(stream, content); } } session.additional_content_add_incoming.connect(on_content_add_received); } private void on_content_add_received(XmppStream stream, Xep.Jingle.Content content) { if (!content_names_by_jingle_sid.has_key(content.session.sid) || content_names_by_jingle_sid[content.session.sid].contains(content.content_name)) { var encryption = new OmemoContentEncryption(NS_URI, "OMEMO", content.peer_full_jid.bare_jid, device_id_by_jingle_sid[content.session.sid]); content.encryptions[encryption.encryption_ns] = encryption; } } private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { StanzaNode? muji_node = presence.stanza.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node == null) return; StanzaNode device_node = new StanzaNode.build("device", NS_URI).add_self_xmlns() .put_attribute("id", stream.get_module(Omemo.StreamModule.IDENTITY).store.local_registration_id.to_string()); muji_node.put_node(device_node); } private void on_received_available(XmppStream stream, Presence.Stanza presence) { StanzaNode? muji_node = presence.stanza.get_subnode("muji", Xep.Muji.NS_URI); if (muji_node == null) return; StanzaNode? device_node = muji_node.get_subnode("device", NS_URI); if (device_node == null) return; int device_id = device_node.get_attribute_int("id", -1); if (device_id == -1) return; StanzaNode? muc_x_node = presence.stanza.get_subnode("x", "http://jabber.org/protocol/muc#user"); if (muc_x_node == null) return; StanzaNode? item_node = muc_x_node.get_subnode("item"); if (item_node == null) return; Jid? real_jid = null; try { string jid_attribute = item_node.get_attribute("jid"); if (jid_attribute == null) return; real_jid = new Jid(jid_attribute); } catch (InvalidJidError e) { // Ignore return; } device_id_by_muji_member[@"$(presence.from.bare_jid)/$(real_jid)"] = device_id; } public override void attach(XmppStream stream) { stream.get_module(Xmpp.MessageModule.IDENTITY).received_message.connect(on_message_received); stream.get_module(Xmpp.MessageModule.IDENTITY).send_pipeline.connect(send_listener); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_incoming_iq_set_get.connect(on_preprocess_incoming_iq_set_get); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_outgoing_iq_set_get.connect(on_preprocess_outgoing_iq_set_get); stream.get_module(Xep.Jingle.Module.IDENTITY).session_initiate_received.connect(on_session_initiate_received); stream.get_module(Xmpp.Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); stream.get_module(Xmpp.Presence.Module.IDENTITY).received_available.connect(on_received_available); } public override void detach(XmppStream stream) { stream.get_module(Xmpp.MessageModule.IDENTITY).received_message.disconnect(on_message_received); stream.get_module(Xmpp.MessageModule.IDENTITY).send_pipeline.disconnect(send_listener); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_incoming_iq_set_get.disconnect(on_preprocess_incoming_iq_set_get); stream.get_module(Xmpp.Iq.Module.IDENTITY).preprocess_outgoing_iq_set_get.disconnect(on_preprocess_outgoing_iq_set_get); stream.get_module(Xep.Jingle.Module.IDENTITY).session_initiate_received.disconnect(on_session_initiate_received); stream.get_module(Xmpp.Presence.Module.IDENTITY).received_available.disconnect(on_received_available); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class VerificationSendListener : StanzaListener { private string[] after_actions_const = {}; public override string action_group { get { return "REWRITE_NODES"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(XmppStream stream, MessageStanza message) { StanzaNode? proceed_node = message.stanza.get_subnode("proceed", Xep.JingleMessageInitiation.NS_URI); if (proceed_node == null) return false; StanzaNode device_node = new StanzaNode.build("device", NS_URI).add_self_xmlns() .put_attribute("id", stream.get_module(Omemo.StreamModule.IDENTITY).store.local_registration_id.to_string()); proceed_node.put_node(device_node); return false; } } public class OmemoContentEncryption : Xep.Jingle.ContentEncryption { public Jid jid { get; set; } public int sid { get; set; } public OmemoContentEncryption(string encryption_ns, string encryption_name, Jid jid, int sid) { base(encryption_ns, encryption_name); this.jid = jid; this.sid = sid; } } } dino-0.5.0/plugins/omemo/src/file_transfer/0000775000000000000000000000000014776241610017364 5ustar rootrootdino-0.5.0/plugins/omemo/src/file_transfer/file_decryptor.vala0000664000000000000000000000727614776241610023257 0ustar rootrootusing Dino.Entities; using Crypto; using Omemo; namespace Dino.Plugins.Omemo { public class OmemoHttpFileReceiveData : HttpFileReceiveData { public string original_url; } public class OmemoFileDecryptor : FileDecryptor, Object { private Regex url_regex = /^aesgcm:\/\/(.*)#(([A-Fa-f0-9]{2}){48}|([A-Fa-f0-9]{2}){44})$/; public Encryption get_encryption() { return Encryption.OMEMO; } public FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { HttpFileReceiveData? http_receive_data = receive_data as HttpFileReceiveData; if (http_receive_data == null) assert(false); if ((receive_data as OmemoHttpFileReceiveData) != null) return receive_data; var omemo_http_receive_data = new OmemoHttpFileReceiveData(); omemo_http_receive_data.url = aesgcm_to_https_link(http_receive_data.url); omemo_http_receive_data.original_url = http_receive_data.url; return omemo_http_receive_data; } public FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { if (file_meta.file_name != null) { file_meta.file_name = file_meta.file_name.split("#")[0]; } return file_meta; } public bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { HttpFileReceiveData? http_file_receive = receive_data as HttpFileReceiveData; if (http_file_receive == null) return false; return this.url_regex.match(http_file_receive.url) || (receive_data as OmemoHttpFileReceiveData) != null; } public async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError { const uint KEY_SIZE = 32; try { OmemoHttpFileReceiveData? omemo_http_receive_data = receive_data as OmemoHttpFileReceiveData; if (omemo_http_receive_data == null) assert(false); // Decode IV and key MatchInfo match_info; this.url_regex.match(omemo_http_receive_data.original_url, 0, out match_info); uint8[] iv_and_key = hex_to_bin(match_info.fetch(2).up()); uint8[] iv = iv_and_key[0:iv_and_key.length-KEY_SIZE]; uint8[] key = iv_and_key[iv_and_key.length-KEY_SIZE:iv_and_key.length]; file_transfer.encryption = Encryption.OMEMO; debug("Decrypting file %s from %s", file_transfer.file_name, file_transfer.server_file_name); SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(key); cipher.set_iv(iv); return new ConverterInputStream(encrypted_stream, new SymmetricCipherDecrypter((owned) cipher, 16)); } catch (Crypto.Error e) { throw new FileReceiveError.DECRYPTION_FAILED("OMEMO file decryption error: %s".printf(e.message)); } catch (GLib.Error e) { throw new FileReceiveError.DECRYPTION_FAILED("OMEMO file decryption error: %s".printf(e.message)); } } private uint8[] hex_to_bin(string hex) { uint8[] bin = new uint8[hex.length / 2]; const string HEX = "0123456789ABCDEF"; for (int i = 0; i < hex.length / 2; i++) { bin[i] = (uint8) (HEX.index_of_char(hex[i*2]) << 4) | HEX.index_of_char(hex[i*2+1]); } return bin; } private string aesgcm_to_https_link(string aesgcm_link) { MatchInfo match_info; this.url_regex.match(aesgcm_link, 0, out match_info); return "https://" + match_info.fetch(1); } } } dino-0.5.0/plugins/omemo/src/file_transfer/file_encryptor.vala0000664000000000000000000000544714776241610023267 0ustar rootrootusing Gee; using Gtk; using Crypto; using Dino.Entities; using Xmpp; using Omemo; namespace Dino.Plugins.Omemo { public class OmemoHttpFileMeta : HttpFileMeta { public uint8[] iv; public uint8[] key; } public class OmemoFileEncryptor : Dino.FileEncryptor, Object { public bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer) { return file_transfer.encryption == Encryption.OMEMO; } public FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError { const uint KEY_SIZE = 32; const uint IV_SIZE = 12; var omemo_http_file_meta = new OmemoHttpFileMeta(); try { //Create a key and use it to encrypt the file uint8[] iv = new uint8[IV_SIZE]; Plugin.get_context().randomize(iv); uint8[] key = new uint8[KEY_SIZE]; Plugin.get_context().randomize(key); SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(key); cipher.set_iv(iv); omemo_http_file_meta.iv = iv; omemo_http_file_meta.key = key; omemo_http_file_meta.size = file_transfer.size + 16; omemo_http_file_meta.mime_type = "application/octet-stream"; file_transfer.input_stream = new ConverterInputStream(file_transfer.input_stream, new SymmetricCipherEncrypter((owned) cipher, 16)); } catch (Crypto.Error error) { throw new FileSendError.ENCRYPTION_FAILED("OMEMO file encryption error: %s".printf(error.message)); } catch (GLib.Error error) { throw new FileSendError.ENCRYPTION_FAILED("OMEMO file encryption error: %s".printf(error.message)); } debug("Encrypting file %s as %s", file_transfer.file_name, file_transfer.server_file_name); return omemo_http_file_meta; } public FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return null; OmemoHttpFileMeta? omemo_http_file_meta = file_meta as OmemoHttpFileMeta; if (omemo_http_file_meta == null) return null; // Convert iv and key to hex string iv_and_key = ""; foreach (uint8 byte in omemo_http_file_meta.iv) iv_and_key += byte.to_string("%02x"); foreach (uint8 byte in omemo_http_file_meta.key) iv_and_key += byte.to_string("%02x"); string aesgcm_link = send_data.url_down + "#" + iv_and_key; aesgcm_link = "aesgcm://" + aesgcm_link.substring(8); // replace https:// by aesgcm:// send_data.url_down = aesgcm_link; send_data.encrypt_message = true; return file_send_data; } } } dino-0.5.0/plugins/omemo/src/jingle/0000775000000000000000000000000014776241610016011 5ustar rootrootdino-0.5.0/plugins/omemo/src/jingle/jet_omemo.vala0000664000000000000000000001216114776241610020635 0ustar rootrootusing Crypto; using Dino; using Dino.Entities; using Gee; using Omemo; using Xmpp; using Xmpp.Xep; namespace Dino.Plugins.JetOmemo { private const string NS_URI = "urn:xmpp:jingle:jet-omemo:0"; private const string AES_128_GCM_URI = "urn:xmpp:ciphers:aes-128-gcm-nopadding"; public class Module : XmppStreamModule, Jet.EnvelopEncoding { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "0396_jet_omemo"); const uint KEY_SIZE = 16; const uint IV_SIZE = 12; public override void attach(XmppStream stream) { if (stream.get_module(Jet.Module.IDENTITY) != null) { stream.get_module(ServiceDiscovery.Module.IDENTITY).add_feature(stream, NS_URI); stream.get_module(Jet.Module.IDENTITY).register_envelop_encoding(this); stream.get_module(Jet.Module.IDENTITY).register_cipher(new AesGcmCipher(KEY_SIZE, IV_SIZE, AES_128_GCM_URI)); } } public override void detach(XmppStream stream) { stream.get_module(ServiceDiscovery.Module.IDENTITY).remove_feature(stream, NS_URI); } public async bool is_available(XmppStream stream, Jid full_jid) { bool? has_feature = yield stream.get_module(ServiceDiscovery.Module.IDENTITY).has_entity_feature(stream, full_jid, NS_URI); if (has_feature == null || !(!)has_feature) { return false; } return yield stream.get_module(Xep.Jet.Module.IDENTITY).is_available(stream, full_jid); } public string get_type_uri() { return Omemo.NS_URI; } public Jet.TransportSecret decode_envolop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, StanzaNode security) throws Jingle.IqError { StanzaNode? encrypted = security.get_subnode("encrypted", Omemo.NS_URI); if (encrypted == null) throw new Jingle.IqError.BAD_REQUEST("Invalid JET-OMEMO envelop: missing encrypted element"); Xep.Omemo.OmemoDecryptor decryptor = stream.get_module(Xep.Omemo.OmemoDecryptor.IDENTITY); Xmpp.Xep.Omemo.ParsedData? data = decryptor.parse_node(encrypted); if (data == null) throw new Jingle.IqError.BAD_REQUEST("Invalid JET-OMEMO envelop: bad encrypted element"); foreach (Bytes encr_key in data.our_potential_encrypted_keys.keys) { data.is_prekey = data.our_potential_encrypted_keys[encr_key]; data.encrypted_key = encr_key.get_data(); try { uint8[] key = decryptor.decrypt_key(data, peer_full_jid.bare_jid); return new Jet.TransportSecret(key, data.iv); } catch (GLib.Error e) { debug("Decrypting JET key from %s/%d failed: %s", peer_full_jid.bare_jid.to_string(), data.sid, e.message); } } throw new Jingle.IqError.NOT_ACCEPTABLE("Not encrypted for targeted device"); } public void encode_envelop(XmppStream stream, Jid local_full_jid, Jid peer_full_jid, Jet.SecurityParameters security_params, StanzaNode security) { Store store = stream.get_module(Omemo.StreamModule.IDENTITY).store; var encryption_data = new Xep.Omemo.EncryptionData(store.local_registration_id); encryption_data.iv = security_params.secret.initialization_vector; encryption_data.keytag = security_params.secret.transport_key; Xep.Omemo.OmemoEncryptor encryptor = stream.get_module(Xep.Omemo.OmemoEncryptor.IDENTITY); encryptor.encrypt_key_to_recipient(stream, encryption_data, peer_full_jid.bare_jid); security.put_node(encryption_data.get_encrypted_node()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class AesGcmCipher : Jet.Cipher, Object { private uint key_size; private uint default_iv_size; private string uri; public AesGcmCipher(uint key_size, uint default_iv_size, string uri) { this.key_size = key_size; this.default_iv_size = default_iv_size; this.uri = uri; } public string get_cipher_uri() { return uri; } public Jet.TransportSecret generate_random_secret() { uint8[] iv = new uint8[default_iv_size]; Omemo.Plugin.get_context().randomize(iv); uint8[] key = new uint8[key_size]; Omemo.Plugin.get_context().randomize(key); return new Jet.TransportSecret(key, iv); } public InputStream wrap_input_stream(InputStream input, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) { SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(secret.transport_key); cipher.set_iv(secret.initialization_vector); return new ConverterInputStream(input, new SymmetricCipherDecrypter((owned) cipher, 16)); } public OutputStream wrap_output_stream(OutputStream output, Jet.TransportSecret secret) requires (secret.transport_key.length == key_size) { Crypto.SymmetricCipher cipher = new SymmetricCipher("AES-GCM"); cipher.set_key(secret.transport_key); cipher.set_iv(secret.initialization_vector); return new ConverterOutputStream(output, new SymmetricCipherEncrypter((owned) cipher, 16)); } } } dino-0.5.0/plugins/omemo/src/jingle/jingle_helper.vala0000664000000000000000000000356114776241610021472 0ustar rootrootusing Dino.Entities; using Xmpp; namespace Dino.Plugins.JetOmemo { public class EncryptionHelper : JingleFileEncryptionHelper, Object { private StreamInteractor stream_interactor; public EncryptionHelper(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool can_transfer(Conversation conversation) { return true; } public async bool can_encrypt(Conversation conversation, FileTransfer file_transfer, Jid? full_jid) { XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) return false; Gee.List? resources = stream.get_flag(Presence.Flag.IDENTITY).get_resources(conversation.counterpart); if (resources == null) return false; if (full_jid == null) { foreach (Jid test_jid in resources) { if (yield stream.get_module(Module.IDENTITY).is_available(stream, test_jid)) { return true; } } } else { if (yield stream.get_module(Module.IDENTITY).is_available(stream, full_jid)) { return true; } } return false; } public string? get_precondition_name(Conversation conversation, FileTransfer file_transfer) { return Xep.Jet.NS_URI; } public Object? get_precondition_options(Conversation conversation, FileTransfer file_transfer) { return new Xep.Jet.Options(Omemo.NS_URI, AES_128_GCM_URI); } public Encryption get_encryption(Xmpp.Xep.JingleFileTransfer.FileTransfer jingle_transfer) { Xep.Jet.SecurityParameters? security = jingle_transfer.security as Xep.Jet.SecurityParameters; if (security != null && security.encoding.get_type_uri() == Omemo.NS_URI) { return Encryption.OMEMO; } return Encryption.NONE; } } } dino-0.5.0/plugins/omemo/src/logic/0000775000000000000000000000000014776241610015636 5ustar rootrootdino-0.5.0/plugins/omemo/src/logic/database.vala0000664000000000000000000003361614776241610020260 0ustar rootrootusing Gee; using Qlite; using Dino.Entities; namespace Dino.Plugins.Omemo { public class Database : Qlite.Database { private const int VERSION = 5; public class IdentityMetaTable : Table { //Default to provide backwards compatability public Column identity_id = new Column.Integer("identity_id") { not_null = true, min_version = 2, default = "-1" }; public Column address_name = new Column.Text("address_name") { not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column identity_key_public_base64 = new Column.Text("identity_key_public_base64"); public Column trusted_identity = new Column.BoolInt("trusted_identity") { default = "0", max_version = 1 }; public Column trust_level = new Column.Integer("trust_level") { default = TrustLevel.UNKNOWN.to_string(), min_version = 2 }; public Column now_active = new Column.BoolInt("now_active") { default = "1" }; public Column last_active = new Column.Long("last_active"); public Column last_message_untrusted = new Column.Long("last_message_untrusted") { min_version = 5 }; public Column last_message_undecryptable = new Column.Long("last_message_undecryptable") { min_version = 5 }; internal IdentityMetaTable(Database db) { base(db, "identity_meta"); init({identity_id, address_name, device_id, identity_key_public_base64, trusted_identity, trust_level, now_active, last_active, last_message_untrusted, last_message_undecryptable}); index("identity_meta_idx", {identity_id, address_name, device_id}, true); index("identity_meta_list_idx", {identity_id, address_name}); } public QueryBuilder with_address(int identity_id, string address_name) { return select().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name); } public QueryBuilder get_with_device_id(int identity_id, int device_id) { return select().with(this.identity_id, "=", identity_id).with(this.device_id, "=", device_id); } public void insert_device_list(int32 identity_id, string address_name, ArrayList devices) { update().with(this.identity_id, "=", identity_id).with(this.address_name, "=", address_name).set(now_active, false).perform(); foreach (int32 device_id in devices) { upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.now_active, true) .value(this.last_active, (long) new DateTime.now_utc().to_unix()) .perform(); } } public int64 insert_device_bundle(int32 identity_id, string address_name, int device_id, Bundle bundle, TrustLevel trust) { if (bundle == null || bundle.identity_key == null) return -1; // Do not replace identity_key if it was known before, it should never change! string identity_key = Base64.encode(bundle.identity_key.serialize()); RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row(); if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) { critical("Tried to change the identity key for a known device id. Likely an attack."); return -1; } return upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.identity_key_public_base64, identity_key) .value(this.trust_level, trust).perform(); } public int64 insert_device_session(int32 identity_id, string address_name, int device_id, string identity_key, TrustLevel trust) { RowOption row = with_address(identity_id, address_name).with(this.device_id, "=", device_id).single().row(); if (row.is_present() && row[identity_key_public_base64] != null && row[identity_key_public_base64] != identity_key) { critical("Tried to change the identity key for a known device id. Likely an attack."); return -1; } return upsert() .value(this.identity_id, identity_id, true) .value(this.address_name, address_name, true) .value(this.device_id, device_id, true) .value(this.identity_key_public_base64, identity_key) .value(this.trust_level, trust).perform(); } public void update_last_message_untrusted(int identity_id, int device_id, DateTime? time) { var stmt = update() .with(this.identity_id, "=", identity_id) .with(this.device_id, "=", device_id); if (time != null) { stmt.set(last_message_untrusted, (long)time.to_unix()); } else { stmt.set_null(last_message_untrusted); } stmt.perform(); } public void update_last_message_undecryptable(int identity_id, int device_id, DateTime? time) { var stmt = update() .with(this.identity_id, "=", identity_id) .with(this.device_id, "=", device_id); if (time != null) { stmt.set(last_message_undecryptable, (long)time.to_unix()); } else { stmt.set_null(last_message_undecryptable); } stmt.perform(); } public QueryBuilder get_trusted_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "!=", TrustLevel.UNTRUSTED) .with(this.now_active, "=", true); } public QueryBuilder get_known_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "!=", TrustLevel.UNKNOWN) .without_null(this.identity_key_public_base64); } public QueryBuilder get_unknown_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with_null(this.identity_key_public_base64); } public QueryBuilder get_new_devices(int identity_id, string address_name) { return this.with_address(identity_id, address_name) .with(this.trust_level, "=", TrustLevel.UNKNOWN) .without_null(this.identity_key_public_base64); } public Row? get_device(int identity_id, string address_name, int device_id) { return this.with_address(identity_id, address_name) .with(this.device_id, "=", device_id).single().row().inner; } } public class TrustTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.Text("address_name"); public Column blind_trust = new Column.BoolInt("blind_trust") { default = "1" } ; internal TrustTable(Database db) { base(db, "trust"); init({identity_id, address_name, blind_trust}); index("trust_idx", {identity_id, address_name}, true); } public bool get_blind_trust(int32 identity_id, string address_name, bool def = false) { RowOption row = this.select().with(this.identity_id, "=", identity_id) .with(this.address_name, "=", address_name).single().row(); if (row.is_present()) return row[blind_trust]; return def; } } public class IdentityTable : Table { public Column id = new Column.Integer("id") { primary_key = true, auto_increment = true }; public Column account_id = new Column.Integer("account_id") { unique = true, not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column identity_key_private_base64 = new Column.NonNullText("identity_key_private_base64"); public Column identity_key_public_base64 = new Column.NonNullText("identity_key_public_base64"); internal IdentityTable(Database db) { base(db, "identity"); init({id, account_id, device_id, identity_key_private_base64, identity_key_public_base64}); } public int get_id(int account_id) { int id = -1; Row? row = this.row_with(this.account_id, account_id).inner; if (row != null) id = ((!)row)[this.id]; return id; } } public class SignedPreKeyTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column signed_pre_key_id = new Column.Integer("signed_pre_key_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal SignedPreKeyTable(Database db) { base(db, "signed_pre_key"); init({identity_id, signed_pre_key_id, record_base64}); unique({identity_id, signed_pre_key_id}); index("signed_pre_key_idx", {identity_id, signed_pre_key_id}, true); } } public class PreKeyTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column pre_key_id = new Column.Integer("pre_key_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal PreKeyTable(Database db) { base(db, "pre_key"); init({identity_id, pre_key_id, record_base64}); unique({identity_id, pre_key_id}); index("pre_key_idx", {identity_id, pre_key_id}, true); } } public class SessionTable : Table { public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.NonNullText("name"); public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column record_base64 = new Column.NonNullText("record_base64"); internal SessionTable(Database db) { base(db, "session"); init({identity_id, address_name, device_id, record_base64}); unique({identity_id, address_name, device_id}); index("session_idx", {identity_id, address_name, device_id}, true); } } public class ContentItemMetaTable : Table { public Column content_item_id = new Column.Integer("message_id") { primary_key = true }; public Column identity_id = new Column.Integer("identity_id") { not_null = true }; public Column address_name = new Column.Text("address_name") { not_null = true }; public Column device_id = new Column.Integer("device_id") { not_null = true }; public Column trusted_when_received = new Column.BoolInt("trusted_when_received") { not_null = true, default = "1" }; internal ContentItemMetaTable(Database db) { base(db, "content_item_meta"); init({content_item_id, identity_id, address_name, device_id, trusted_when_received}); index("content_item_meta_device_idx", {identity_id, device_id, address_name}); } public RowOption with_content_item(ContentItem item) { return row_with(content_item_id, item.id); } public QueryBuilder with_device(int identity_id, string address_name, int device_id) { return select() .with(this.identity_id, "=", identity_id) .with(this.address_name, "=", address_name) .with(this.device_id, "=", device_id); } } public IdentityMetaTable identity_meta { get; private set; } public TrustTable trust { get; private set; } public IdentityTable identity { get; private set; } public SignedPreKeyTable signed_pre_key { get; private set; } public PreKeyTable pre_key { get; private set; } public SessionTable session { get; private set; } public ContentItemMetaTable content_item_meta { get; private set; } public Database(string fileName) { base(fileName, VERSION); identity_meta = new IdentityMetaTable(this); trust = new TrustTable(this); identity = new IdentityTable(this); signed_pre_key = new SignedPreKeyTable(this); pre_key = new PreKeyTable(this); session = new SessionTable(this); content_item_meta = new ContentItemMetaTable(this); init({identity_meta, trust, identity, signed_pre_key, pre_key, session, content_item_meta}); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set OMEMO database properties: %s", e.message); } } public override void migrate(long oldVersion) { if(oldVersion == 1) { try { exec("DROP INDEX identity_meta_idx"); exec("DROP INDEX identity_meta_list_idx"); exec("CREATE UNIQUE INDEX identity_meta_idx ON identity_meta (identity_id, address_name, device_id)"); exec("CREATE INDEX identity_meta_list_idx ON identity_meta (identity_id, address_name)"); } catch (Error e) { stderr.printf("Failed to migrate OMEMO database\n"); Process.exit(-1); } } } } } dino-0.5.0/plugins/omemo/src/logic/decrypt.vala0000664000000000000000000002426014776241610020161 0ustar rootrootusing Dino.Entities; using Qlite; using Gee; using Omemo; using Xmpp; namespace Dino.Plugins.Omemo { public class OmemoDecryptor : Xep.Omemo.OmemoDecryptor { private Account account; private Store store; private Database db; private StreamInteractor stream_interactor; private TrustManager trust_manager; public override uint32 own_device_id { get { return store.local_registration_id; }} public OmemoDecryptor(Account account, StreamInteractor stream_interactor, TrustManager trust_manager, Database db, Store store) { this.account = account; this.stream_interactor = stream_interactor; this.trust_manager = trust_manager; this.db = db; this.store = store; } public bool decrypt_message(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { StanzaNode? encrypted_node = stanza.stanza.get_subnode("encrypted", NS_URI); if (encrypted_node == null || MessageFlag.get_flag(stanza) != null || stanza.from == null) return false; if (!Plugin.ensure_context()) return false; int identity_id = db.identity.get_id(conversation.account.id); MessageFlag flag = new MessageFlag(); stanza.add_flag(flag); Xep.Omemo.ParsedData? data = parse_node(encrypted_node); if (data == null) return false; foreach (Bytes encr_key in data.our_potential_encrypted_keys.keys) { data.is_prekey = data.our_potential_encrypted_keys[encr_key]; data.encrypted_key = encr_key.get_data(); Gee.List possible_jids = get_potential_message_jids(message, data, identity_id); if (possible_jids.size == 0) { debug("Received message from unknown entity with device id %d", data.sid); } foreach (Jid possible_jid in possible_jids) { try { uint8[] key = decrypt_key(data, possible_jid); if (data.ciphertext != null) { string cleartext = arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, data.iv, data.ciphertext)); message.body = cleartext; } // If we figured out which real jid a message comes from due to decryption working, save it if (conversation.type_ == Conversation.Type.GROUPCHAT && message.real_jid == null) { message.real_jid = possible_jid; } message.encryption = Encryption.OMEMO; trust_manager.message_device_id_map[message] = data.sid; return true; } catch (Error e) { debug("Decrypting message from %s/%d failed: %s", possible_jid.to_string(), data.sid, e.message); } } } if ( data.ciphertext != null && // Ratchet forwarding doesn't contain payload and might not include us, which is ok data.our_potential_encrypted_keys.size == 0 && // The message was not encrypted to us stream_interactor.module_manager.get_module(message.account, StreamModule.IDENTITY).store.local_registration_id != data.sid // Message from this device. Never encrypted to itself. ) { db.identity_meta.update_last_message_undecryptable(identity_id, data.sid, message.time); trust_manager.bad_message_state_updated(conversation.account, message.from, data.sid); } debug("Received OMEMO encryped message that could not be decrypted."); return false; } public Gee.List get_potential_message_jids(Entities.Message message, Xmpp.Xep.Omemo.ParsedData data, int identity_id) { Gee.List possible_jids = new ArrayList(); if (message.type_ == Message.Type.CHAT) { possible_jids.add(message.from.bare_jid); } else { if (message.real_jid != null) { possible_jids.add(message.real_jid.bare_jid); } else if (data.is_prekey) { // pre key messages do store the identity key, so we can use that to find the real jid PreKeyOmemoMessage msg = Plugin.get_context().deserialize_signal_pre_key_message(data.encrypted_key); string identity_key = Base64.encode(msg.identity_key.serialize()); foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid).with(db.identity_meta.identity_key_public_base64, "=", identity_key)) { try { possible_jids.add(new Jid(row[db.identity_meta.address_name])); } catch (InvalidJidError e) { warning("Ignoring invalid jid from database: %s", e.message); } } } else { // If we don't know the device name (MUC history w/o MAM), test decryption with all keys with fitting device id foreach (Row row in db.identity_meta.get_with_device_id(identity_id, data.sid)) { try { possible_jids.add(new Jid(row[db.identity_meta.address_name])); } catch (InvalidJidError e) { warning("Ignoring invalid jid from database: %s", e.message); } } } } return possible_jids; } public override uint8[] decrypt_key(Xmpp.Xep.Omemo.ParsedData data, Jid from_jid) throws GLib.Error { int sid = data.sid; uint8[] ciphertext = data.ciphertext; uint8[] encrypted_key = data.encrypted_key; Address address = new Address(from_jid.to_string(), sid); uint8[] key; if (data.is_prekey) { int identity_id = db.identity.get_id(account.id); PreKeyOmemoMessage msg = Plugin.get_context().deserialize_signal_pre_key_message(encrypted_key); string identity_key = Base64.encode(msg.identity_key.serialize()); bool ok = update_db_for_prekey(identity_id, identity_key, from_jid, sid); if (!ok) throw new GLib.Error(-1, 0, "Failed updating db for prekey"); debug("Starting new session for decryption with device from %s/%d", from_jid.to_string(), sid); SessionCipher cipher = store.create_session_cipher(address); key = cipher.decrypt_pre_key_message(msg); // TODO: Finish session } else { debug("Continuing session for decryption with device from %s/%d", from_jid.to_string(), sid); OmemoMessage msg = Plugin.get_context().deserialize_signal_message(encrypted_key); SessionCipher cipher = store.create_session_cipher(address); key = cipher.decrypt_message(msg); } if (key.length >= 32) { int authtaglength = key.length - 16; uint8[] new_ciphertext = new uint8[ciphertext.length + authtaglength]; uint8[] new_key = new uint8[16]; Memory.copy(new_ciphertext, ciphertext, ciphertext.length); Memory.copy((uint8*)new_ciphertext + ciphertext.length, (uint8*)key + 16, authtaglength); Memory.copy(new_key, key, 16); data.ciphertext = new_ciphertext; key = new_key; } return key; } public override string decrypt(uint8[] ciphertext, uint8[] key, uint8[] iv) throws GLib.Error { return arr_to_str(aes_decrypt(Cipher.AES_GCM_NOPADDING, key, iv, ciphertext)); } private bool update_db_for_prekey(int identity_id, string identity_key, Jid from_jid, int sid) { Row? device = db.identity_meta.get_device(identity_id, from_jid.to_string(), sid); if (device != null && device[db.identity_meta.identity_key_public_base64] != null) { if (device[db.identity_meta.identity_key_public_base64] != identity_key) { critical("Tried to use a different identity key for a known device id."); return false; } } else { debug("Learn new device from incoming message from %s/%d", from_jid.to_string(), sid); bool blind_trust = db.trust.get_blind_trust(identity_id, from_jid.to_string(), true); if (db.identity_meta.insert_device_session(identity_id, from_jid.to_string(), sid, identity_key, blind_trust ? TrustLevel.TRUSTED : TrustLevel.UNKNOWN) < 0) { critical("Failed learning a device."); return false; } XmppStream? stream = stream_interactor.get_stream(account); if (device == null && stream != null) { stream.get_module(StreamModule.IDENTITY).request_user_devicelist.begin(stream, from_jid); } } return true; } private string arr_to_str(uint8[] arr) { // null-terminate the array uint8[] rarr = new uint8[arr.length+1]; Memory.copy(rarr, arr, arr.length); return (string)rarr; } } public class DecryptMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "DECRYPT"; } } public override string[] after_actions { get { return after_actions_const; } } private HashMap decryptors; public DecryptMessageListener(HashMap decryptors) { this.decryptors = decryptors; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { decryptors[message.account].decrypt_message(message, stanza, conversation); return false; } } } dino-0.5.0/plugins/omemo/src/logic/encrypt.vala0000664000000000000000000001321014776241610020164 0ustar rootrootusing Gee; using Omemo; using Dino.Entities; using Xmpp; using Xmpp.Xep.Omemo; namespace Dino.Plugins.Omemo { public class OmemoEncryptor : Xep.Omemo.OmemoEncryptor { private Account account; private Store store; private TrustManager trust_manager; public override uint32 own_device_id { get { return store.local_registration_id; }} public OmemoEncryptor(Account account, TrustManager trust_manager, Store store) { this.account = account; this.trust_manager = trust_manager; this.store = store; } public override Xep.Omemo.EncryptionData encrypt_plaintext(string plaintext) throws GLib.Error { const uint KEY_SIZE = 16; const uint IV_SIZE = 12; //Create a key and use it to encrypt the message uint8[] key = new uint8[KEY_SIZE]; Plugin.get_context().randomize(key); uint8[] iv = new uint8[IV_SIZE]; Plugin.get_context().randomize(iv); uint8[] aes_encrypt_result = aes_encrypt(Cipher.AES_GCM_NOPADDING, key, iv, plaintext.data); uint8[] ciphertext = aes_encrypt_result[0:aes_encrypt_result.length - 16]; uint8[] tag = aes_encrypt_result[aes_encrypt_result.length - 16:aes_encrypt_result.length]; uint8[] keytag = new uint8[key.length + tag.length]; Memory.copy(keytag, key, key.length); Memory.copy((uint8*)keytag + key.length, tag, tag.length); var ret = new Xep.Omemo.EncryptionData(own_device_id); ret.ciphertext = ciphertext; ret.keytag = keytag; ret.iv = iv; return ret; } public EncryptState encrypt(MessageStanza message, Jid self_jid, Gee.List recipients, XmppStream stream) { EncryptState status = new EncryptState(); if (!Plugin.ensure_context()) return status; if (message.to == null) return status; try { EncryptionData enc_data = encrypt_plaintext(message.body); status = encrypt_key_to_recipients(enc_data, self_jid, recipients, stream); message.stanza.put_node(enc_data.get_encrypted_node()); Xep.ExplicitEncryption.add_encryption_tag_to_message(message, NS_URI, "OMEMO"); message.body = "[This message is OMEMO encrypted]"; status.encrypted = true; } catch (Error e) { warning(@"error while encrypting message: $(e.message)\n"); message.body = "[OMEMO encryption failed]"; status.encrypted = false; } return status; } internal EncryptState encrypt_key_to_recipients(EncryptionData enc_data, Jid self_jid, Gee.List recipients, XmppStream stream) throws Error { EncryptState status = new EncryptState(); //Check we have the bundles and device lists needed to send the message if (!trust_manager.is_known_address(account, self_jid)) return status; status.own_list = true; status.own_devices = trust_manager.get_trusted_devices(account, self_jid).size; status.other_waiting_lists = 0; status.other_devices = 0; foreach (Jid recipient in recipients) { if (!trust_manager.is_known_address(account, recipient)) { status.other_waiting_lists++; } if (status.other_waiting_lists > 0) return status; status.other_devices += trust_manager.get_trusted_devices(account, recipient).size; } if (status.own_devices == 0 || status.other_devices == 0) return status; //Encrypt the key for each recipient's device individually foreach (Jid recipient in recipients) { EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, recipient); status.add_result(enc_res, false); } // Encrypt the key for each own device EncryptionResult enc_res = encrypt_key_to_recipient(stream, enc_data, self_jid); status.add_result(enc_res, true); return status; } public override EncryptionResult encrypt_key_to_recipient(XmppStream stream, Xep.Omemo.EncryptionData enc_data, Jid recipient) throws GLib.Error { var result = new EncryptionResult(); StreamModule module = stream.get_module(StreamModule.IDENTITY); foreach(int32 device_id in trust_manager.get_trusted_devices(account, recipient)) { if (module.is_ignored_device(recipient, device_id)) { result.lost++; continue; } try { encrypt_key(enc_data, recipient, device_id); result.success++; } catch (Error e) { if (e.code == ErrorCode.UNKNOWN) result.unknown++; else result.failure++; } } return result; } public override void encrypt_key(Xep.Omemo.EncryptionData encryption_data, Jid jid, int32 device_id) throws GLib.Error { Address address = new Address(jid.to_string(), device_id); SessionCipher cipher = store.create_session_cipher(address); CiphertextMessage device_key = cipher.encrypt(encryption_data.keytag); address.device_id = 0; debug("Created encrypted key for %s/%d", jid.to_string(), device_id); encryption_data.add_device_key(device_id, device_key.serialized, device_key.type == CiphertextType.PREKEY); } } }dino-0.5.0/plugins/omemo/src/logic/manager.vala0000664000000000000000000005040514776241610020121 0ustar rootrootusing Dino.Entities; using Omemo; using Qlite; using Xmpp; using Gee; namespace Dino.Plugins.Omemo { public class Manager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("omemo_manager"); public string id { get { return IDENTITY.id; } } private StreamInteractor stream_interactor; private Database db; private TrustManager trust_manager; private HashMap encryptors; private Map message_states = new HashMap(Entities.Message.hash_func, Entities.Message.equals_func); private class MessageState { public Entities.Message msg { get; private set; } public Xep.Omemo.EncryptState last_try { get; private set; } public int waiting_other_sessions { get; set; } public int waiting_own_sessions { get; set; } public bool waiting_own_devicelist { get; set; } public int waiting_other_devicelists { get; set; } public bool force_next_attempt { get; set; } public bool will_send_now { get; private set; } public bool active_send_attempt { get; set; } public MessageState(Entities.Message msg, Xep.Omemo.EncryptState last_try) { update_from_encrypt_status(msg, last_try); } public void update_from_encrypt_status(Entities.Message msg, Xep.Omemo.EncryptState new_try) { this.msg = msg; this.last_try = new_try; this.waiting_other_sessions = new_try.other_unknown; this.waiting_own_sessions = new_try.own_unknown; this.waiting_own_devicelist = !new_try.own_list; this.waiting_other_devicelists = new_try.other_waiting_lists; this.active_send_attempt = false; will_send_now = false; if (new_try.other_failure > 0 || (new_try.other_lost == new_try.other_devices && new_try.other_devices > 0)) { msg.marked = Entities.Message.Marked.WONTSEND; } else if (new_try.other_unknown > 0 || new_try.own_unknown > 0 || new_try.other_waiting_lists > 0 || !new_try.own_list || new_try.own_devices == 0) { msg.marked = Entities.Message.Marked.UNSENT; } else if (!new_try.encrypted) { msg.marked = Entities.Message.Marked.WONTSEND; } else { will_send_now = true; } } public bool should_retry_now() { return !waiting_own_devicelist && waiting_other_devicelists <= 0 && waiting_other_sessions <= 0 && waiting_own_sessions <= 0 && !active_send_attempt; } public string to_string() { return @"MessageState (msg=$(msg.stanza_id), send=$will_send_now, $last_try)"; } } private Manager(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap encryptors) { this.stream_interactor = stream_interactor; this.db = db; this.trust_manager = trust_manager; this.encryptors = encryptors; stream_interactor.account_added.connect(on_account_added); stream_interactor.stream_negotiated.connect(on_stream_negotiated); stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_send.connect(on_pre_message_send); stream_interactor.get_module(RosterManager.IDENTITY).mutual_subscription.connect(on_mutual_subscription); } public void clear_device_list(Account account) { XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) return; stream.get_module(StreamModule.IDENTITY).clear_device_list(stream); } private Gee.List get_occupants(Jid jid, Account account){ Gee.List occupants = new ArrayList(Jid.equals_bare_func); if(!stream_interactor.get_module(MucManager.IDENTITY).is_groupchat(jid, account)){ occupants.add(jid); } Gee.List? occupant_jids = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(jid, account); if(occupant_jids == null) { return occupants; } foreach (Jid occupant in occupant_jids) { if(!occupant.equals(account.bare_jid)){ occupants.add(occupant.bare_jid); } } return occupants; } private void on_pre_message_send(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { if (message.encryption == Encryption.OMEMO) { if (message.type_ == Message.Type.GROUPCHAT_PM) { message.marked = Message.Marked.WONTSEND; return; } XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream == null) { message.marked = Entities.Message.Marked.UNSENT; return; } StreamModule? module_ = ((!)stream).get_module(StreamModule.IDENTITY); if (module_ == null) { message.marked = Entities.Message.Marked.UNSENT; return; } StreamModule module = (!)module_; //Get a list of everyone for whom the message should be encrypted Gee.List recipients; if (message_stanza.type_ == MessageStanza.TYPE_GROUPCHAT) { recipients = get_occupants((!)message.to.bare_jid, conversation.account); if (recipients.size == 0) { message.marked = Entities.Message.Marked.WONTSEND; return; } } else { recipients = new ArrayList(Jid.equals_bare_func); recipients.add(message_stanza.to); } //Attempt to encrypt the message Xep.Omemo.EncryptState enc_state = encryptors[conversation.account].encrypt(message_stanza, conversation.account.bare_jid, recipients, stream); MessageState state; lock (message_states) { if (message_states.has_key(message)) { state = message_states.get(message); state.update_from_encrypt_status(message, enc_state); if (state.will_send_now) { debug("sending message delayed: %s", state.to_string()); } } else { state = new MessageState(message, enc_state); message_states[message] = state; } if (state.will_send_now) { message_states.unset(message); } } //Encryption failed - need to fetch more information if (!state.will_send_now) { if (message.marked == Entities.Message.Marked.WONTSEND) { debug("retracting message %s", state.to_string()); message_states.unset(message); } else { debug("delaying message %s", state.to_string()); if (state.waiting_own_sessions > 0) { module.fetch_bundles((!)stream, conversation.account.bare_jid, trust_manager.get_trusted_devices(conversation.account, conversation.account.bare_jid)); } if (state.waiting_other_sessions > 0 && message.counterpart != null) { foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) { module.fetch_bundles((!)stream, jid, trust_manager.get_trusted_devices(conversation.account, jid)); } } if (state.waiting_other_devicelists > 0 && message.counterpart != null) { foreach(Jid jid in get_occupants(((!)message.counterpart).bare_jid, conversation.account)) { module.request_user_devicelist.begin((!)stream, jid); } } } } } } private void on_mutual_subscription(Account account, Jid jid) { XmppStream? stream = stream_interactor.get_stream(account); if(stream == null) return; stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist.begin((!)stream, jid); } private void on_stream_negotiated(Account account, XmppStream stream) { StreamModule module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY); if (module != null) { module.request_user_devicelist.begin(stream, account.bare_jid); } } private void on_account_added(Account account) { StreamModule module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY); if (module != null) { module.device_list_loaded.connect((jid, devices) => on_device_list_loaded(account, jid, devices)); module.bundle_fetched.connect((jid, device_id, bundle) => on_bundle_fetched(account, jid, device_id, bundle)); module.bundle_fetch_failed.connect((jid) => continue_message_sending(account, jid)); } initialize_store.begin(account); } private void on_device_list_loaded(Account account, Jid jid, ArrayList device_list) { debug("received device list for %s from %s", account.bare_jid.to_string(), jid.to_string()); XmppStream? stream = stream_interactor.get_stream(account); if (stream == null) { return; } StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY); if (module == null) { return; } int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; //Update meta database db.identity_meta.insert_device_list(identity_id, jid.bare_jid.to_string(), device_list); //Fetch the bundle for each new device int inc = 0; foreach (Row row in db.identity_meta.get_unknown_devices(identity_id, jid.bare_jid.to_string())) { try { module.fetch_bundle(stream, new Jid(row[db.identity_meta.address_name]), row[db.identity_meta.device_id], false); inc++; } catch (InvalidJidError e) { warning("Ignoring device with invalid Jid: %s", e.message); } } if (inc > 0) { debug("new bundles %i/%i for %s", inc, device_list.size, jid.to_string()); } //Create an entry for the jid in the account table if one does not exist already if (db.trust.select().with(db.trust.identity_id, "=", identity_id).with(db.trust.address_name, "=", jid.bare_jid.to_string()).count() == 0) { db.trust.insert().value(db.trust.identity_id, identity_id).value(db.trust.address_name, jid.bare_jid.to_string()).value(db.trust.blind_trust, true).perform(); } //Get all messages that needed the devicelist and determine if we can now send them HashSet send_now = new HashSet(); lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); MessageState state = message_states[msg]; if (account.bare_jid.equals(jid)) { state.waiting_own_devicelist = false; } else if (msg.counterpart != null && occupants.contains(jid)) { state.waiting_other_devicelists--; } if (state.should_retry_now()) { send_now.add(msg); state.active_send_attempt = true; } } } foreach (Entities.Message msg in send_now) { if (msg.counterpart == null) continue; Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation(((!)msg.counterpart), account); if (conv == null) continue; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true); } } private void on_bundle_fetched(Account account, Jid jid, int32 device_id, Bundle bundle) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; bool blind_trust = db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true); //If we don't blindly trust new devices and we haven't seen this key before then don't trust it bool untrust = !(blind_trust || db.identity_meta.with_address(identity_id, jid.bare_jid.to_string()) .with(db.identity_meta.device_id, "=", device_id) .with(db.identity_meta.identity_key_public_base64, "=", Base64.encode(bundle.identity_key.serialize())) .single().row().is_present()); //Get trust information from the database if the device id is known Row device = db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id); TrustLevel trusted = TrustLevel.UNKNOWN; if (device != null) { trusted = (TrustLevel) device[db.identity_meta.trust_level]; } if(untrust) { trusted = TrustLevel.UNKNOWN; } else if (blind_trust && trusted == TrustLevel.UNKNOWN) { trusted = TrustLevel.TRUSTED; } //Update the database with the appropriate trust information db.identity_meta.insert_device_bundle(identity_id, jid.bare_jid.to_string(), device_id, bundle, trusted); if (should_start_session(account, jid)) { XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { StreamModule? module = ((!)stream).get_module(StreamModule.IDENTITY); if (module != null) { module.start_session(stream, jid, device_id, bundle); } } } continue_message_sending(account, jid); } private bool should_start_session(Account account, Jid jid) { lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); if (account.bare_jid.equals(jid) || (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid)))) { return true; } } } return false; } private void continue_message_sending(Account account, Jid jid) { //Get all messages waiting and determine if they can now be sent HashSet send_now = new HashSet(); lock (message_states) { foreach (Entities.Message msg in message_states.keys) { if (!msg.account.equals(account)) continue; Gee.List occupants = get_occupants(msg.counterpart.bare_jid, account); MessageState state = message_states[msg]; if (account.bare_jid.equals(jid)) { state.waiting_own_sessions--; } else if (msg.counterpart != null && (msg.counterpart.equals_bare(jid) || occupants.contains(jid))) { state.waiting_other_sessions--; } if (state.should_retry_now()){ send_now.add(msg); state.active_send_attempt = true; } } } foreach (Entities.Message msg in send_now) { if (msg.counterpart == null) continue; Entities.Conversation? conv = stream_interactor.get_module(ConversationManager.IDENTITY).get_conversation((!)msg.counterpart, account); if (conv == null) continue; stream_interactor.get_module(MessageProcessor.IDENTITY).send_xmpp_message(msg, (!)conv, true); } } private async void initialize_store(Account account) { // If the account is not yet persisted, wait for that and then continue - without identity.account_id the entry isn't worth much. if (account.id == -1) { account.notify["id"].connect(() => initialize_store.callback()); yield; } StreamModule? module = stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY); if (module == null) return; Store store = module.store; Qlite.Row? row = db.identity.row_with(db.identity.account_id, account.id).inner; int identity_id = -1; bool publish_identity = false; if (row == null) { // OMEMO not yet initialized, starting with empty base publish_identity = true; try { store.identity_key_store.local_registration_id = Random.int_range(1, int32.MAX); ECKeyPair key_pair = Plugin.get_context().generate_key_pair(); store.identity_key_store.identity_key_private = new Bytes(key_pair.private.serialize()); store.identity_key_store.identity_key_public = new Bytes(key_pair.public.serialize()); identity_id = (int) db.identity.upsert() .value(db.identity.account_id, account.id, true) .value(db.identity.device_id, (int) store.local_registration_id) .value(db.identity.identity_key_private_base64, Base64.encode(store.identity_key_store.identity_key_private.get_data())) .value(db.identity.identity_key_public_base64, Base64.encode(store.identity_key_store.identity_key_public.get_data())) .perform(); } catch (Error e) { // Ignore error } } else { store.identity_key_store.local_registration_id = ((!)row)[db.identity.device_id]; store.identity_key_store.identity_key_private = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_private_base64])); store.identity_key_store.identity_key_public = new Bytes(Base64.decode(((!)row)[db.identity.identity_key_public_base64])); identity_id = ((!)row)[db.identity.id]; } if (identity_id >= 0) { store.signed_pre_key_store = new BackedSignedPreKeyStore(db, identity_id); store.pre_key_store = new BackedPreKeyStore(db, identity_id); store.session_store = new BackedSessionStore(db, identity_id); } else { warning("store for %s is not persisted!", account.bare_jid.to_string()); } // Generated new device ID, ensure this gets added to the devicelist XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { module.request_user_devicelist.begin((!)stream, account.bare_jid); } } public async bool ensure_get_keys_for_conversation(Conversation conversation) { if (stream_interactor.get_module(MucManager.IDENTITY).is_private_room(conversation.account, conversation.counterpart)) { foreach (Jid offline_member in stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account)) { bool ok = yield ensure_get_keys_for_jid(conversation.account, offline_member); if (!ok) { return false; } } return true; } return yield ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid); } public async bool ensure_get_keys_for_jid(Account account, Jid jid) { if (trust_manager.is_known_address(account, jid)) return true; XmppStream? stream = stream_interactor.get_stream(account); if (stream != null) { var device_list = yield stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).request_user_devicelist(stream, jid); return device_list.size > 0; } return true; // TODO wait for stream? } public static void start(StreamInteractor stream_interactor, Database db, TrustManager trust_manager, HashMap encryptors) { Manager m = new Manager(stream_interactor, db, trust_manager, encryptors); stream_interactor.add_module(m); } } } dino-0.5.0/plugins/omemo/src/logic/pre_key_store.vala0000664000000000000000000000256014776241610021360 0ustar rootrootusing Omemo; using Qlite; namespace Dino.Plugins.Omemo { private class BackedPreKeyStore : SimplePreKeyStore { private Database db; private int identity_id; public BackedPreKeyStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.pre_key.select().with(db.pre_key.identity_id, "=", identity_id)) { store_pre_key(row[db.pre_key.pre_key_id], Base64.decode(row[db.pre_key.record_base64])); } } catch (Error e) { warning("Error while initializing pre key store: %s", e.message); } pre_key_stored.connect(on_pre_key_stored); pre_key_deleted.connect(on_pre_key_deleted); } public void on_pre_key_stored(PreKeyStore.Key key) { db.pre_key.upsert() .value(db.pre_key.identity_id, identity_id, true) .value(db.pre_key.pre_key_id, (int) key.key_id, true) .value(db.pre_key.record_base64, Base64.encode(key.record)) .perform(); } public void on_pre_key_deleted(PreKeyStore.Key key) { db.pre_key.delete() .with(db.pre_key.identity_id, "=", identity_id) .with(db.pre_key.pre_key_id, "=", (int) key.key_id) .perform(); } } } dino-0.5.0/plugins/omemo/src/logic/session_store.vala0000664000000000000000000000320014776241610021375 0ustar rootrootusing Omemo; using Qlite; namespace Dino.Plugins.Omemo { private class BackedSessionStore : SimpleSessionStore { private Database db; private int identity_id; public BackedSessionStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.session.select().with(db.session.identity_id, "=", identity_id)) { Address addr = new Address(row[db.session.address_name], row[db.session.device_id]); store_session(addr, Base64.decode(row[db.session.record_base64])); addr.device_id = 0; } } catch (Error e) { print("Error while initializing session store: %s", e.message); } session_stored.connect(on_session_stored); session_removed.connect(on_session_deleted); } public void on_session_stored(SessionStore.Session session) { db.session.upsert() .value(db.session.identity_id, identity_id, true) .value(db.session.address_name, session.name, true) .value(db.session.device_id, session.device_id, true) .value(db.session.record_base64, Base64.encode(session.record)) .perform(); } public void on_session_deleted(SessionStore.Session session) { db.session.delete() .with(db.session.identity_id, "=", identity_id) .with(db.session.address_name, "=", session.name) .with(db.session.device_id, "=", session.device_id) .perform(); } } } dino-0.5.0/plugins/omemo/src/logic/signed_pre_key_store.vala0000664000000000000000000000304614776241610022711 0ustar rootrootusing Qlite; using Omemo; namespace Dino.Plugins.Omemo { private class BackedSignedPreKeyStore : SimpleSignedPreKeyStore { private Database db; private int identity_id; public BackedSignedPreKeyStore(Database db, int identity_id) { this.db = db; this.identity_id = identity_id; init(); } private void init() { try { foreach (Row row in db.signed_pre_key.select().with(db.signed_pre_key.identity_id, "=", identity_id)) { store_signed_pre_key(row[db.signed_pre_key.signed_pre_key_id], Base64.decode(row[db.signed_pre_key.record_base64])); } } catch (Error e) { print("Error while initializing signed pre key store: %s", e.message); } signed_pre_key_stored.connect(on_signed_pre_key_stored); signed_pre_key_deleted.connect(on_signed_pre_key_deleted); } public void on_signed_pre_key_stored(SignedPreKeyStore.Key key) { db.signed_pre_key.upsert() .value(db.signed_pre_key.identity_id, identity_id, true) .value(db.signed_pre_key.signed_pre_key_id, (int) key.key_id, true) .value(db.signed_pre_key.record_base64, Base64.encode(key.record)) .perform(); } public void on_signed_pre_key_deleted(SignedPreKeyStore.Key key) { db.signed_pre_key.delete() .with(db.signed_pre_key.identity_id, "=", identity_id) .with(db.signed_pre_key.signed_pre_key_id, "=", (int) key.key_id) .perform(); } } } dino-0.5.0/plugins/omemo/src/logic/trust_manager.vala0000664000000000000000000001472414776241610021366 0ustar rootrootusing Dino.Entities; using Gee; using Xmpp; using Omemo; using Qlite; namespace Dino.Plugins.Omemo { public class TrustManager { public signal void bad_message_state_updated(Account account, Jid jid, int device_id); private StreamInteractor stream_interactor; private Database db; private TagMessageListener tag_message_listener; public HashMap message_device_id_map = new HashMap(Message.hash_func, Message.equals_func); public TrustManager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; tag_message_listener = new TagMessageListener(stream_interactor, this, db, message_device_id_map); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(tag_message_listener); } public void set_blind_trust(Account account, Jid jid, bool blind_trust) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return; db.trust.update() .with(db.trust.identity_id, "=", identity_id) .with(db.trust.address_name, "=", jid.bare_jid.to_string()) .set(db.trust.blind_trust, blind_trust).perform(); } public void set_device_trust(Account account, Jid jid, int device_id, TrustLevel trust_level) { int identity_id = db.identity.get_id(account.id); db.identity_meta.update() .with(db.identity_meta.identity_id, "=", identity_id) .with(db.identity_meta.address_name, "=", jid.bare_jid.to_string()) .with(db.identity_meta.device_id, "=", device_id) .set(db.identity_meta.trust_level, trust_level).perform(); // Hide messages from untrusted or unknown devices string selection = null; string[] selection_args = {}; var app_db = Application.get_default().db; foreach (Row row in db.content_item_meta.with_device(identity_id, jid.bare_jid.to_string(), device_id).with(db.content_item_meta.trusted_when_received, "=", false)) { if (selection == null) { selection = @"$(app_db.content_item.id) = ?"; } else { selection += @" OR $(app_db.content_item.id) = ?"; } selection_args += row[db.content_item_meta.content_item_id].to_string(); } if (selection != null) { app_db.content_item.update() .set(app_db.content_item.hide, trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) .where(selection, selection_args) .perform(); } if (trust_level == TrustLevel.TRUSTED) { db.identity_meta.update_last_message_untrusted(identity_id, device_id, null); bad_message_state_updated(account, jid, device_id); } } public bool is_known_address(Account account, Jid jid) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return false; return db.identity_meta.with_address(identity_id, jid.to_string()).with(db.identity_meta.last_active, ">", 0).count() > 0; } public Gee.List get_trusted_devices(Account account, Jid jid) { Gee.List devices = new ArrayList(); int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return devices; foreach (Row device in db.identity_meta.get_trusted_devices(identity_id, jid.bare_jid.to_string())) { if(device[db.identity_meta.trust_level] != TrustLevel.UNKNOWN || device[db.identity_meta.identity_key_public_base64] == null) devices.add(device[db.identity_meta.device_id]); } return devices; } private class TagMessageListener : MessageListener { public string[] after_actions_const = new string[]{ "STORE" }; public override string action_group { get { return "DECRYPT_TAG"; } } public override string[] after_actions { get { return after_actions_const; } } private StreamInteractor stream_interactor; private TrustManager trust_manager; private Database db; private HashMap message_device_id_map; public TagMessageListener(StreamInteractor stream_interactor, TrustManager trust_manager, Database db, HashMap message_device_id_map) { this.stream_interactor = stream_interactor; this.trust_manager = trust_manager; this.db = db; this.message_device_id_map = message_device_id_map; } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { int device_id = 0; if (message_device_id_map.has_key(message)) { device_id = message_device_id_map[message]; message_device_id_map.unset(message); } // TODO: Handling of files ContentItem? content_item = stream_interactor.get_module(ContentItemStore.IDENTITY).get_item_by_foreign(conversation, 1, message.id); if (content_item != null && device_id != 0) { Jid jid = content_item.jid; if (conversation.type_ == Conversation.Type.GROUPCHAT) { jid = message.real_jid; } int identity_id = db.identity.get_id(conversation.account.id); TrustLevel trust_level = (TrustLevel) db.identity_meta.get_device(identity_id, jid.bare_jid.to_string(), device_id)[db.identity_meta.trust_level]; if (trust_level == TrustLevel.UNTRUSTED || trust_level == TrustLevel.UNKNOWN) { stream_interactor.get_module(ContentItemStore.IDENTITY).set_item_hide(content_item, true); db.identity_meta.update_last_message_untrusted(identity_id, device_id, message.time); trust_manager.bad_message_state_updated(conversation.account, jid, device_id); } db.content_item_meta.insert() .value(db.content_item_meta.content_item_id, content_item.id) .value(db.content_item_meta.identity_id, identity_id) .value(db.content_item_meta.address_name, jid.bare_jid.to_string()) .value(db.content_item_meta.device_id, device_id) .value(db.content_item_meta.trusted_when_received, trust_level != TrustLevel.UNTRUSTED) .perform(); } return false; } } } } dino-0.5.0/plugins/omemo/src/native/0000775000000000000000000000000014776241610016027 5ustar rootrootdino-0.5.0/plugins/omemo/src/native/context.vala0000664000000000000000000001051614776241610020363 0ustar rootrootnamespace Omemo { public class Context { internal NativeContext native_context; private RecMutex mutex = RecMutex(); static void locking_function_lock(void* user_data) { Context ctx = (Context) user_data; ctx.mutex.lock(); } static void locking_function_unlock(void* user_data) { Context ctx = (Context) user_data; ctx.mutex.unlock(); } static void stderr_log(LogLevel level, string message, size_t len, void* user_data) { printerr(@"$level: $message\n"); } public Context(bool log = false) throws Error { throw_by_code(NativeContext.create(out native_context, this), "Error initializing native context"); throw_by_code(native_context.set_locking_functions(locking_function_lock, locking_function_unlock), "Error initializing native locking functions"); if (log) native_context.set_log_function(stderr_log); setup_crypto_provider(native_context); } public Store create_store() { return new Store(this); } public void randomize(uint8[] data) throws Error { throw_by_code(native_random(data)); } public SignedPreKeyRecord generate_signed_pre_key(IdentityKeyPair identity_key_pair, int32 id, uint64 timestamp = 0) throws Error { if (timestamp == 0) timestamp = new DateTime.now_utc().to_unix(); SignedPreKeyRecord res; throw_by_code(Protocol.KeyHelper.generate_signed_pre_key(out res, identity_key_pair, id, timestamp, native_context)); return res; } public Gee.Set generate_pre_keys(uint start, uint count) throws Error { Gee.Set res = new Gee.HashSet(); for(uint i = start; i < start+count; i++) { ECKeyPair pair = generate_key_pair(); PreKeyRecord record; throw_by_code(PreKeyRecord.create(out record, i, pair)); res.add(record); } return res; } public ECPublicKey decode_public_key(uint8[] bytes) throws Error { ECPublicKey public_key; throw_by_code(curve_decode_point(out public_key, bytes, native_context), "Error decoding public key"); return public_key; } public ECPrivateKey decode_private_key(uint8[] bytes) throws Error { ECPrivateKey private_key; throw_by_code(curve_decode_private_point(out private_key, bytes, native_context), "Error decoding private key"); return private_key; } public ECKeyPair generate_key_pair() throws Error { ECKeyPair key_pair; throw_by_code(curve_generate_key_pair(native_context, out key_pair), "Error generating key pair"); return key_pair; } public uint8[] calculate_signature(ECPrivateKey signing_key, uint8[] message) throws Error { Buffer signature; throw_by_code(Curve.calculate_signature(native_context, out signature, signing_key, message), "Error calculating signature"); return signature.data; } public OmemoMessage deserialize_signal_message(uint8[] data) throws Error { OmemoMessage res; throw_by_code(message_deserialize(out res, data, native_context)); return res; } public OmemoMessage deserialize_omemo_message(uint8[] data) throws Error { OmemoMessage res; throw_by_code(message_deserialize_omemo(out res, data, native_context)); return res; } public OmemoMessage copy_message(CiphertextMessage original) throws Error { OmemoMessage res; throw_by_code(message_copy(out res, (OmemoMessage) original, native_context)); return res; } public PreKeyOmemoMessage deserialize_signal_pre_key_message(uint8[] data) throws Error { PreKeyOmemoMessage res; throw_by_code(pre_key_message_deserialize(out res, data, native_context)); return res; } public PreKeyOmemoMessage deserialize_omemo_pre_key_message(uint8[] data, uint32 remote_registration_id) throws Error { PreKeyOmemoMessage res; throw_by_code(pre_key_message_deserialize_omemo(out res, data, remote_registration_id, native_context)); return res; } public PreKeyOmemoMessage copy_pre_key_message(CiphertextMessage original) throws Error { PreKeyOmemoMessage res; throw_by_code(pre_key_message_copy(out res, (PreKeyOmemoMessage) original, native_context)); return res; } } } dino-0.5.0/plugins/omemo/src/native/helper.c0000664000000000000000000002563214776241610017462 0ustar rootroot#include "helper.h" #include signal_type_base* signal_type_ref_vapi(void* instance) { g_return_val_if_fail(instance != NULL, NULL); signal_type_ref(instance); return instance; } signal_type_base* signal_type_unref_vapi(void* instance) { g_return_val_if_fail(instance != NULL, NULL); signal_type_unref(instance); return NULL; } signal_protocol_address* signal_protocol_address_new(const gchar* name, int32_t device_id) { g_return_val_if_fail(name != NULL, NULL); signal_protocol_address* address = malloc(sizeof(signal_protocol_address)); address->device_id = -1; address->name = NULL; signal_protocol_address_set_name(address, name); signal_protocol_address_set_device_id(address, device_id); return address; } void signal_protocol_address_free(signal_protocol_address* ptr) { g_return_if_fail(ptr != NULL); if (ptr->name) { g_free((void*)ptr->name); } return free(ptr); } void signal_protocol_address_set_name(signal_protocol_address* self, const gchar* name) { g_return_if_fail(self != NULL); g_return_if_fail(name != NULL); gchar* n = g_malloc(strlen(name)+1); memcpy(n, name, strlen(name)); n[strlen(name)] = 0; if (self->name) { g_free((void*)self->name); } self->name = n; self->name_len = strlen(n); } gchar* signal_protocol_address_get_name(signal_protocol_address* self) { g_return_val_if_fail(self != NULL, NULL); g_return_val_if_fail(self->name != NULL, 0); gchar* res = g_malloc(sizeof(char) * (self->name_len + 1)); memcpy(res, self->name, self->name_len); res[self->name_len] = 0; return res; } int32_t signal_protocol_address_get_device_id(signal_protocol_address* self) { g_return_val_if_fail(self != NULL, -1); return self->device_id; } void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_t device_id) { g_return_if_fail(self != NULL); self->device_id = device_id; } int signal_vala_randomize(uint8_t *data, size_t len) { gcry_randomize(data, len, GCRY_STRONG_RANDOM); return SG_SUCCESS; } int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data) { gcry_randomize(data, len, GCRY_STRONG_RANDOM); return SG_SUCCESS; } int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data) { gcry_mac_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); if (!ctx) return SG_ERR_NOMEM; if (gcry_mac_open(ctx, GCRY_MAC_HMAC_SHA256, 0, 0)) { free(ctx); return SG_ERR_UNKNOWN; } if (gcry_mac_setkey(*ctx, key, key_len)) { free(ctx); return SG_ERR_UNKNOWN; } *hmac_context = ctx; return SG_SUCCESS; } int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data) { gcry_mac_hd_t* ctx = hmac_context; if (gcry_mac_write(*ctx, data, data_len)) return SG_ERR_UNKNOWN; return SG_SUCCESS; } int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data) { size_t len = gcry_mac_get_algo_maclen(GCRY_MAC_HMAC_SHA256); uint8_t md[len]; gcry_mac_hd_t* ctx = hmac_context; if (gcry_mac_read(*ctx, md, &len)) return SG_ERR_UNKNOWN; signal_buffer *output_buffer = signal_buffer_create(md, len); if (!output_buffer) return SG_ERR_NOMEM; *output = output_buffer; return SG_SUCCESS; } void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data) { gcry_mac_hd_t* ctx = hmac_context; if (ctx) { gcry_mac_close(*ctx); free(ctx); } } int signal_vala_sha512_digest_init(void **digest_context, void *user_data) { gcry_md_hd_t* ctx = malloc(sizeof(gcry_mac_hd_t)); if (!ctx) return SG_ERR_NOMEM; if (gcry_md_open(ctx, GCRY_MD_SHA512, 0)) { free(ctx); return SG_ERR_UNKNOWN; } *digest_context = ctx; return SG_SUCCESS; } int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data) { gcry_md_hd_t* ctx = digest_context; gcry_md_write(*ctx, data, data_len); return SG_SUCCESS; } int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data) { size_t len = gcry_md_get_algo_dlen(GCRY_MD_SHA512); gcry_md_hd_t* ctx = digest_context; uint8_t* md = gcry_md_read(*ctx, GCRY_MD_SHA512); if (!md) return SG_ERR_UNKNOWN; gcry_md_reset(*ctx); signal_buffer *output_buffer = signal_buffer_create(md, len); free(md); if (!output_buffer) return SG_ERR_NOMEM; *output = output_buffer; return SG_SUCCESS; } void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data) { gcry_md_hd_t* ctx = digest_context; if (ctx) { gcry_md_close(*ctx); free(ctx); } } const int aes_cipher(int cipher, size_t key_len, int* algo, int* mode) { switch (key_len) { case 16: *algo = GCRY_CIPHER_AES128; break; case 24: *algo = GCRY_CIPHER_AES192; break; case 32: *algo = GCRY_CIPHER_AES256; break; default: return SG_ERR_UNKNOWN; } switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: *mode = GCRY_CIPHER_MODE_CBC; break; case SG_CIPHER_AES_CTR_NOPADDING: *mode = GCRY_CIPHER_MODE_CTR; break; case SG_CIPHER_AES_GCM_NOPADDING: *mode = GCRY_CIPHER_MODE_GCM; break; default: return SG_ERR_UNKNOWN; } return SG_SUCCESS; } int signal_vala_encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data) { int algo, mode, error_code = SG_ERR_UNKNOWN; if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; gcry_cipher_hd_t ctx = {0}; if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; signal_buffer* padded = 0; signal_buffer* out_buf = 0; goto no_error; error: gcry_cipher_close(ctx); if (padded != 0) { signal_buffer_bzero_free(padded); } if (out_buf != 0) { signal_buffer_free(out_buf); } return error_code; no_error: if (gcry_cipher_setkey(ctx, key, key_len)) goto error; uint8_t tag_len = 0, pad_len = 0; switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; pad_len = 16 - (plaintext_len % 16); if (pad_len == 0) pad_len = 16; break; case SG_CIPHER_AES_CTR_NOPADDING: if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; break; case SG_CIPHER_AES_GCM_NOPADDING: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; tag_len = 16; break; default: return SG_ERR_UNKNOWN; } size_t padded_len = plaintext_len + pad_len; padded = signal_buffer_alloc(padded_len); if (padded == 0) { error_code = SG_ERR_NOMEM; goto error; } memset(signal_buffer_data(padded) + plaintext_len, pad_len, pad_len); memcpy(signal_buffer_data(padded), plaintext, plaintext_len); out_buf = signal_buffer_alloc(padded_len + tag_len); if (out_buf == 0) { error_code = SG_ERR_NOMEM; goto error; } if (gcry_cipher_encrypt(ctx, signal_buffer_data(out_buf), padded_len, signal_buffer_data(padded), padded_len)) goto error; if (tag_len > 0) { if (gcry_cipher_gettag(ctx, signal_buffer_data(out_buf) + padded_len, tag_len)) goto error; } *output = out_buf; out_buf = 0; signal_buffer_bzero_free(padded); padded = 0; gcry_cipher_close(ctx); return SG_SUCCESS; } int signal_vala_decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data) { int algo, mode, error_code = SG_ERR_UNKNOWN; *output = 0; if (aes_cipher(cipher, key_len, &algo, &mode)) return SG_ERR_INVAL; if (ciphertext_len == 0) return SG_ERR_INVAL; gcry_cipher_hd_t ctx = {0}; if (gcry_cipher_open(&ctx, algo, mode, 0)) return SG_ERR_NOMEM; signal_buffer* out_buf = 0; goto no_error; error: gcry_cipher_close(ctx); if (out_buf != 0) { signal_buffer_bzero_free(out_buf); } return error_code; no_error: if (gcry_cipher_setkey(ctx, key, key_len)) goto error; uint8_t tag_len = 0, pkcs_pad = FALSE; switch (cipher) { case SG_CIPHER_AES_CBC_PKCS5: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; pkcs_pad = TRUE; break; case SG_CIPHER_AES_CTR_NOPADDING: if (gcry_cipher_setctr(ctx, iv, iv_len)) goto error; break; case SG_CIPHER_AES_GCM_NOPADDING: if (gcry_cipher_setiv(ctx, iv, iv_len)) goto error; if (ciphertext_len < 16) goto error; tag_len = 16; break; default: goto error; } size_t padded_len = ciphertext_len - tag_len; out_buf = signal_buffer_alloc(padded_len); if (out_buf == 0) { error_code = SG_ERR_NOMEM; goto error; } if (gcry_cipher_decrypt(ctx, signal_buffer_data(out_buf), signal_buffer_len(out_buf), ciphertext, padded_len)) goto error; if (tag_len > 0) { if (gcry_cipher_checktag(ctx, ciphertext + padded_len, tag_len)) goto error; } if (pkcs_pad) { uint8_t pad_len = signal_buffer_data(out_buf)[padded_len - 1]; if (pad_len > 16 || pad_len > padded_len) goto error; *output = signal_buffer_create(signal_buffer_data(out_buf), padded_len - pad_len); signal_buffer_bzero_free(out_buf); out_buf = 0; } else { *output = out_buf; out_buf = 0; } gcry_cipher_close(ctx); return SG_SUCCESS; } void setup_signal_vala_crypto_provider(signal_context *context) { gcry_check_version(NULL); signal_crypto_provider provider = { .random_func = signal_vala_random_generator, .hmac_sha256_init_func = signal_vala_hmac_sha256_init, .hmac_sha256_update_func = signal_vala_hmac_sha256_update, .hmac_sha256_final_func = signal_vala_hmac_sha256_final, .hmac_sha256_cleanup_func = signal_vala_hmac_sha256_cleanup, .sha512_digest_init_func = signal_vala_sha512_digest_init, .sha512_digest_update_func = signal_vala_sha512_digest_update, .sha512_digest_final_func = signal_vala_sha512_digest_final, .sha512_digest_cleanup_func = signal_vala_sha512_digest_cleanup, .encrypt_func = signal_vala_encrypt, .decrypt_func = signal_vala_decrypt, .user_data = 0 }; signal_context_set_crypto_provider(context, &provider); } dino-0.5.0/plugins/omemo/src/native/helper.h0000664000000000000000000000426514776241610017466 0ustar rootroot#ifndef SIGNAL_PROTOCOL_VALA_HELPER #define SIGNAL_PROTOCOL_VALA_HELPER 1 #include #include #include #define SG_CIPHER_AES_GCM_NOPADDING 1000 signal_type_base* signal_type_ref_vapi(void* what); signal_type_base* signal_type_unref_vapi(void* what); signal_protocol_address* signal_protocol_address_new(const gchar* name, int32_t device_id); void signal_protocol_address_free(signal_protocol_address* ptr); void signal_protocol_address_set_name(signal_protocol_address* self, const gchar* name); gchar* signal_protocol_address_get_name(signal_protocol_address* self); void signal_protocol_address_set_device_id(signal_protocol_address* self, int32_t device_id); int32_t signal_protocol_address_get_device_id(signal_protocol_address* self); int signal_vala_randomize(uint8_t *data, size_t len); int signal_vala_random_generator(uint8_t *data, size_t len, void *user_data); int signal_vala_hmac_sha256_init(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data); int signal_vala_hmac_sha256_update(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data); int signal_vala_hmac_sha256_final(void *hmac_context, signal_buffer **output, void *user_data); void signal_vala_hmac_sha256_cleanup(void *hmac_context, void *user_data); int signal_vala_sha512_digest_init(void **digest_context, void *user_data); int signal_vala_sha512_digest_update(void *digest_context, const uint8_t *data, size_t data_len, void *user_data); int signal_vala_sha512_digest_final(void *digest_context, signal_buffer **output, void *user_data); void signal_vala_sha512_digest_cleanup(void *digest_context, void *user_data); int signal_vala_encrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *plaintext, size_t plaintext_len, void *user_data); int signal_vala_decrypt(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len, const uint8_t *ciphertext, size_t ciphertext_len, void *user_data); void setup_signal_vala_crypto_provider(signal_context *context); #endif dino-0.5.0/plugins/omemo/src/native/simple_iks.vala0000664000000000000000000000355114776241610021037 0ustar rootrootusing Gee; namespace Omemo { public class SimpleIdentityKeyStore : IdentityKeyStore { public override Bytes identity_key_private { get; set; } public override Bytes identity_key_public { get; set; } public override uint32 local_registration_id { get; set; } private Map> trusted_identities = new HashMap>(); public override void save_identity(Address address, uint8[] key) throws Error { string name = address.name; if (trusted_identities.has_key(name)) { if (trusted_identities[name].has_key(address.device_id)) { trusted_identities[name][address.device_id].key = key; trusted_identity_updated(trusted_identities[name][address.device_id]); } else { trusted_identities[name][address.device_id] = new TrustedIdentity.by_address(address, key); trusted_identity_added(trusted_identities[name][address.device_id]); } } else { trusted_identities[name] = new HashMap(); trusted_identities[name][address.device_id] = new TrustedIdentity.by_address(address, key); trusted_identity_added(trusted_identities[name][address.device_id]); } } public override bool is_trusted_identity(Address address, uint8[] key) throws Error { if (!trusted_identities.has_key(address.name)) return true; if (!trusted_identities[address.name].has_key(address.device_id)) return true; uint8[] other_key = trusted_identities[address.name][address.device_id].key; if (other_key.length != key.length) return false; for (int i = 0; i < key.length; i++) { if (other_key[i] != key[i]) return false; } return true; } } } dino-0.5.0/plugins/omemo/src/native/simple_pks.vala0000664000000000000000000000166414776241610021051 0ustar rootrootusing Gee; namespace Omemo { public class SimplePreKeyStore : PreKeyStore { private Map pre_key_map = new HashMap(); public override uint8[]? load_pre_key(uint32 pre_key_id) throws Error { if (contains_pre_key(pre_key_id)) { return pre_key_map[pre_key_id].record; } return null; } public override void store_pre_key(uint32 pre_key_id, uint8[] record) throws Error { PreKeyStore.Key key = new Key(pre_key_id, record); pre_key_map[pre_key_id] = key; pre_key_stored(key); } public override bool contains_pre_key(uint32 pre_key_id) throws Error { return pre_key_map.has_key(pre_key_id); } public override void delete_pre_key(uint32 pre_key_id) throws Error { PreKeyStore.Key key; if (pre_key_map.unset(pre_key_id, out key)) { pre_key_deleted(key); } } } }dino-0.5.0/plugins/omemo/src/native/simple_spks.vala0000664000000000000000000000201114776241610021217 0ustar rootrootusing Gee; namespace Omemo { public class SimpleSignedPreKeyStore : SignedPreKeyStore { private Map pre_key_map = new HashMap(); public override uint8[]? load_signed_pre_key(uint32 pre_key_id) throws Error { if (contains_signed_pre_key(pre_key_id)) { return pre_key_map[pre_key_id].record; } return null; } public override void store_signed_pre_key(uint32 pre_key_id, uint8[] record) throws Error { SignedPreKeyStore.Key key = new Key(pre_key_id, record); pre_key_map[pre_key_id] = key; signed_pre_key_stored(key); } public override bool contains_signed_pre_key(uint32 pre_key_id) throws Error { return pre_key_map.has_key(pre_key_id); } public override void delete_signed_pre_key(uint32 pre_key_id) throws Error { SignedPreKeyStore.Key key; if (pre_key_map.unset(pre_key_id, out key)) { signed_pre_key_deleted(key); } } } }dino-0.5.0/plugins/omemo/src/native/simple_ss.vala0000664000000000000000000000527714776241610020705 0ustar rootrootusing Gee; namespace Omemo { public class SimpleSessionStore : SessionStore { private Map> session_map = new HashMap>(); public override uint8[]? load_session(Address address) throws Error { if (session_map.has_key(address.name)) { foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) return session.record; } } return null; } public override IntList get_sub_device_sessions(string name) throws Error { IntList res = new IntList(); if (session_map.has_key(name)) { foreach (SessionStore.Session session in session_map[name]) { res.add(session.device_id); } } return res; } public override void store_session(Address address, uint8[] record) throws Error { if (contains_session(address)) { delete_session(address); } if (!session_map.has_key(address.name)) { session_map[address.name] = new ArrayList(); } SessionStore.Session session = new Session() { name = address.name, device_id = address.device_id, record = record }; session_map[address.name].add(session); session_stored(session); } public override bool contains_session(Address address) throws Error { if (!session_map.has_key(address.name)) return false; foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) return true; } return false; } public override void delete_session(Address address) throws Error { if (!session_map.has_key(address.name)) throw_by_code(ErrorCode.UNKNOWN, "No session found"); foreach (SessionStore.Session session in session_map[address.name]) { if (session.device_id == address.device_id) { session_map[address.name].remove(session); if (session_map[address.name].size == 0) { session_map.unset(address.name); } session_removed(session); return; } } } public override void delete_all_sessions(string name) throws Error { if (session_map.has_key(name)) { foreach (SessionStore.Session session in session_map[name]) { session_map[name].remove(session); if (session_map[name].size == 0) { session_map.unset(name); } session_removed(session); } } } } }dino-0.5.0/plugins/omemo/src/native/store.vala0000664000000000000000000003516014776241610020035 0ustar rootrootnamespace Omemo { public abstract class IdentityKeyStore : Object { public abstract Bytes identity_key_private { get; set; } public abstract Bytes identity_key_public { get; set; } public abstract uint32 local_registration_id { get; set; } public signal void trusted_identity_added(TrustedIdentity id); public signal void trusted_identity_updated(TrustedIdentity id); public abstract void save_identity(Address address, uint8[] key) throws Error ; public abstract bool is_trusted_identity(Address address, uint8[] key) throws Error ; public class TrustedIdentity { public uint8[] key { get; set; } public string name { get; private set; } public int device_id { get; private set; } public TrustedIdentity(string name, int device_id, uint8[] key) { this.key = key; this.name = name; this.device_id = device_id; } public TrustedIdentity.by_address(Address address, uint8[] key) { this(address.name, address.device_id, key); } } } public abstract class SessionStore : Object { public signal void session_stored(Session session); public signal void session_removed(Session session); public abstract uint8[]? load_session(Address address) throws Error ; public abstract IntList get_sub_device_sessions(string name) throws Error ; public abstract void store_session(Address address, uint8[] record) throws Error ; public abstract bool contains_session(Address address) throws Error ; public abstract void delete_session(Address address) throws Error ; public abstract void delete_all_sessions(string name) throws Error ; public class Session { public string name; public int device_id; public uint8[] record; } } public abstract class PreKeyStore : Object { public signal void pre_key_stored(Key key); public signal void pre_key_deleted(Key key); public abstract uint8[]? load_pre_key(uint32 pre_key_id) throws Error ; public abstract void store_pre_key(uint32 pre_key_id, uint8[] record) throws Error ; public abstract bool contains_pre_key(uint32 pre_key_id) throws Error ; public abstract void delete_pre_key(uint32 pre_key_id) throws Error ; public class Key { public uint32 key_id { get; private set; } public uint8[] record { get; private set; } public Key(uint32 key_id, uint8[] record) { this.key_id = key_id; this.record = record; } } } public abstract class SignedPreKeyStore : Object { public signal void signed_pre_key_stored(Key key); public signal void signed_pre_key_deleted(Key key); public abstract uint8[]? load_signed_pre_key(uint32 pre_key_id) throws Error ; public abstract void store_signed_pre_key(uint32 pre_key_id, uint8[] record) throws Error ; public abstract bool contains_signed_pre_key(uint32 pre_key_id) throws Error ; public abstract void delete_signed_pre_key(uint32 pre_key_id) throws Error ; public class Key { public uint32 key_id { get; private set; } public uint8[] record { get; private set; } public Key(uint32 key_id, uint8[] record) { this.key_id = key_id; this.record = record; } } } public class Store : Object { public Context context { get; private set; } public IdentityKeyStore identity_key_store { get; set; default = new SimpleIdentityKeyStore(); } public SessionStore session_store { get; set; default = new SimpleSessionStore(); } public PreKeyStore pre_key_store { get; set; default = new SimplePreKeyStore(); } public SignedPreKeyStore signed_pre_key_store { get; set; default = new SimpleSignedPreKeyStore(); } public uint32 local_registration_id { get { return identity_key_store.local_registration_id; } } internal NativeStoreContext native_context {get { return native_store_context_; }} private NativeStoreContext native_store_context_; static int iks_get_identity_key_pair(out Buffer public_data, out Buffer private_data, void* user_data) { Store store = (Store) user_data; public_data = new Buffer.from(store.identity_key_store.identity_key_public.get_data()); private_data = new Buffer.from(store.identity_key_store.identity_key_private.get_data()); return 0; } static int iks_get_local_registration_id(void* user_data, out uint32 registration_id) { Store store = (Store) user_data; registration_id = store.identity_key_store.local_registration_id; return 0; } static int iks_save_identity(Address address, uint8[] key, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.identity_key_store.save_identity(address, key); return 0; }); } static int iks_is_trusted_identity(Address address, uint8[] key, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.identity_key_store.is_trusted_identity(address, key) ? 1 : 0; }); } static void iks_destroy_func(void* user_data) { } static int ss_load_session_func(out Buffer? record, out Buffer? user_record, Address address, void* user_data) { Store store = (Store) user_data; user_record = null; // No support for user_record uint8[]? res = null; try { res = store.session_store.load_session(address); } catch (Error e) { record = null; return e.code; } if (res == null) { record = null; return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int ss_get_sub_device_sessions_func(out IntList? sessions, char[] name, void* user_data) { Store store = (Store) user_data; try { sessions = store.session_store.get_sub_device_sessions(carr_to_string(name)); } catch (Error e) { sessions = null; return e.code; } return 0; } static int ss_store_session_func(Address address, uint8[] record, uint8[] user_record, void* user_data) { // Ignoring user_record Store store = (Store) user_data; return catch_to_code(() => { store.session_store.store_session(address, record); return 0; }); } static int ss_contains_session_func(Address address, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.session_store.contains_session(address) ? 1 : 0; }); } static int ss_delete_session_func(Address address, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.session_store.delete_session(address); return 0; }); } static int ss_delete_all_sessions_func(char[] name, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.session_store.delete_all_sessions(carr_to_string(name)); return 0; }); } static void ss_destroy_func(void* user_data) { } static int pks_load_pre_key(out Buffer? record, uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; uint8[]? res = null; try { res = store.pre_key_store.load_pre_key(pre_key_id); } catch (Error e) { record = null; return e.code; } if (res == null) { record = new Buffer(0); return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int pks_store_pre_key(uint32 pre_key_id, uint8[] record, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.pre_key_store.store_pre_key(pre_key_id, record); return 0; }); } static int pks_contains_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.pre_key_store.contains_pre_key(pre_key_id) ? 1 : 0; }); } static int pks_remove_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.pre_key_store.delete_pre_key(pre_key_id); return 0; }); } static void pks_destroy_func(void* user_data) { } static int spks_load_signed_pre_key(out Buffer? record, uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; uint8[]? res = null; try { res = store.signed_pre_key_store.load_signed_pre_key(pre_key_id); } catch (Error e) { record = null; return e.code; } if (res == null) { record = new Buffer(0); return 0; } record = new Buffer.from((!)res); if (record == null) return ErrorCode.NOMEM; return 1; } static int spks_store_signed_pre_key(uint32 pre_key_id, uint8[] record, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.signed_pre_key_store.store_signed_pre_key(pre_key_id, record); return 0; }); } static int spks_contains_signed_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { return store.signed_pre_key_store.contains_signed_pre_key(pre_key_id) ? 1 : 0; }); } static int spks_remove_signed_pre_key(uint32 pre_key_id, void* user_data) { Store store = (Store) user_data; return catch_to_code(() => { store.signed_pre_key_store.delete_signed_pre_key(pre_key_id); return 0; }); } static void spks_destroy_func(void* user_data) { } internal Store(Context context) { this.context = context; NativeStoreContext.create(out native_store_context_, context.native_context); NativeIdentityKeyStore iks = NativeIdentityKeyStore() { get_identity_key_pair = iks_get_identity_key_pair, get_local_registration_id = iks_get_local_registration_id, save_identity = iks_save_identity, is_trusted_identity = iks_is_trusted_identity, destroy_func = iks_destroy_func, user_data = this }; native_context.set_identity_key_store(iks); NativeSessionStore ss = NativeSessionStore() { load_session_func = ss_load_session_func, get_sub_device_sessions_func = ss_get_sub_device_sessions_func, store_session_func = ss_store_session_func, contains_session_func = ss_contains_session_func, delete_session_func = ss_delete_session_func, delete_all_sessions_func = ss_delete_all_sessions_func, destroy_func = ss_destroy_func, user_data = this }; native_context.set_session_store(ss); NativePreKeyStore pks = NativePreKeyStore() { load_pre_key = pks_load_pre_key, store_pre_key = pks_store_pre_key, contains_pre_key = pks_contains_pre_key, remove_pre_key = pks_remove_pre_key, destroy_func = pks_destroy_func, user_data = this }; native_context.set_pre_key_store(pks); NativeSignedPreKeyStore spks = NativeSignedPreKeyStore() { load_signed_pre_key = spks_load_signed_pre_key, store_signed_pre_key = spks_store_signed_pre_key, contains_signed_pre_key = spks_contains_signed_pre_key, remove_signed_pre_key = spks_remove_signed_pre_key, destroy_func = spks_destroy_func, user_data = this }; native_context.set_signed_pre_key_store(spks); } public SessionBuilder create_session_builder(Address other) throws Error { SessionBuilder builder; throw_by_code(session_builder_create(out builder, native_context, other, context.native_context), "Error creating session builder"); return builder; } public SessionCipher create_session_cipher(Address other) throws Error { SessionCipher cipher; throw_by_code(session_cipher_create(out cipher, native_context, other, context.native_context)); return cipher; } public IdentityKeyPair identity_key_pair { owned get { IdentityKeyPair pair; Protocol.Identity.get_key_pair(native_context, out pair); return pair; } } public bool is_trusted_identity(Address address, ECPublicKey key) throws Error { return throw_by_code(Protocol.Identity.is_trusted_identity(native_context, address, key)) == 1; } public void save_identity(Address address, ECPublicKey key) throws Error { throw_by_code(Protocol.Identity.save_identity(native_context, address, key)); } public bool contains_session(Address other) throws Error { return throw_by_code(Protocol.Session.contains_session(native_context, other)) == 1; } public void delete_session(Address address) throws Error { throw_by_code(Protocol.Session.delete_session(native_context, address)); } public SessionRecord load_session(Address other) throws Error { SessionRecord record; throw_by_code(Protocol.Session.load_session(native_context, out record, other)); return record; } public bool contains_pre_key(uint32 pre_key_id) throws Error { return throw_by_code(Protocol.PreKey.contains_key(native_context, pre_key_id)) == 1; } public void store_pre_key(PreKeyRecord record) throws Error { throw_by_code(Protocol.PreKey.store_key(native_context, record)); } public PreKeyRecord load_pre_key(uint32 pre_key_id) throws Error { PreKeyRecord res; throw_by_code(Protocol.PreKey.load_key(native_context, out res, pre_key_id)); return res; } public bool contains_signed_pre_key(uint32 pre_key_id) throws Error { return throw_by_code(Protocol.SignedPreKey.contains_key(native_context, pre_key_id)) == 1; } public void store_signed_pre_key(SignedPreKeyRecord record) throws Error { throw_by_code(Protocol.SignedPreKey.store_key(native_context, record)); } public SignedPreKeyRecord load_signed_pre_key(uint32 pre_key_id) throws Error { SignedPreKeyRecord res; throw_by_code(Protocol.SignedPreKey.load_key(native_context, out res, pre_key_id)); return res; } } } dino-0.5.0/plugins/omemo/src/native/util.vala0000664000000000000000000000313414776241610017652 0ustar rootrootnamespace Omemo { public ECPublicKey generate_public_key(ECPrivateKey private_key) throws Error { ECPublicKey public_key; throw_by_code(ECPublicKey.generate(out public_key, private_key), "Error generating public key"); return public_key; } public uint8[] calculate_agreement(ECPublicKey public_key, ECPrivateKey private_key) throws Error { uint8[] res; int len = Curve.calculate_agreement(out res, public_key, private_key); throw_by_code(len, "Error calculating agreement"); res.length = len; return res; } public bool verify_signature(ECPublicKey signing_key, uint8[] message, uint8[] signature) throws Error { return throw_by_code(Curve.verify_signature(signing_key, message, signature)) == 1; } public PreKeyBundle create_pre_key_bundle(uint32 registration_id, int device_id, uint32 pre_key_id, ECPublicKey? pre_key_public, uint32 signed_pre_key_id, ECPublicKey? signed_pre_key_public, uint8[]? signed_pre_key_signature, ECPublicKey? identity_key) throws Error { PreKeyBundle res; throw_by_code(PreKeyBundle.create(out res, registration_id, device_id, pre_key_id, pre_key_public, signed_pre_key_id, signed_pre_key_public, signed_pre_key_signature, identity_key), "Error creating PreKeyBundle"); return res; } internal string carr_to_string(char[] carr) { char[] nu = new char[carr.length + 1]; Memory.copy(nu, carr, carr.length); return (string) nu; } internal delegate int CodeErroringFunc() throws Error; internal int catch_to_code(CodeErroringFunc func) { try { return func(); } catch (Error e) { return e.code; } } }dino-0.5.0/plugins/omemo/src/plugin.vala0000664000000000000000000001061514776241610016707 0ustar rootrootusing Gee; using Dino.Entities; using Omemo; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.Omemo { public class Plugin : RootInterface, Object { public const bool DEBUG = false; private static Context? _context; public static Context get_context() { assert(_context != null); return (!)_context; } public static bool ensure_context() { lock(_context) { try { if (_context == null) { _context = new Context(DEBUG); } return true; } catch (Error e) { warning("Error initializing libomemo-c Context %s", e.message); return false; } } } public Dino.Application app; public Database db; public EncryptionListEntry list_entry; public ContactDetailsProvider contact_details_provider; public DeviceNotificationPopulator device_notification_populator; public OwnNotifications own_notifications; public TrustManager trust_manager; public HashMap decryptors = new HashMap(Account.hash_func, Account.equals_func); public HashMap encryptors = new HashMap(Account.hash_func, Account.equals_func); public void registered(Dino.Application app) { ensure_context(); this.app = app; this.db = new Database(Path.build_filename(Application.get_storage_dir(), "omemo.db")); this.list_entry = new EncryptionListEntry(this); this.contact_details_provider = new ContactDetailsProvider(this); this.device_notification_populator = new DeviceNotificationPopulator(this, this.app.stream_interactor); this.trust_manager = new TrustManager(this.app.stream_interactor, this.db); this.app.plugin_registry.register_encryption_list_entry(list_entry); this.app.plugin_registry.register_encryption_preferences_entry(new OmemoPreferencesEntry(this)); this.app.plugin_registry.register_contact_details_entry(contact_details_provider); this.app.plugin_registry.register_notification_populator(device_notification_populator); this.app.plugin_registry.register_conversation_addition_populator(new BadMessagesPopulator(this.app.stream_interactor, this)); this.app.plugin_registry.register_call_entryption_entry(DtlsSrtpVerificationDraft.NS_URI, new CallEncryptionEntry(db)); this.app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { Store store = Plugin.get_context().create_store(); list.add(new StreamModule(store)); decryptors[account] = new OmemoDecryptor(account, app.stream_interactor, trust_manager, db, store); list.add(decryptors[account]); encryptors[account] = new OmemoEncryptor(account, trust_manager, store); list.add(encryptors[account]); list.add(new JetOmemo.Module()); list.add(new DtlsSrtpVerificationDraft.StreamModule()); this.own_notifications = new OwnNotifications(this, this.app.stream_interactor, account); }); app.stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(new DecryptMessageListener(decryptors)); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_decryptor(new OmemoFileDecryptor()); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_encryptor(new OmemoFileEncryptor()); JingleFileHelperRegistry.instance.add_encryption_helper(Encryption.OMEMO, new JetOmemo.EncryptionHelper(app.stream_interactor)); Manager.start(this.app.stream_interactor, db, trust_manager, encryptors); string locales_dir; if (app.search_path_generator != null) { locales_dir = ((!)app.search_path_generator).get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR); } else { locales_dir = LOCALE_INSTALL_DIR; } internationalize(GETTEXT_PACKAGE, locales_dir); } public void shutdown() { // Nothing to do } public bool has_new_devices(Account account, Xmpp.Jid jid) { int identity_id = db.identity.get_id(account.id); if (identity_id < 0) return false; return db.identity_meta.get_new_devices(identity_id, jid.bare_jid.to_string()).count() > 0; } } } dino-0.5.0/plugins/omemo/src/protocol/0000775000000000000000000000000014776241610016402 5ustar rootrootdino-0.5.0/plugins/omemo/src/protocol/bundle.vala0000664000000000000000000000515414776241610020525 0ustar rootrootusing Gee; using Omemo; using Xmpp; namespace Dino.Plugins.Omemo { public class Bundle { public StanzaNode? node; public Bundle(StanzaNode? node) { this.node = node; assert(Plugin.ensure_context()); } public int32 signed_pre_key_id { owned get { if (node == null) return -1; string? id = ((!)node).get_deep_attribute("signedPreKeyPublic", "signedPreKeyId"); if (id == null) return -1; return int.parse((!)id); }} public ECPublicKey? signed_pre_key { owned get { if (node == null) return null; string? key = ((!)node).get_deep_string_content("signedPreKeyPublic"); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} public uint8[]? signed_pre_key_signature { owned get { if (node == null) return null; string? sig = ((!)node).get_deep_string_content("signedPreKeySignature"); if (sig == null) return null; return Base64.decode((!)sig); }} public ECPublicKey? identity_key { owned get { if (node == null) return null; string? key = ((!)node).get_deep_string_content("identityKey"); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} public ArrayList pre_keys { owned get { ArrayList list = new ArrayList(); if (node == null || ((!)node).get_subnode("prekeys") == null) return list; ((!)node).get_deep_subnodes("prekeys", "preKeyPublic") .filter((node) => ((!)node).get_attribute("preKeyId") != null) .map(PreKey.create) .foreach((key) => list.add(key)); return list; }} public class PreKey { private StanzaNode node; public static PreKey create(owned StanzaNode node) { return new PreKey(node); } public PreKey(StanzaNode node) { this.node = node; } public int32 key_id { owned get { return int.parse(node.get_attribute("preKeyId") ?? "-1"); }} public ECPublicKey? key { owned get { string? key = node.get_string_content(); if (key == null) return null; try { return Plugin.get_context().decode_public_key(Base64.decode((!)key)); } catch (Error e) { return null; } }} } } } dino-0.5.0/plugins/omemo/src/protocol/message_flag.vala0000664000000000000000000000066014776241610021666 0ustar rootrootusing Xmpp; namespace Dino.Plugins.Omemo { public class MessageFlag : Xmpp.MessageFlag { public const string id = "omemo"; public bool decrypted = false; public static MessageFlag? get_flag(MessageStanza message) { return (MessageFlag) message.get_flag(NS_URI, id); } public override string get_ns() { return NS_URI; } public override string get_id() { return id; } } }dino-0.5.0/plugins/omemo/src/protocol/stream_module.vala0000664000000000000000000003657714776241610022131 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; using Omemo; namespace Dino.Plugins.Omemo { internal const string NS_URI = "eu.siacs.conversations.axolotl"; private const string NODE_DEVICELIST = NS_URI + ".devicelist"; private const string NODE_BUNDLES = NS_URI + ".bundles"; private const string NODE_VERIFICATION = NS_URI + ".verification"; private const int NUM_KEYS_TO_PUBLISH = 100; public class StreamModule : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "omemo_module"); private static TimeSpan IGNORE_TIME = TimeSpan.MINUTE; public Store store { public get; private set; } private ConcurrentSet active_bundle_requests = new ConcurrentSet(); private HashMap>> active_devicelist_requests = new HashMap>>(Jid.hash_func, Jid.equals_func); private Map device_ignore_time = new HashMap(); public signal void device_list_loaded(Jid jid, ArrayList devices); public signal void bundle_fetched(Jid jid, int device_id, Bundle bundle); public signal void bundle_fetch_failed(Jid jid, int device_id); public StreamModule(Store store) { this.store = store; } public override void attach(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).add_filtered_notification(stream, NODE_DEVICELIST, (stream, jid, id, node) => parse_device_list(stream, jid, id, node), null, null); } public override void detach(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).remove_filtered_notification(stream, NODE_DEVICELIST); } public async ArrayList request_user_devicelist(XmppStream stream, Jid jid) { var future = active_devicelist_requests[jid]; if (future == null) { var promise = new Promise?>(); future = promise.future; active_devicelist_requests[jid] = future; stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid, NODE_DEVICELIST, (stream, jid, id, node) => { ArrayList device_list = parse_device_list(stream, jid, id, node); promise.set_value(device_list); active_devicelist_requests.unset(jid); }); } try { ArrayList device_list = yield future.wait_async(); return device_list; } catch (FutureError error) { warning("Future error when waiting for device list: %s", error.message); return new ArrayList(); } } public ArrayList parse_device_list(XmppStream stream, Jid jid, string? id, StanzaNode? node_) { ArrayList device_list = new ArrayList(); StanzaNode node = node_ ?? new StanzaNode.build("list", NS_URI).add_self_xmlns(); Jid? my_jid = stream.get_flag(Bind.Flag.IDENTITY).my_jid; if (my_jid == null) return device_list; if (jid.equals_bare(my_jid) && store.local_registration_id != 0) { bool am_on_devicelist = false; foreach (StanzaNode device_node in node.get_subnodes("device")) { int device_id = device_node.get_attribute_int("id"); if (store.local_registration_id == device_id) { am_on_devicelist = true; } } if (!am_on_devicelist) { debug("Not on device list, adding id"); node.put_node(new StanzaNode.build("device", NS_URI).put_attribute("id", store.local_registration_id.to_string())); stream.get_module(Pubsub.Module.IDENTITY).publish.begin(stream, jid, NODE_DEVICELIST, id, node, null, true, () => { try_make_node_public.begin(stream, NODE_DEVICELIST); }); } publish_bundles_if_needed(stream, jid); } foreach (StanzaNode device_node in node.get_subnodes("device")) { device_list.add(device_node.get_attribute_int("id")); } device_list_loaded(jid, device_list); return device_list; } public void fetch_bundles(XmppStream stream, Jid jid, Gee.List devices) { Address address = new Address(jid.bare_jid.to_string(), 0); foreach(int32 device_id in devices) { if (!is_ignored_device(jid, device_id)) { address.device_id = device_id; try { if (!store.contains_session(address)) { fetch_bundle(stream, jid, device_id); } } catch (Error e) { // Ignore } } } address.device_id = 0; // TODO: Hack to have address obj live longer } public void fetch_bundle(XmppStream stream, Jid jid, int device_id, bool ignore_if_non_present = true) { if (active_bundle_requests.add(jid.bare_jid.to_string() + @":$device_id")) { debug("Asking for bundle for %s/%d", jid.bare_jid.to_string(), device_id); stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid.bare_jid, @"$NODE_BUNDLES:$device_id", (stream, jid, id, node) => { on_other_bundle_result(stream, jid, device_id, id, node, ignore_if_non_present); }); } } public void ignore_device(Jid jid, int32 device_id) { if (device_id <= 0) return; lock (device_ignore_time) { device_ignore_time[jid.bare_jid.to_string() + @":$device_id"] = new DateTime.now_utc(); } } public void unignore_device(Jid jid, int32 device_id) { if (device_id <= 0) return; lock (device_ignore_time) { device_ignore_time.unset(jid.bare_jid.to_string() + @":$device_id"); } } public bool is_ignored_device(Jid jid, int32 device_id) { if (device_id <= 0) return true; lock (device_ignore_time) { string id = jid.bare_jid.to_string() + @":$device_id"; if (device_ignore_time.has_key(id)) { return new DateTime.now_utc().difference(device_ignore_time[id]) < IGNORE_TIME; } } return false; } public void clear_device_list(XmppStream stream) { stream.get_module(Pubsub.Module.IDENTITY).delete_node(stream, null, NODE_DEVICELIST); } private void on_other_bundle_result(XmppStream stream, Jid jid, int device_id, string? id, StanzaNode? node, bool ignore_if_non_present) { if (node == null) { // Device not registered, shouldn't exist if (ignore_if_non_present) { debug("Ignoring device %s/%d: No bundle", jid.bare_jid.to_string(), device_id); stream.get_module(IDENTITY).ignore_device(jid, device_id); } bundle_fetch_failed(jid, device_id); } else { Bundle bundle = new Bundle(node); stream.get_module(IDENTITY).unignore_device(jid, device_id); debug("Received bundle for %s/%d: %s", jid.bare_jid.to_string(), device_id, Base64.encode(bundle.identity_key.serialize())); bundle_fetched(jid, device_id, bundle); } stream.get_module(IDENTITY).active_bundle_requests.remove(jid.bare_jid.to_string() + @":$device_id"); } public bool start_session(XmppStream stream, Jid jid, int32 device_id, Bundle bundle) { bool fail = false; int32 signed_pre_key_id = bundle.signed_pre_key_id; ECPublicKey? signed_pre_key = bundle.signed_pre_key; uint8[] signed_pre_key_signature = bundle.signed_pre_key_signature; ECPublicKey? identity_key = bundle.identity_key; ArrayList pre_keys = bundle.pre_keys; if (signed_pre_key_id < 0 || signed_pre_key == null || identity_key == null || pre_keys.size == 0) { fail = true; } else { int pre_key_idx = Random.int_range(0, pre_keys.size); int32 pre_key_id = pre_keys[pre_key_idx].key_id; ECPublicKey? pre_key = pre_keys[pre_key_idx].key; if (pre_key_id < 0 || pre_key == null) { fail = true; } else { Address address = new Address(jid.bare_jid.to_string(), device_id); try { if (store.contains_session(address)) { return false; } debug("Starting new session for encryption with %s/%d", jid.bare_jid.to_string(), device_id); SessionBuilder builder = store.create_session_builder(address); builder.process_pre_key_bundle(create_pre_key_bundle(device_id, device_id, pre_key_id, pre_key, signed_pre_key_id, signed_pre_key, signed_pre_key_signature, identity_key)); } catch (Error e) { debug("Can't create session with %s/%d: %s", jid.bare_jid.to_string(), device_id, e.message); fail = true; } address.device_id = 0; // TODO: Hack to have address obj live longer } } if (fail) { debug("Ignoring device %s/%d: Bad bundle: %s", jid.bare_jid.to_string(), device_id, bundle.node.to_string()); stream.get_module(IDENTITY).ignore_device(jid, device_id); } return true; } public void publish_bundles_if_needed(XmppStream stream, Jid jid) { if (active_bundle_requests.add(jid.bare_jid.to_string() + @":$(store.local_registration_id)")) { stream.get_module(Pubsub.Module.IDENTITY).request(stream, jid, @"$NODE_BUNDLES:$(store.local_registration_id)", on_self_bundle_result); } } private void on_self_bundle_result(XmppStream stream, Jid jid, string? id, StanzaNode? node) { if (!Plugin.ensure_context()) return; Map keys = new HashMap(); ECPublicKey? identity_key = null; int32 signed_pre_key_id = -1; ECPublicKey? signed_pre_key = null; SignedPreKeyRecord? signed_pre_key_record = null; bool changed = false; if (node == null) { identity_key = store.identity_key_pair.public; changed = true; } else { Bundle bundle = new Bundle(node); foreach (Bundle.PreKey prekey in bundle.pre_keys) { ECPublicKey? key = prekey.key; if (key != null) { keys[prekey.key_id] = (!)key; } } identity_key = bundle.identity_key; signed_pre_key_id = bundle.signed_pre_key_id; signed_pre_key = bundle.signed_pre_key; } try { // Validate IdentityKey if (identity_key == null || store.identity_key_pair.public.compare((!)identity_key) != 0) { changed = true; } IdentityKeyPair identity_key_pair = store.identity_key_pair; // Validate signedPreKeyRecord + ID if (signed_pre_key == null || signed_pre_key_id == -1 || !store.contains_signed_pre_key(signed_pre_key_id) || store.load_signed_pre_key(signed_pre_key_id).key_pair.public.compare((!)signed_pre_key) != 0) { signed_pre_key_id = Random.int_range(1, int32.MAX); // TODO: No random, use ordered number signed_pre_key_record = Plugin.get_context().generate_signed_pre_key(identity_key_pair, signed_pre_key_id); store.store_signed_pre_key((!)signed_pre_key_record); changed = true; } else { signed_pre_key_record = store.load_signed_pre_key(signed_pre_key_id); } // Validate PreKeys Set pre_key_records = new HashSet(); foreach (var entry in keys.entries) { if (store.contains_pre_key(entry.key)) { PreKeyRecord record = store.load_pre_key(entry.key); if (record.key_pair.public.compare(entry.value) == 0) { pre_key_records.add(record); } } } int new_keys = NUM_KEYS_TO_PUBLISH - pre_key_records.size; if (new_keys > 0) { int32 next_id = Random.int_range(1, int32.MAX); // TODO: No random, use ordered number Set new_records = Plugin.get_context().generate_pre_keys((uint)next_id, (uint)new_keys); pre_key_records.add_all(new_records); foreach (PreKeyRecord record in new_records) { store.store_pre_key(record); } changed = true; } if (changed) { publish_bundles.begin(stream, (!)signed_pre_key_record, identity_key_pair, pre_key_records, (int32) store.local_registration_id); } } catch (Error e) { warning(@"Unexpected error while publishing bundle: $(e.message)\n"); } stream.get_module(IDENTITY).active_bundle_requests.remove(jid.bare_jid.to_string() + @":$(store.local_registration_id)"); } public async void publish_bundles(XmppStream stream, SignedPreKeyRecord signed_pre_key_record, IdentityKeyPair identity_key_pair, Set pre_key_records, int32 device_id) throws Error { ECKeyPair tmp; StanzaNode bundle = new StanzaNode.build("bundle", NS_URI) .add_self_xmlns() .put_node(new StanzaNode.build("signedPreKeyPublic", NS_URI) .put_attribute("signedPreKeyId", signed_pre_key_record.id.to_string()) .put_node(new StanzaNode.text(Base64.encode((tmp = signed_pre_key_record.key_pair).public.serialize())))) .put_node(new StanzaNode.build("signedPreKeySignature", NS_URI) .put_node(new StanzaNode.text(Base64.encode(signed_pre_key_record.signature)))) .put_node(new StanzaNode.build("identityKey", NS_URI) .put_node(new StanzaNode.text(Base64.encode(identity_key_pair.public.serialize())))); StanzaNode prekeys = new StanzaNode.build("prekeys", NS_URI); foreach (PreKeyRecord pre_key_record in pre_key_records) { prekeys.put_node(new StanzaNode.build("preKeyPublic", NS_URI) .put_attribute("preKeyId", pre_key_record.id.to_string()) .put_node(new StanzaNode.text(Base64.encode(pre_key_record.key_pair.public.serialize())))); } bundle.put_node(prekeys); string node_id = @"$NODE_BUNDLES:$device_id"; yield stream.get_module(Pubsub.Module.IDENTITY).publish(stream, null, node_id, "1", bundle); yield try_make_node_public(stream, node_id); } private async void try_make_node_public(XmppStream stream, string node_id) { DataForms.DataForm? data_form = yield stream.get_module(Pubsub.Module.IDENTITY).request_node_config(stream, null, node_id); if (data_form == null) return; foreach (DataForms.DataForm.Field field in data_form.fields) { if (field.var == "pubsub#access_model" && field.get_value_string() != Pubsub.ACCESS_MODEL_OPEN) { field.set_value_string(Pubsub.ACCESS_MODEL_OPEN); yield stream.get_module(Pubsub.Module.IDENTITY).submit_node_config(stream, data_form, node_id); break; } } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/plugins/omemo/src/register_plugin.vala0000664000000000000000000000013614776241610020610 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Omemo.Plugin); } dino-0.5.0/plugins/omemo/src/trust_level.vala0000664000000000000000000000032414776241610017755 0ustar rootrootnamespace Dino.Plugins.Omemo { public enum TrustLevel { VERIFIED, TRUSTED, UNTRUSTED, UNKNOWN; public string to_string() { int val = this; return val.to_string(); } } } dino-0.5.0/plugins/omemo/src/ui/0000775000000000000000000000000014776241610015156 5ustar rootrootdino-0.5.0/plugins/omemo/src/ui/bad_messages_populator.vala0000664000000000000000000001740314776241610022552 0ustar rootrootusing Gee; using Gtk; using Qlite; using Dino.Entities; using Xmpp; namespace Dino.Plugins.Omemo { public enum BadnessType { UNTRUSTED, UNDECRYPTABLE } public class BadMessagesPopulator : Plugins.ConversationItemPopulator, Plugins.ConversationAdditionPopulator, Object { public string id { get { return "bad_omemo_messages"; } } private StreamInteractor stream_interactor; private Plugin plugin; private Database db; private Conversation? current_conversation; private Plugins.ConversationItemCollection? item_collection; private Gee.List bad_items = new ArrayList(); public BadMessagesPopulator(StreamInteractor stream_interactor, Plugin plugin) { this.stream_interactor = stream_interactor; this.plugin = plugin; this.db = plugin.db; plugin.trust_manager.bad_message_state_updated.connect((account, jid, device_id) => { clear_state(); init_state(); }); } private void init_state() { if (current_conversation == null) return; if (current_conversation.type_ == Conversation.Type.GROUPCHAT_PM) return; var qry = db.identity_meta.select() .join_with(db.identity, db.identity.id, db.identity_meta.identity_id) .with(db.identity.account_id, "=", current_conversation.account.id) .where("last_message_untrusted is not NULL OR last_message_undecryptable is not NULL"); switch (current_conversation.type_) { case Conversation.Type.CHAT: qry.with(db.identity_meta.address_name, "=", current_conversation.counterpart.to_string()); break; case Conversation.Type.GROUPCHAT: bool is_private = stream_interactor.get_module(MucManager.IDENTITY).is_private_room(current_conversation.account, current_conversation.counterpart); if (!is_private) return; var list = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(current_conversation.counterpart, current_conversation.account); if (list == null || list.is_empty) return; var selection = new StringBuilder(); string[] selection_args = {}; foreach (Jid jid in list) { if (selection.len == 0) { selection.append(@" ($(db.identity_meta.address_name) = ?"); } else { selection.append(@" OR $(db.identity_meta.address_name) = ?"); } selection_args += jid.to_string(); } selection.append(")"); qry.where(selection.str, selection_args); break; case Conversation.Type.GROUPCHAT_PM: break; } foreach (Row row in qry) { Jid jid = new Jid(row[db.identity_meta.address_name]); if (!db.identity_meta.last_message_untrusted.is_null(row)) { DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_untrusted]); var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNTRUSTED); bad_items.add(item); item_collection.insert_item(item); } if (!db.identity_meta.last_message_undecryptable.is_null(row)) { DateTime time = new DateTime.from_unix_utc(row[db.identity_meta.last_message_undecryptable]); var item = new BadMessageItem(plugin, current_conversation, jid, time, BadnessType.UNDECRYPTABLE); bad_items.add(item); item_collection.insert_item(item); } } } private void clear_state() { foreach (BadMessageItem bad_item in bad_items) { item_collection.remove_item(bad_item); } bad_items.clear(); } public void init(Conversation conversation, Plugins.ConversationItemCollection item_collection, Plugins.WidgetType type) { current_conversation = conversation; this.item_collection = item_collection; init_state(); } public void close(Conversation conversation) { clear_state(); } public void populate_timespan(Conversation conversation, DateTime after, DateTime before) { } } public class BadMessageItem : Plugins.MetaConversationItem { private Plugin plugin; private Conversation conversation; private Jid problem_jid; private BadnessType badness_type; public BadMessageItem(Plugin plugin, Conversation conversation, Jid jid, DateTime date, BadnessType badness_type) { this.plugin = plugin; this.conversation = conversation; this.problem_jid = jid; this.time = date; this.badness_type = badness_type; } public override Object? get_widget(Plugins.ConversationItemWidgetInterface outer, Plugins.WidgetType widget_type) { return new BadMessagesWidget(plugin, conversation, problem_jid, badness_type); } public override Gee.List? get_item_actions(Plugins.WidgetType type) { return null; } } public class BadMessagesWidget : Box { private Plugin plugin; private Conversation conversation; private Jid jid; private Label label; public BadMessagesWidget(Plugin plugin, Conversation conversation, Jid jid, BadnessType badness_type) { Object(orientation:Orientation.HORIZONTAL, spacing:5); this.plugin = plugin; this.conversation = conversation; this.jid = jid; this.halign = Align.CENTER; this.visible = true; string who = ""; if (conversation.type_ == Conversation.Type.CHAT) { who = Dino.get_participant_display_name(plugin.app.stream_interactor, conversation, jid); } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { who = jid.to_string(); // `jid` is a real JID. In MUCs, try to show nicks instead (given that the JID is currently online) var occupants = plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants == null) return; foreach (Jid occupant in occupants) { if (jid.equals_bare(plugin.app.stream_interactor.get_module(MucManager.IDENTITY).get_real_jid(occupant, conversation.account))) { who = occupant.resourcepart; } } } string warning_text = ""; if (badness_type == BadnessType.UNTRUSTED) { warning_text = _("%s has been using an untrusted device. You won't see messages from devices that you do not trust.").printf(who) + " %s".printf(_("Manage devices")); } else { warning_text += _("%s does not trust this device. That means, you might be missing messages.").printf(who); } label = new Label(warning_text) { margin_start=70, margin_end=70, justify=Justification.CENTER, use_markup=true, selectable=true, wrap=true, wrap_mode=Pango.WrapMode.WORD_CHAR, hexpand=true }; label.add_css_class("dim-label"); this.append(label); if (badness_type == BadnessType.UNTRUSTED) { label.activate_link.connect(on_label_activate_link); } } private bool on_label_activate_link() { var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")}); GLib.Application.get_default().activate_action("open-conversation-details", variant); return false; } public override void dispose() { if (label != null) { label.unparent(); label.dispose(); label = null; } base.dispose(); } } } dino-0.5.0/plugins/omemo/src/ui/call_encryption_entry.vala0000664000000000000000000000345714776241610022442 0ustar rootrootusing Dino.Entities; using Gtk; using Qlite; using Xmpp; namespace Dino.Plugins.Omemo { public class CallEncryptionEntry : Plugins.CallEncryptionEntry, Object { private Database db; public CallEncryptionEntry(Database db) { this.db = db; } public Plugins.CallEncryptionWidget? get_widget(Account account, Xmpp.Xep.Jingle.ContentEncryption encryption) { DtlsSrtpVerificationDraft.OmemoContentEncryption? omemo_encryption = encryption as DtlsSrtpVerificationDraft.OmemoContentEncryption; if (omemo_encryption == null) return null; int identity_id = db.identity.get_id(account.id); Row? device = db.identity_meta.get_device(identity_id, omemo_encryption.jid.to_string(), omemo_encryption.sid); if (device == null) return null; TrustLevel trust = (TrustLevel) device[db.identity_meta.trust_level]; return new CallEncryptionWidget(trust); } } public class CallEncryptionWidget : Plugins.CallEncryptionWidget, Object { string? title = null; string? icon = null; bool should_show_keys = false; public CallEncryptionWidget(TrustLevel trust) { if (trust == TrustLevel.VERIFIED) { title = "This call is encrypted and verified with OMEMO."; icon = "dino-security-high-symbolic"; should_show_keys = false; } else { title = "This call is encrypted with OMEMO."; should_show_keys = true; } } public string? get_title() { return title; } public string? get_icon_name() { return icon; } public bool show_keys() { return should_show_keys; } } } dino-0.5.0/plugins/omemo/src/ui/contact_details_provider.vala0000664000000000000000000000144314776241610023077 0ustar rootrootusing Gtk; using Gee; using Qlite; using Dino.Entities; namespace Dino.Plugins.Omemo { public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "omemo_info"; } } public string tab { get { return "encryption"; } } private Plugin plugin; public ContactDetailsProvider(Plugin plugin) { this.plugin = plugin; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { } public Object? get_widget(Conversation conversation) { if (conversation.type_ != Conversation.Type.CHAT) return null; var widget = new OmemoPreferencesWidget(plugin); widget.set_jid(conversation.account, conversation.counterpart); return widget; } } } dino-0.5.0/plugins/omemo/src/ui/device_notification_populator.vala0000664000000000000000000000640514776241610024142 0ustar rootrootusing Dino.Entities; using Xmpp; using Gtk; namespace Dino.Plugins.Omemo { public class DeviceNotificationPopulator : NotificationPopulator, Object { public string id { get { return "device_notification"; } } private StreamInteractor? stream_interactor; private Plugin plugin; private Conversation? current_conversation; private NotificationCollection? notification_collection; private ConversationNotification notification; public DeviceNotificationPopulator(Plugin plugin, StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; this.plugin = plugin; stream_interactor.account_added.connect(on_account_added); } public void init(Conversation conversation, NotificationCollection notification_collection, Plugins.WidgetType type) { current_conversation = conversation; this.notification_collection = notification_collection; if (plugin.has_new_devices(conversation.account, conversation.counterpart) && conversation.type_ == Conversation.Type.CHAT) { display_notification(); } } public void close(Conversation conversation) { notification = null; } private void display_notification() { if (notification == null) { notification = new ConversationNotification(plugin, current_conversation); notification.should_hide.connect(should_hide); notification_collection.add_meta_notification(notification); } } public void should_hide() { if (!plugin.has_new_devices(current_conversation.account, current_conversation.counterpart) && notification != null){ notification_collection.remove_meta_notification(notification); notification = null; } } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { if (current_conversation != null && jid.equals(current_conversation.counterpart) && plugin.has_new_devices(current_conversation.account, current_conversation.counterpart)) { display_notification(); } }); } } private class ConversationNotification : MetaConversationNotification { private Widget widget; private Plugin plugin; private Jid jid; private Account account; public signal void should_hide(); public ConversationNotification(Plugin plugin, Conversation conversation) { this.plugin = plugin; this.jid = jid; this.account = account; Box box = new Box(Orientation.HORIZONTAL, 5); Button manage_button = new Button.with_label(_("Manage")); manage_button.clicked.connect(() => { manage_button.activate(); var variant = new Variant.tuple(new Variant[] {new Variant.int32(conversation.id), new Variant.string("encryption")}); GLib.Application.get_default().activate_action("open-conversation-details", variant); }); box.append(new Label(_("This contact has new devices")) { margin_end=10 }); box.append(manage_button); widget = box; } public override Object? get_widget(WidgetType type) { return widget; } } } dino-0.5.0/plugins/omemo/src/ui/encryption_list_entry.vala0000664000000000000000000000660314776241610022476 0ustar rootrootusing Dino.Entities; using Gtk; using Qlite; using Xmpp; namespace Dino.Plugins.Omemo { public class EncryptionListEntry : Plugins.EncryptionListEntry, Object { private Plugin plugin; private Database db; public EncryptionListEntry(Plugin plugin) { this.plugin = plugin; this.db = plugin.db; } public Entities.Encryption encryption { get { return Entities.Encryption.OMEMO; }} public string name { get { return "OMEMO"; }} public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { return null; } public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { if (content_item.encryption != encryption) return null; RowOption row = db.content_item_meta.select( { db.identity_meta.trust_level } ).with(db.content_item_meta.content_item_id, "=", content_item.id) .join_on(db.identity_meta, @"$(db.identity_meta.address_name)=$(db.content_item_meta.address_name) AND $(db.identity_meta.device_id)=$(db.content_item_meta.device_id)") .single().row(); if (row.is_present() && (TrustLevel) row[db.identity_meta.trust_level] == TrustLevel.VERIFIED) { return "dino-security-high-symbolic"; } return null; } public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { encryption_activated_async.begin(conversation, input_status_callback); } public async void encryption_activated_async(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { if (conversation.type_ == Conversation.Type.GROUPCHAT_PM) { input_status_callback(new Plugins.InputFieldStatus("Can't use encryption in a groupchat private message.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } MucManager muc_manager = plugin.app.stream_interactor.get_module(MucManager.IDENTITY); Manager omemo_manager = plugin.app.stream_interactor.get_module(Manager.IDENTITY); if (muc_manager.is_private_room(conversation.account, conversation.counterpart)) { var offline_members = muc_manager.get_offline_members(conversation.counterpart, conversation.account); if (offline_members == null) { // We don't store offline members yet, and it'll be null if we're offline return; } foreach (Jid offline_member in offline_members) { bool ok = yield omemo_manager.ensure_get_keys_for_jid(conversation.account, offline_member); if (!ok) { input_status_callback(new Plugins.InputFieldStatus("A member does not support OMEMO: %s".printf(offline_member.to_string()), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } } return; } if (!(yield omemo_manager.ensure_get_keys_for_jid(conversation.account, conversation.counterpart.bare_jid))) { input_status_callback(new Plugins.InputFieldStatus("This contact does not support %s encryption".printf("OMEMO"), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); } } } } dino-0.5.0/plugins/omemo/src/ui/encryption_preferences_entry.vala0000664000000000000000000003510114776241610024017 0ustar rootrootusing Qlite; using Qrencode; using Gee; using Xmpp; using Dino.Entities; using Gtk; using Omemo; namespace Dino.Plugins.Omemo { public class OmemoPreferencesEntry : Plugins.EncryptionPreferencesEntry { OmemoPreferencesWidget widget; Plugin plugin; public OmemoPreferencesEntry(Plugin plugin) { this.plugin = plugin; } public override Object? get_widget(Account account, WidgetType type) { if (type != WidgetType.GTK4) return null; var widget = new OmemoPreferencesWidget(plugin); widget.set_jid(account, account.bare_jid); return widget; } public override string id { get { return "omemo_preferences_entryption"; }} } [GtkTemplate (ui = "/im/dino/Dino/omemo/encryption_preferences_entry.ui")] public class OmemoPreferencesWidget : Adw.PreferencesGroup { private Plugin plugin; private Account account; private Jid jid; private int identity_id = 0; private Store store; private Set displayed_ids = new HashSet(); [GtkChild] private unowned Adw.ActionRow automatically_accept_new_row; [GtkChild] private Switch automatically_accept_new_switch; [GtkChild] private unowned Adw.ActionRow encrypt_by_default_row; [GtkChild] private Switch encrypt_by_default_switch; [GtkChild] private unowned Label new_keys_label; [GtkChild] private unowned Adw.PreferencesGroup keys_preferences_group; [GtkChild] private unowned ListBox new_keys_listbox; [GtkChild] private unowned Picture qrcode_picture; [GtkChild] private unowned Popover qrcode_popover; private ArrayList keys_preferences_group_children = new ArrayList(); construct { // If we set the strings in the .ui file, they don't get translated encrypt_by_default_row.title = _("OMEMO by default"); encrypt_by_default_row.subtitle = _("Enable OMEMO encryption for new conversations"); automatically_accept_new_row.title = _("Encrypt to new devices"); automatically_accept_new_row.subtitle = _("Automatically encrypt to new devices from this contact."); new_keys_label.label = _("New keys"); } public OmemoPreferencesWidget(Plugin plugin) { this.plugin = plugin; } public void set_jid(Account account, Jid jid) { this.account = account; this.jid = jid; this.identity_id = plugin.db.identity.get_id(account.id); if (identity_id <= 0) { warning("OmemoPreferencesWidget missing identity_id"); return; } automatically_accept_new_switch.set_active(plugin.db.trust.get_blind_trust(identity_id, jid.bare_jid.to_string(), true)); automatically_accept_new_switch.state_set.connect(on_auto_accept_toggled); encrypt_by_default_row.visible = account.bare_jid.equals_bare(jid); encrypt_by_default_switch.set_active(plugin.app.settings.get_default_encryption(account) != Encryption.NONE); encrypt_by_default_switch.state_set.connect(on_omemo_by_default_toggled); Dino.Application? app = Application.get_default() as Dino.Application; if (app != null) { store = app.stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).store; } redraw_key_list(); // Check for unknown devices fetch_unknown_bundles(); } private void redraw_key_list() { // Remove current widgets foreach (var widget in keys_preferences_group_children) { keys_preferences_group.remove(widget); } keys_preferences_group_children.clear(); // Dialog opened from the account settings menu // Show the fingerprint for this device separately with buttons for a qrcode and to copy if(jid.equals(account.bare_jid)) { automatically_accept_new_row.subtitle = _("New encryption keys from your other devices will be accepted automatically."); add_own_fingerprint(); } //Show the normal devicelist var own_id = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; foreach (Row device in plugin.db.identity_meta.get_known_devices(identity_id, jid.to_string())) { if(jid.equals(account.bare_jid) && device[plugin.db.identity_meta.device_id] == own_id) { // If this is our own account, don't show this device twice (did it separately already) continue; } add_fingerprint(device, (TrustLevel) device[plugin.db.identity_meta.trust_level]); } //Show any new devices for which the user must decide whether to accept or reject foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { add_new_fingerprint(device); } } private static string escape_for_iri_path_segment(string s) { // from RFC 3986, 2.2. Reserved Characters: string SUB_DELIMS = "!$&'()*+,;="; // from RFC 3986, 3.3. Path (pchar without unreserved and pct-encoded): string ALLOWED_RESERVED_CHARS = SUB_DELIMS + ":@"; return GLib.Uri.escape_string(s, ALLOWED_RESERVED_CHARS, true); } private void fetch_unknown_bundles() { Dino.Application app = Application.get_default() as Dino.Application; XmppStream? stream = app.stream_interactor.get_stream(account); if (stream == null) return; StreamModule? module = stream.get_module(StreamModule.IDENTITY); if (module == null) return; module.bundle_fetched.connect_after((bundle_jid, device_id, bundle) => { if (bundle_jid.equals(jid) && !displayed_ids.contains(device_id)) { redraw_key_list(); } }); foreach (Row device in plugin.db.identity_meta.get_unknown_devices(identity_id, jid.to_string())) { try { module.fetch_bundle(stream, new Jid(device[plugin.db.identity_meta.address_name]), device[plugin.db.identity_meta.device_id], false); } catch (InvalidJidError e) { warning("Ignoring device with invalid Jid: %s", e.message); } } } private void add_own_fingerprint() { string own_b64 = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.identity_key_public_base64]; string fingerprint = fingerprint_from_base64(own_b64); var own_action_box = new Box(Orientation.HORIZONTAL, 6); var show_qrcode_button = new MenuButton() { icon_name="dino-qr-code-symbolic", valign=Align.CENTER }; own_action_box.append(show_qrcode_button); var copy_button = new Button() { icon_name="edit-copy-symbolic", valign=Align.CENTER }; copy_button.clicked.connect(() => { copy_button.get_clipboard().set_text(fingerprint); }); own_action_box.append(copy_button); Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; action_row.title = _("This device"); action_row.subtitle = fingerprint_markup(fingerprint_from_base64(own_b64)); action_row.add_suffix(own_action_box); add_key_row(action_row); // Create and set QR code popover int sid = plugin.db.identity.row_with(plugin.db.identity.account_id, account.id)[plugin.db.identity.device_id]; var iri_query = @"omemo-sid-$(sid)=$(fingerprint)"; #if GLIB_2_66 && VALA_0_50 string iri = GLib.Uri.join(UriFlags.NONE, "xmpp", null, null, 0, jid.to_string(), iri_query, null); #else var iri_path_seg = escape_for_iri_path_segment(jid.to_string()); var iri = @"xmpp:$(iri_path_seg)?$(iri_query)"; #endif const int QUIET_ZONE_MODULES = 4; // MUST be at least 4 const int MODULE_SIZE_PX = 4; // arbitrary var qr_paintable = new QRcode(iri, 2) .to_paintable(MODULE_SIZE_PX * qrcode_picture.scale_factor); qrcode_picture.paintable = qr_paintable; qrcode_picture.margin_top = qrcode_picture.margin_end = qrcode_picture.margin_bottom = qrcode_picture.margin_start = QUIET_ZONE_MODULES * MODULE_SIZE_PX; qrcode_popover.add_css_class("qrcode-container"); show_qrcode_button.popover = qrcode_popover; } private void add_fingerprint(Row device, TrustLevel trust) { string key_base64 = device[plugin.db.identity_meta.identity_key_public_base64]; bool key_active = device[plugin.db.identity_meta.now_active]; if (store != null) { try { Address address = new Address(jid.to_string(), device[plugin.db.identity_meta.device_id]); SessionRecord? session = null; if (store.contains_session(address)) { session = store.load_session(address); string session_key_base64 = Base64.encode(session.state.remote_identity_key.serialize()); if (key_base64 != session_key_base64) { critical("Session and database identity key mismatch!"); key_base64 = session_key_base64; } } } catch (Error e) { print("Error while reading session store: %s", e.message); } } if (device[plugin.db.identity_meta.now_active]) { Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; action_row.activated.connect(() => { Row updated_device = plugin.db.identity_meta.get_device(device[plugin.db.identity_meta.identity_id], device[plugin.db.identity_meta.address_name], device[plugin.db.identity_meta.device_id]); ManageKeyDialog manage_dialog = new ManageKeyDialog(updated_device, plugin.db); manage_dialog.set_transient_for((Gtk.Window) get_root()); manage_dialog.present(); manage_dialog.response.connect((response) => { update_stored_trust(response, updated_device); redraw_key_list(); }); }); action_row.activatable = true; action_row.title = account.bare_jid.equals_bare(jid) ? _("Other device") : _("Device"); action_row.subtitle = fingerprint_markup(fingerprint_from_base64(key_base64)); string trust_str = _("Accepted"); switch(trust) { case TrustLevel.UNTRUSTED: trust_str = _("Rejected"); break; case TrustLevel.VERIFIED: trust_str = _("Verified"); break; } action_row.add_suffix(new Label(trust_str)); add_key_row(action_row); } displayed_ids.add(device[plugin.db.identity_meta.device_id]); } private bool on_auto_accept_toggled(bool active) { plugin.trust_manager.set_blind_trust(account, jid, active); if (active) { int identity_id = plugin.db.identity.get_id(account.id); if (identity_id < 0) return false; foreach (Row device in plugin.db.identity_meta.get_new_devices(identity_id, jid.to_string())) { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); add_fingerprint(device, TrustLevel.TRUSTED); } } return false; } private bool on_omemo_by_default_toggled(bool active) { var encryption_value = active ? Encryption.OMEMO : Encryption.NONE; plugin.app.settings.set_default_encryption(account, encryption_value); return false; } private void update_stored_trust(int response, Row device) { switch (response) { case TrustLevel.TRUSTED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); break; case TrustLevel.UNTRUSTED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); break; case TrustLevel.VERIFIED: plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.VERIFIED); plugin.trust_manager.set_blind_trust(account, jid, false); automatically_accept_new_switch.set_active(false); break; } } private void add_new_fingerprint(Row device) { Adw.ActionRow action_row = new Adw.ActionRow() { use_markup = true }; action_row.title = _("New device"); action_row.subtitle = fingerprint_markup(fingerprint_from_base64(device[plugin.db.identity_meta.identity_key_public_base64])); Button accept_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; accept_button.set_icon_name("emblem-ok-symbolic"); // using .image = sets .image-button. Together with .suggested/destructive action that breaks the button Adwaita accept_button.add_css_class("suggested-action"); accept_button.tooltip_text = _("Accept key"); Button reject_button = new Button() { visible = true, valign = Align.CENTER, hexpand = true }; reject_button.set_icon_name("action-unavailable-symbolic"); reject_button.add_css_class("destructive-action"); reject_button.tooltip_text = _("Reject key"); accept_button.clicked.connect(() => { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.TRUSTED); add_fingerprint(device, TrustLevel.TRUSTED); remove_key_row(action_row); }); reject_button.clicked.connect(() => { plugin.trust_manager.set_device_trust(account, jid, device[plugin.db.identity_meta.device_id], TrustLevel.UNTRUSTED); add_fingerprint(device, TrustLevel.UNTRUSTED); remove_key_row(action_row); }); Box control_box = new Box(Gtk.Orientation.HORIZONTAL, 0) { visible = true, hexpand = true }; control_box.append(accept_button); control_box.append(reject_button); control_box.add_css_class("linked"); // .linked: Visually link the accept / reject buttons action_row.add_suffix(control_box); add_key_row(action_row); displayed_ids.add(device[plugin.db.identity_meta.device_id]); } private void add_key_row(Adw.PreferencesRow widget) { keys_preferences_group.add(widget); keys_preferences_group_children.add(widget); } private void remove_key_row(Adw.PreferencesRow widget) { keys_preferences_group.remove(widget); keys_preferences_group_children.remove(widget); } } }dino-0.5.0/plugins/omemo/src/ui/manage_key_dialog.vala0000664000000000000000000002061714776241610021450 0ustar rootrootusing Gtk; using Qlite; namespace Dino.Plugins.Omemo { [GtkTemplate (ui = "/im/dino/Dino/omemo/manage_key_dialog.ui")] public class ManageKeyDialog : Gtk.Dialog { [GtkChild] private unowned Stack manage_stack; [GtkChild] private unowned Button cancel_button; [GtkChild] private unowned Button ok_button; [GtkChild] private unowned Label main_desc_label; [GtkChild] private unowned ListBox main_action_list; [GtkChild] private unowned Image confirm_image; [GtkChild] private unowned Label confirm_title_label; [GtkChild] private unowned Label confirm_desc_label; [GtkChild] private unowned Label verify_label; [GtkChild] private unowned Label compare_fingerprint_label; [GtkChild] private unowned Button verify_yes_button; [GtkChild] private unowned Button verify_no_button; private Row device; private Database db; private bool return_to_main; private int current_response; construct { // If we set the strings in the .ui file, they don't get translated this.title = _("Manage Key"); compare_fingerprint_label.label = _("Compare the fingerprint, character by character, with the one shown on your contact's device."); verify_no_button.label = _("Fingerprints differ"); verify_yes_button.label = _("Fingerprints match"); cancel_button.label = _("Cancel"); ok_button.label = _("Confirm"); } public ManageKeyDialog(Row device, Database db) { Object(use_header_bar : 1); this.device = device; this.db = db; setup_main_screen(); setup_verify_screen(); cancel_button.clicked.connect(handle_cancel); ok_button.clicked.connect(() => { response(current_response); close(); }); verify_yes_button.clicked.connect(() => { confirm_image.set_from_icon_name("security-high-symbolic"); confirm_title_label.label = _("Verify key"); confirm_desc_label.set_markup(_("Future messages sent by %s from the device that uses this key will be highlighted accordingly in the chat window.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = false; current_response = TrustLevel.VERIFIED; }); verify_no_button.clicked.connect(() => { return_to_main = false; confirm_image.set_from_icon_name("dialog-warning-symbolic"); confirm_title_label.label = _("Fingerprints do not match"); confirm_desc_label.set_markup(_("Please verify that you are comparing the correct fingerprint. If fingerprints do not match, %s's account may be compromised and you should consider rejecting this key.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); }); } private void handle_cancel() { if (manage_stack.get_visible_child_name() == "main") close(); if (manage_stack.get_visible_child_name() == "verify") { manage_stack.set_visible_child_name("main"); cancel_button.label = _("Cancel"); } if (manage_stack.get_visible_child_name() == "confirm") { if (return_to_main) { manage_stack.set_visible_child_name("main"); cancel_button.label = _("Cancel"); } else { manage_stack.set_visible_child_name("verify"); } } ok_button.sensitive = false; } private Box make_action_box(string title, string desc){ Box box = new Box(Orientation.VERTICAL, 0) { visible = true, margin_start = 20, margin_end = 20, margin_top = 14, margin_bottom = 14 }; Label lbl_title = new Label(title) { visible = true, halign = Align.START }; Label lbl_desc = new Label(desc) { visible = true, xalign = 0, wrap = true, max_width_chars = 40 }; Pango.AttrList title_attrs = new Pango.AttrList(); title_attrs.insert(Pango.attr_scale_new(1.1)); lbl_title.attributes = title_attrs; Pango.AttrList desc_attrs = new Pango.AttrList(); desc_attrs.insert(Pango.attr_scale_new(0.8)); lbl_desc.attributes = desc_attrs; lbl_desc.add_css_class("dim-label"); box.append(lbl_title); box.append(lbl_desc); return box; } private void setup_main_screen() { main_action_list.set_header_func((row, before_row) => { if (row.get_header() == null && before_row != null) { row.set_header(new Separator(Orientation.HORIZONTAL)); } }); ListBoxRow verify_row = new ListBoxRow() { visible = true }; verify_row.set_child(make_action_box(_("Verify key fingerprint"), _("Compare this key's fingerprint with the fingerprint displayed on the contact's device."))); ListBoxRow reject_row = new ListBoxRow() { visible = true }; reject_row.set_child(make_action_box(_("Reject key"), _("Block encrypted communication with the contact's device that uses this key."))); ListBoxRow accept_row = new ListBoxRow() {visible = true }; accept_row.set_child(make_action_box(_("Accept key"), _("Allow encrypted communication with the contact's device that uses this key."))); switch((TrustLevel) device[db.identity_meta.trust_level]) { case TrustLevel.TRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("accepted")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.append(verify_row); main_action_list.append(reject_row); break; case TrustLevel.VERIFIED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("verified")+"")+" "+_("This means it can be used by %s to receive and send encrypted messages.").printf(@"$(device[db.identity_meta.address_name])") + " " + _("Additionally it has been verified to match the key on the contact's device.")); main_action_list.append(reject_row); break; case TrustLevel.UNTRUSTED: main_desc_label.set_markup(_("This key is currently %s.").printf(""+_("rejected")+"")+" "+_("This means it cannot be used by %s to decipher your messages, and you won't see messages encrypted with it.").printf(@"$(device[db.identity_meta.address_name])")); main_action_list.append(accept_row); break; } //Row clicked - go to appropriate screen main_action_list.row_activated.connect((row) => { if(row == verify_row) { manage_stack.set_visible_child_name("verify"); } else if (row == reject_row) { confirm_image.set_from_icon_name("action-unavailable-symbolic"); confirm_title_label.label = _("Reject key"); confirm_desc_label.set_markup(_("You won't see encrypted messages from the device of %s that uses this key. Conversely, that device won't be able to decipher your messages anymore.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; current_response = TrustLevel.UNTRUSTED; } else if (row == accept_row) { confirm_image.set_from_icon_name("emblem-ok-symbolic"); confirm_title_label.label = _("Accept key"); confirm_desc_label.set_markup(_("You will be able to exchange encrypted messages with the device of %s that uses this key.").printf(@"$(device[db.identity_meta.address_name])")); manage_stack.set_visible_child_name("confirm"); ok_button.sensitive = true; return_to_main = true; current_response = TrustLevel.TRUSTED; } cancel_button.label = _("Back"); }); manage_stack.set_visible_child_name("main"); } private void setup_verify_screen() { verify_label.set_markup(fingerprint_markup(fingerprint_from_base64(device[db.identity_meta.identity_key_public_base64]))); } } } dino-0.5.0/plugins/omemo/src/ui/own_notifications.vala0000664000000000000000000000244614776241610021565 0ustar rootrootusing Dino.Entities; using Xmpp; using Gtk; namespace Dino.Plugins.Omemo { public class OwnNotifications { private StreamInteractor stream_interactor; private Plugin plugin; private Account account; public OwnNotifications (Plugin plugin, StreamInteractor stream_interactor, Account account) { this.stream_interactor = (!)stream_interactor; this.plugin = plugin; this.account = account; stream_interactor.module_manager.get_module(account, StreamModule.IDENTITY).bundle_fetched.connect_after((jid, device_id, bundle) => { if (jid.equals(account.bare_jid) && plugin.has_new_devices(account, account.bare_jid)) { display_notification(); } }); if (plugin.has_new_devices(account, account.bare_jid)) { display_notification(); } } private void display_notification() { Notification notification = new Notification(_("OMEMO trust decision required")); notification.set_default_action_and_target_value("app.own-keys", new Variant.int32(account.id)); notification.set_body(_("Did you add a new device for account %s?").printf(@"$(account.bare_jid.to_string())")); plugin.app.send_notification(account.id.to_string()+"-new-device", notification); } } } dino-0.5.0/plugins/omemo/src/ui/util.vala0000664000000000000000000000172014776241610017000 0ustar rootrootusing Xmpp.Util; namespace Dino.Plugins.Omemo { public static string fingerprint_from_base64(string b64) { uint8[] arr = Base64.decode(b64); arr = arr[1:arr.length]; string s = ""; foreach (uint8 i in arr) { string tmp = i.to_string("%x"); if (tmp.length == 1) tmp = "0" + tmp; s = s + tmp; } return s; } public static string fingerprint_markup(string s) { return "" + format_fingerprint(s) + ""; } public static string format_fingerprint(string s) { string markup = ""; for (int i = 0; i < s.length; i += 4) { string four_chars = s.substring(i, 4).down(); if (i % 32 == 0 && i != 0) markup += "\n"; markup += four_chars; if (i % 16 == 12 && i % 32 != 28) { markup += " "; } if (i % 8 == 4 && i % 16 != 12) { markup += "\u00a0"; // Non-breaking space } } return markup; } } dino-0.5.0/plugins/omemo/tests/0000775000000000000000000000000014776241610015114 5ustar rootrootdino-0.5.0/plugins/omemo/tests/native/0000775000000000000000000000000014776241610016402 5ustar rootrootdino-0.5.0/plugins/omemo/tests/native/common.vala0000664000000000000000000000551214776241610020542 0ustar rootrootnamespace Omemo.Test { int main(string[] args) { GLib.Test.init(ref args); GLib.Test.set_nonfatal_assertions(); TestSuite.get_root().add_suite(new Curve25519().get_suite()); TestSuite.get_root().add_suite(new SessionBuilderTest().get_suite()); TestSuite.get_root().add_suite(new HKDF().get_suite()); return GLib.Test.run(); } Store setup_test_store_context(Context global_context) { Store store = global_context.create_store(); try { store.identity_key_store.local_registration_id = (Random.next_int() % 16380) + 1; ECKeyPair key_pair = global_context.generate_key_pair(); store.identity_key_store.identity_key_private = new Bytes(key_pair.private.serialize()); store.identity_key_store.identity_key_public = new Bytes(key_pair.public.serialize()); } catch (Error e) { fail_if_reached(); } return store; } ECPublicKey? create_test_ec_public_key(Context context) { try { return context.generate_key_pair().public; } catch (Error e) { fail_if_reached(); return null; } } bool fail_if(bool exp, string? reason = null) { if (exp) { if (reason != null) GLib.Test.message(reason); GLib.Test.fail(); return true; } return false; } void fail_if_reached(string? reason = null) { fail_if(true, reason); } delegate void ErrorFunc() throws Error; void fail_if_not_error_code(ErrorFunc func, int expectedCode, string? reason = null) { try { func(); fail_if_reached(@"$(reason + ": " ?? "")no error thrown"); } catch (Error e) { fail_if_not_eq_int(e.code, expectedCode, @"$(reason + ": " ?? "")caught unexpected error"); } } bool fail_if_not(bool exp, string? reason = null) { return fail_if(!exp, reason); } bool fail_if_eq_int(int left, int right, string? reason = null) { return fail_if(left == right, @"$(reason + ": " ?? "")$left == $right"); } bool fail_if_not_eq_int(int left, int right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_str(string left, string right, string? reason = null) { return fail_if_not(left == right, @"$(reason + ": " ?? "")$left != $right"); } bool fail_if_not_eq_uint8_arr(uint8[] left, uint8[] right, string? reason = null) { if (fail_if_not_eq_int(left.length, right.length, @"$(reason + ": " ?? "")array length not equal")) return true; return fail_if_not_eq_str(Base64.encode(left), Base64.encode(right), reason); } bool fail_if_not_zero_int(int zero, string? reason = null) { return fail_if_not_eq_int(zero, 0, reason); } bool fail_if_zero_int(int zero, string? reason = null) { return fail_if_eq_int(zero, 0, reason); } bool fail_if_null(void* what, string? reason = null) { return fail_if(what == null || (size_t)what == 0, reason); } } dino-0.5.0/plugins/omemo/tests/native/curve25519.vala0000664000000000000000000002012214776241610020776 0ustar rootrootnamespace Omemo.Test { class Curve25519 : Gee.TestCase { public Curve25519() { base("Curve25519"); add_test("agreement", test_curve25519_agreement); add_test("generate_public", test_curve25519_generate_public); add_test("random_agreements", test_curve25519_random_agreements); add_test("signature", test_curve25519_signature); } private Context global_context; public override void set_up() { try { global_context = new Context(); } catch (Error e) { fail_if_reached(); } } public override void tear_down() { global_context = null; } void test_curve25519_agreement() { try { uint8[] alicePublic = { 0x05, 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28}; uint8[] alicePrivate = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59}; uint8[] bobPublic = { 0x05, 0x65, 0x36, 0x14, 0x99, 0x3d, 0x2b, 0x15, 0xee, 0x9e, 0x5f, 0xd3, 0xd8, 0x6c, 0xe7, 0x19, 0xef, 0x4e, 0xc1, 0xda, 0xae, 0x18, 0x86, 0xa8, 0x7b, 0x3f, 0x5f, 0xa9, 0x56, 0x5a, 0x27, 0xa2, 0x2f}; uint8[] bobPrivate = { 0xb0, 0x3b, 0x34, 0xc3, 0x3a, 0x1c, 0x44, 0xf2, 0x25, 0xb6, 0x62, 0xd2, 0xbf, 0x48, 0x59, 0xb8, 0x13, 0x54, 0x11, 0xfa, 0x7b, 0x03, 0x86, 0xd4, 0x5f, 0xb7, 0x5d, 0xc5, 0xb9, 0x1b, 0x44, 0x66}; uint8[] shared = { 0x32, 0x5f, 0x23, 0x93, 0x28, 0x94, 0x1c, 0xed, 0x6e, 0x67, 0x3b, 0x86, 0xba, 0x41, 0x01, 0x74, 0x48, 0xe9, 0x9b, 0x64, 0x9a, 0x9c, 0x38, 0x06, 0xc1, 0xdd, 0x7c, 0xa4, 0xc4, 0x77, 0xe6, 0x29}; ECPublicKey alice_public_key = global_context.decode_public_key(alicePublic); ECPrivateKey alice_private_key = global_context.decode_private_key(alicePrivate); ECPublicKey bob_public_key = global_context.decode_public_key(bobPublic); ECPrivateKey bob_private_key = global_context.decode_private_key(bobPrivate); uint8[] shared_one = calculate_agreement(alice_public_key, bob_private_key); uint8[] shared_two = calculate_agreement(bob_public_key, alice_private_key); fail_if_not_eq_int(shared_one.length, 32); fail_if_not_eq_int(shared_two.length, 32); fail_if_not_eq_uint8_arr(shared, shared_one); fail_if_not_eq_uint8_arr(shared_one, shared_two); } catch (Error e) { fail_if_reached(); } } void test_curve25519_generate_public() { try { uint8[] alicePublic = { 0x05, 0x1b, 0xb7, 0x59, 0x66, 0xf2, 0xe9, 0x3a, 0x36, 0x91, 0xdf, 0xff, 0x94, 0x2b, 0xb2, 0xa4, 0x66, 0xa1, 0xc0, 0x8b, 0x8d, 0x78, 0xca, 0x3f, 0x4d, 0x6d, 0xf8, 0xb8, 0xbf, 0xa2, 0xe4, 0xee, 0x28}; uint8[] alicePrivate = { 0xc8, 0x06, 0x43, 0x9d, 0xc9, 0xd2, 0xc4, 0x76, 0xff, 0xed, 0x8f, 0x25, 0x80, 0xc0, 0x88, 0x8d, 0x58, 0xab, 0x40, 0x6b, 0xf7, 0xae, 0x36, 0x98, 0x87, 0x90, 0x21, 0xb9, 0x6b, 0xb4, 0xbf, 0x59}; ECPrivateKey alice_private_key = global_context.decode_private_key(alicePrivate); ECPublicKey alice_expected_public_key = global_context.decode_public_key(alicePublic); ECPublicKey alice_public_key = generate_public_key(alice_private_key); fail_if_not_zero_int(alice_expected_public_key.compare(alice_public_key)); } catch (Error e) { fail_if_reached(); } } void test_curve25519_random_agreements() { try { ECKeyPair alice_key_pair = null; ECPublicKey alice_public_key = null; ECPrivateKey alice_private_key = null; ECKeyPair bob_key_pair = null; ECPublicKey bob_public_key = null; ECPrivateKey bob_private_key = null; uint8[] shared_alice = null; uint8[] shared_bob = null; for (int i = 0; i < 50; i++) { fail_if_null(alice_key_pair = global_context.generate_key_pair()); fail_if_null(alice_public_key = alice_key_pair.public); fail_if_null(alice_private_key = alice_key_pair.private); fail_if_null(bob_key_pair = global_context.generate_key_pair()); fail_if_null(bob_public_key = bob_key_pair.public); fail_if_null(bob_private_key = bob_key_pair.private); shared_alice = calculate_agreement(bob_public_key, alice_private_key); fail_if_not_eq_int(shared_alice.length, 32); shared_bob = calculate_agreement(alice_public_key, bob_private_key); fail_if_not_eq_int(shared_bob.length, 32); fail_if_not_eq_uint8_arr(shared_alice, shared_bob); } } catch (Error e) { fail_if_reached(); } } void test_curve25519_signature() { try { uint8[] aliceIdentityPrivate = { 0xc0, 0x97, 0x24, 0x84, 0x12, 0xe5, 0x8b, 0xf0, 0x5d, 0xf4, 0x87, 0x96, 0x82, 0x05, 0x13, 0x27, 0x94, 0x17, 0x8e, 0x36, 0x76, 0x37, 0xf5, 0x81, 0x8f, 0x81, 0xe0, 0xe6, 0xce, 0x73, 0xe8, 0x65}; uint8[] aliceIdentityPublic = { 0x05, 0xab, 0x7e, 0x71, 0x7d, 0x4a, 0x16, 0x3b, 0x7d, 0x9a, 0x1d, 0x80, 0x71, 0xdf, 0xe9, 0xdc, 0xf8, 0xcd, 0xcd, 0x1c, 0xea, 0x33, 0x39, 0xb6, 0x35, 0x6b, 0xe8, 0x4d, 0x88, 0x7e, 0x32, 0x2c, 0x64}; uint8[] aliceEphemeralPublic = { 0x05, 0xed, 0xce, 0x9d, 0x9c, 0x41, 0x5c, 0xa7, 0x8c, 0xb7, 0x25, 0x2e, 0x72, 0xc2, 0xc4, 0xa5, 0x54, 0xd3, 0xeb, 0x29, 0x48, 0x5a, 0x0e, 0x1d, 0x50, 0x31, 0x18, 0xd1, 0xa8, 0x2d, 0x99, 0xfb, 0x4a}; uint8[] aliceSignature = { 0x5d, 0xe8, 0x8c, 0xa9, 0xa8, 0x9b, 0x4a, 0x11, 0x5d, 0xa7, 0x91, 0x09, 0xc6, 0x7c, 0x9c, 0x74, 0x64, 0xa3, 0xe4, 0x18, 0x02, 0x74, 0xf1, 0xcb, 0x8c, 0x63, 0xc2, 0x98, 0x4e, 0x28, 0x6d, 0xfb, 0xed, 0xe8, 0x2d, 0xeb, 0x9d, 0xcd, 0x9f, 0xae, 0x0b, 0xfb, 0xb8, 0x21, 0x56, 0x9b, 0x3d, 0x90, 0x01, 0xbd, 0x81, 0x30, 0xcd, 0x11, 0xd4, 0x86, 0xce, 0xf0, 0x47, 0xbd, 0x60, 0xb8, 0x6e, 0x88}; global_context.decode_private_key(aliceIdentityPrivate); global_context.decode_public_key(aliceEphemeralPublic); ECPublicKey alice_public_key = global_context.decode_public_key(aliceIdentityPublic); fail_if(!verify_signature(alice_public_key, aliceEphemeralPublic, aliceSignature), "signature verification failed"); uint8[] modifiedSignature = new uint8[aliceSignature.length]; for (int i = 0; i < aliceSignature.length; i++) { Memory.copy(modifiedSignature, aliceSignature, aliceSignature.length); modifiedSignature[i] ^= 0x01; fail_if(verify_signature(alice_public_key, aliceEphemeralPublic, modifiedSignature), "invalid signature verification succeeded"); } } catch (Error e) { fail_if_reached(); } } } }dino-0.5.0/plugins/omemo/tests/native/hkdf.vala0000664000000000000000000000326214776241610020166 0ustar rootrootnamespace Omemo.Test { class HKDF : Gee.TestCase { public HKDF() { base("HKDF"); add_test("vector_v3", test_hkdf_vector_v3); } private Context global_context; public override void set_up() { try { global_context = new Context(); } catch (Error e) { fail_if_reached(); } } public override void tear_down() { global_context = null; } public void test_hkdf_vector_v3() { uint8[] ikm = { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; uint8[] salt = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}; uint8[] info = { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; uint8[] okm = { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}; NativeHkdfContext context = null; fail_if_not_zero_int(NativeHkdfContext.create(out context, 3, global_context.native_context)); uint8[] output = null; int result = (int) context.derive_secrets(out output, ikm, salt, info, 42); fail_if_not_eq_int(result, okm.length); output.length = result; fail_if_not_eq_uint8_arr(output, okm); } } }dino-0.5.0/plugins/omemo/tests/native/session_builder.vala0000664000000000000000000007237414776241610022455 0ustar rootrootnamespace Omemo.Test { class SessionBuilderTest : Gee.TestCase { Address alice_address; Address bob_address; public SessionBuilderTest() { base("SessionBuilder"); add_test("basic_pre_key_v2", test_basic_pre_key_v2); add_test("basic_pre_key_v3", test_basic_pre_key_v3); add_test("basic_pre_key_omemo", test_basic_pre_key_omemo); add_test("bad_signed_pre_key_signature", test_bad_signed_pre_key_signature); add_test("repeat_bundle_message_v2", test_repeat_bundle_message_v2); } private Context global_context; public override void set_up() { try { global_context = new Context(); alice_address = new Address("+14151111111", 1); bob_address = new Address("+14152222222", 1); } catch (Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } public override void tear_down() { global_context = null; alice_address = null; bob_address = null; } void test_basic_pre_key_v2() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); uint32 bob_local_registration_id = bob_store.local_registration_id; IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, bob_pre_key_pair.public, 0, null, null, bob_identity_key_pair.public); /* * Have Alice process Bob's pre key bundle, which should fail due to a * missing unsigned pre key. */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_basic_pre_key_v3() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); uint32 bob_local_registration_id = bob_store.local_registration_id; ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, bob_pre_key_pair.public, 22, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Check that we can load the session state and verify its version */ fail_if_not(alice_store.contains_session(bob_address)); SessionRecord loaded_record = alice_store.load_session(bob_address); fail_if_not_eq_int((int)loaded_record.state.session_version, 3); /* Encrypt an outgoing message to send to Bob */ string original_message = "L'homme est condamné à être libre"; SessionCipher alice_session_cipher = alice_store.create_session_cipher(bob_address); CiphertextMessage outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Convert to an incoming message for Bob */ PreKeyOmemoMessage incoming_message = global_context.deserialize_signal_pre_key_message(outgoing_message.serialized); /* Save the pre key and signed pre key in Bob's data store */ PreKeyRecord bob_pre_key_record; throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); SignedPreKeyRecord bob_signed_pre_key_record; throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 22, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature, null)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Create Bob's session cipher and decrypt the message from Alice */ SessionCipher bob_session_cipher = bob_store.create_session_cipher(alice_address); /* Prepare the data for the callback test */ //int callback_context = 1234; //bob_session_cipher.user_data = //bob_session_cipher.decryption_callback = uint8[] plaintext = bob_session_cipher.decrypt_pre_key_message(incoming_message); /* Clean up callback data */ bob_session_cipher.user_data = null; bob_session_cipher.decryption_callback = null; /* Verify Bob's session state and the decrypted message */ fail_if_not(bob_store.contains_session(alice_address)); SessionRecord alice_recipient_session_record = bob_store.load_session(alice_address); SessionState alice_recipient_session_state = alice_recipient_session_record.state; fail_if_not_eq_int((int)alice_recipient_session_state.session_version, 3); fail_if_null(alice_recipient_session_state.alice_base_key); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Have Bob send a reply to Alice */ CiphertextMessage bob_outgoing_message = bob_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(bob_outgoing_message.type, CiphertextType.SIGNAL); /* Verify that Alice can decrypt it */ OmemoMessage bob_outgoing_message_copy = global_context.copy_message(bob_outgoing_message); uint8[] alice_plaintext = alice_session_cipher.decrypt_message(bob_outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, alice_plaintext); GLib.Test.message("Pre-interaction tests complete"); /* Interaction tests */ run_interaction(alice_store, bob_store); /* Cleanup state from previous tests that we need to replace */ alice_store = null; bob_pre_key_pair = null; bob_signed_pre_key_pair = null; bob_identity_key_pair = null; bob_signed_pre_key_signature = null; bob_pre_key_record = null; bob_signed_pre_key_record = null; /* Create Alice's new session data */ alice_store = setup_test_store_context(global_context); alice_session_builder = alice_store.create_session_builder(bob_address); alice_session_cipher = alice_store.create_session_cipher(bob_address); /* Create Bob's new pre key bundle */ bob_pre_key_pair = global_context.generate_key_pair(); bob_signed_pre_key_pair = global_context.generate_key_pair(); bob_identity_key_pair = bob_store.identity_key_pair; bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31338, bob_pre_key_pair.public, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Save the new pre key and signed pre key in Bob's data store */ throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 23, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature, null)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Have Alice encrypt a message for Bob */ outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Have Bob try to decrypt the message */ PreKeyOmemoMessage outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); /* The decrypt should fail with a specific error */ fail_if_not_error_code(() => bob_session_cipher.decrypt_pre_key_message(outgoing_message_copy), ErrorCode.UNTRUSTED_IDENTITY); outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); /* Save the identity key to Bob's store */ bob_store.save_identity(alice_address, outgoing_message_copy.identity_key); /* Try the decrypt again, this time it should succeed */ outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); plaintext = bob_session_cipher.decrypt_pre_key_message(outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Create a new pre key for Bob */ ECPublicKey test_public_key = create_test_ec_public_key(global_context); IdentityKeyPair alice_identity_key_pair = alice_store.identity_key_pair; bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, test_public_key, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, alice_identity_key_pair.public); /* Have Alice process Bob's new pre key bundle, which should fail */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.UNTRUSTED_IDENTITY); GLib.Test.message("Post-interaction tests complete"); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_basic_pre_key_omemo() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); alice_session_builder.version = 4; /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); uint32 bob_local_registration_id = bob_store.local_registration_id; ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize_omemo()); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, bob_pre_key_pair.public, 22, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Check that we can load the session state and verify its version */ fail_if_not(alice_store.contains_session(bob_address)); SessionRecord loaded_record = alice_store.load_session(bob_address); fail_if_not_eq_int((int)loaded_record.state.session_version, 4); /* Encrypt an outgoing message to send to Bob */ string original_message = "L'homme est condamné à être libre"; SessionCipher alice_session_cipher = alice_store.create_session_cipher(bob_address); alice_session_cipher.version = 4; CiphertextMessage outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Convert to an incoming message for Bob */ PreKeyOmemoMessage incoming_message = global_context.deserialize_omemo_pre_key_message(outgoing_message.serialized, bob_local_registration_id); /* Save the pre key and signed pre key in Bob's data store */ PreKeyRecord bob_pre_key_record; throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); SignedPreKeyRecord bob_signed_pre_key_record; throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 22, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, new uint8[1], bob_signed_pre_key_signature)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Create Bob's session cipher and decrypt the message from Alice */ SessionCipher bob_session_cipher = bob_store.create_session_cipher(alice_address); bob_session_cipher.version = 4; /* Prepare the data for the callback test */ //int callback_context = 1234; //bob_session_cipher.user_data = //bob_session_cipher.decryption_callback = uint8[] plaintext = bob_session_cipher.decrypt_pre_key_message(incoming_message); /* Clean up callback data */ bob_session_cipher.user_data = null; bob_session_cipher.decryption_callback = null; /* Verify Bob's session state and the decrypted message */ fail_if_not(bob_store.contains_session(alice_address)); SessionRecord alice_recipient_session_record = bob_store.load_session(alice_address); SessionState alice_recipient_session_state = alice_recipient_session_record.state; fail_if_not_eq_int((int)alice_recipient_session_state.session_version, 4); fail_if_null(alice_recipient_session_state.alice_base_key); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Have Bob send a reply to Alice */ CiphertextMessage bob_outgoing_message = bob_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(bob_outgoing_message.type, CiphertextType.SIGNAL); /* Verify that Alice can decrypt it */ OmemoMessage bob_outgoing_message_copy = global_context.copy_message(bob_outgoing_message); uint8[] alice_plaintext = alice_session_cipher.decrypt_message(bob_outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, alice_plaintext); GLib.Test.message("Pre-interaction tests complete"); /* Interaction tests */ run_interaction(alice_store, bob_store, 4); /* Cleanup state from previous tests that we need to replace */ alice_store = null; bob_pre_key_pair = null; bob_signed_pre_key_pair = null; bob_identity_key_pair = null; bob_signed_pre_key_signature = null; bob_pre_key_record = null; bob_signed_pre_key_record = null; /* Create Alice's new session data */ alice_store = setup_test_store_context(global_context); alice_session_builder = alice_store.create_session_builder(bob_address); alice_session_builder.version = 4; alice_session_cipher = alice_store.create_session_cipher(bob_address); alice_session_cipher.version = 4; /* Create Bob's new pre key bundle */ bob_pre_key_pair = global_context.generate_key_pair(); bob_signed_pre_key_pair = global_context.generate_key_pair(); bob_identity_key_pair = bob_store.identity_key_pair; bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize_omemo()); bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31338, bob_pre_key_pair.public, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, bob_identity_key_pair.public); /* Save the new pre key and signed pre key in Bob's data store */ throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 23, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, new uint8[1], bob_signed_pre_key_signature)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* Have Alice process Bob's pre key bundle */ alice_session_builder.process_pre_key_bundle(bob_pre_key); /* Have Alice encrypt a message for Bob */ outgoing_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(outgoing_message.type, CiphertextType.PREKEY); /* Have Bob try to decrypt the message */ PreKeyOmemoMessage outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); /* The decrypt should fail with a specific error */ fail_if_not_error_code(() => bob_session_cipher.decrypt_pre_key_message(outgoing_message_copy), ErrorCode.UNTRUSTED_IDENTITY); outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); /* Save the identity key to Bob's store */ bob_store.save_identity(alice_address, outgoing_message_copy.identity_key); /* Try the decrypt again, this time it should succeed */ outgoing_message_copy = global_context.copy_pre_key_message(outgoing_message); plaintext = bob_session_cipher.decrypt_pre_key_message(outgoing_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); /* Create a new pre key for Bob */ ECPublicKey test_public_key = create_test_ec_public_key(global_context); IdentityKeyPair alice_identity_key_pair = alice_store.identity_key_pair; bob_pre_key = create_pre_key_bundle(bob_local_registration_id, 1, 31337, test_public_key, 23, bob_signed_pre_key_pair.public, bob_signed_pre_key_signature, alice_identity_key_pair.public); /* Have Alice process Bob's new pre key bundle, which should fail */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.UNTRUSTED_IDENTITY); GLib.Test.message("Post-interaction tests complete"); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_bad_signed_pre_key_signature() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store */ Store bob_store = setup_test_store_context(global_context); /* Create Bob's regular and signed pre key pairs */ ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); /* Create Bob's signed pre key signature */ IdentityKeyPair bob_identity_key_pair = bob_store.identity_key_pair; uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); for (int i = 0; i < bob_signed_pre_key_signature.length * 8; i++) { uint8[] modified_signature = bob_signed_pre_key_signature[0:bob_signed_pre_key_signature.length]; /* Intentionally corrupt the signature data */ modified_signature[i/8] ^= (1 << ((uint8)i % 8)); /* Create a pre key bundle */ PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31137,bob_pre_key_pair.public,22,bob_signed_pre_key_pair.public,modified_signature,bob_identity_key_pair.public); /* Process the bundle and make sure we fail with an invalid key error */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } /* Create a correct pre key bundle */ PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31137,bob_pre_key_pair.public,22,bob_signed_pre_key_pair.public,bob_signed_pre_key_signature,bob_identity_key_pair.public); /* Process the bundle and make sure we do not fail */ alice_session_builder.process_pre_key_bundle(bob_pre_key); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } void test_repeat_bundle_message_v2() { try { /* Create Alice's data store and session builder */ Store alice_store = setup_test_store_context(global_context); SessionBuilder alice_session_builder = alice_store.create_session_builder(bob_address); /* Create Bob's data store and pre key bundle */ Store bob_store = setup_test_store_context(global_context); ECKeyPair bob_pre_key_pair = global_context.generate_key_pair(); ECKeyPair bob_signed_pre_key_pair = global_context.generate_key_pair(); uint8[] bob_signed_pre_key_signature = global_context.calculate_signature(bob_store.identity_key_pair.private, bob_signed_pre_key_pair.public.serialize()); PreKeyBundle bob_pre_key = create_pre_key_bundle(bob_store.local_registration_id,1,31337,bob_pre_key_pair.public,0,null,null,bob_store.identity_key_pair.public); /* Add Bob's pre keys to Bob's data store */ PreKeyRecord bob_pre_key_record; throw_by_code(PreKeyRecord.create(out bob_pre_key_record, bob_pre_key.pre_key_id, bob_pre_key_pair)); bob_store.store_pre_key(bob_pre_key_record); SignedPreKeyRecord bob_signed_pre_key_record; throw_by_code(SignedPreKeyRecord.create(out bob_signed_pre_key_record, 22, new DateTime.now_utc().to_unix(), bob_signed_pre_key_pair, bob_signed_pre_key_signature, null)); bob_store.store_signed_pre_key(bob_signed_pre_key_record); /* * Have Alice process Bob's pre key bundle, which should fail due to a * missing signed pre key. */ fail_if_not_error_code(() => alice_session_builder.process_pre_key_bundle(bob_pre_key), ErrorCode.INVALID_KEY); } catch(Error e) { fail_if_reached(@"Unexpected error: $(e.message)"); } } class Holder { public uint8[] data { get; private set; } public Holder(uint8[] data) { this.data = data; } } void run_interaction(Store alice_store, Store bob_store, uint32 version = 3) throws Error { /* Create the session ciphers */ SessionCipher alice_session_cipher = alice_store.create_session_cipher(bob_address); alice_session_cipher.version = version; SessionCipher bob_session_cipher = bob_store.create_session_cipher(alice_address); bob_session_cipher.version = version; /* Create a test message */ string original_message = "smert ze smert"; /* Simulate Alice sending a message to Bob */ CiphertextMessage alice_message = alice_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(alice_message.type, CiphertextType.SIGNAL); OmemoMessage alice_message_copy = global_context.copy_message(alice_message); uint8[] plaintext = bob_session_cipher.decrypt_message(alice_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); GLib.Test.message("Interaction complete: Alice -> Bob"); /* Simulate Bob sending a message to Alice */ CiphertextMessage bob_message = bob_session_cipher.encrypt(original_message.data); fail_if_not_eq_int(alice_message.type, CiphertextType.SIGNAL); OmemoMessage bob_message_copy = global_context.copy_message(bob_message); plaintext = alice_session_cipher.decrypt_message(bob_message_copy); fail_if_not_eq_uint8_arr(original_message.data, plaintext); GLib.Test.message("Interaction complete: Bob -> Alice"); /* Looping Alice -> Bob */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage alice_looping_message = alice_session_cipher.encrypt(looping_message); OmemoMessage alice_looping_message_copy = global_context.copy_message(alice_looping_message); uint8[] looping_plaintext = bob_session_cipher.decrypt_message(alice_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (looping)"); /* Looping Bob -> Alice */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage bob_looping_message = bob_session_cipher.encrypt(looping_message); OmemoMessage bob_looping_message_copy = global_context.copy_message(bob_looping_message); uint8[] looping_plaintext = alice_session_cipher.decrypt_message(bob_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Bob -> Alice (looping)"); /* Generate a shuffled list of encrypted messages for later use */ Holder[] alice_ooo_plaintext = new Holder[10]; Holder[] alice_ooo_ciphertext = new Holder[10]; for (int i = 0; i < 10; i++) { alice_ooo_plaintext[i] = new Holder(create_looping_message(i)); alice_ooo_ciphertext[i] = new Holder(alice_session_cipher.encrypt(alice_ooo_plaintext[i].data).serialized); } for (int i = 0; i < 10; i++) { uint32 s = Random.next_int() % 10; Holder tmp = alice_ooo_plaintext[s]; alice_ooo_plaintext[s] = alice_ooo_plaintext[i]; alice_ooo_plaintext[i] = tmp; tmp = alice_ooo_ciphertext[s]; alice_ooo_ciphertext[s] = alice_ooo_ciphertext[i]; alice_ooo_ciphertext[i] = tmp; } GLib.Test.message("Shuffled Alice->Bob messages created"); /* Looping Alice -> Bob (repeated) */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage alice_looping_message = alice_session_cipher.encrypt(looping_message); OmemoMessage alice_looping_message_copy = global_context.copy_message(alice_looping_message); uint8[] looping_plaintext = bob_session_cipher.decrypt_message(alice_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (looping, repeated)"); /* Looping Bob -> Alice (repeated) */ for (int i = 0; i < 10; i++) { uint8[] looping_message = create_looping_message(i); CiphertextMessage bob_looping_message = bob_session_cipher.encrypt(looping_message); OmemoMessage bob_looping_message_copy = global_context.copy_message(bob_looping_message); uint8[] looping_plaintext = alice_session_cipher.decrypt_message(bob_looping_message_copy); fail_if_not_eq_uint8_arr(looping_message, looping_plaintext); } GLib.Test.message("Interaction complete: Bob -> Alice (looping, repeated)"); /* Shuffled Alice -> Bob */ for (int i = 0; i < 10; i++) { OmemoMessage ooo_message_deserialized = version >= 4 ? global_context.deserialize_omemo_message(alice_ooo_ciphertext[i].data) : global_context.deserialize_signal_message(alice_ooo_ciphertext[i].data); uint8[] ooo_plaintext = bob_session_cipher.decrypt_message(ooo_message_deserialized); fail_if_not_eq_uint8_arr(alice_ooo_plaintext[i].data, ooo_plaintext); } GLib.Test.message("Interaction complete: Alice -> Bob (shuffled)"); } uint8[] create_looping_message(int index) { return (@"You can only desire based on what you know: $index").data; } /* uint8[] create_looping_message_short(int index) { return ("What do we mean by saying that existence precedes essence? " + "We mean that man first of all exists, encounters himself, " + @"surges up in the world--and defines himself aftward. $index").data; } */ } } dino-0.5.0/plugins/omemo/tests/native/testcase.vala0000664000000000000000000000451314776241610021065 0ustar rootroot/* testcase.vala * * Copyright (C) 2009 Julien Peeters * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Julien Peeters */ public abstract class Gee.TestCase : Object { private GLib.TestSuite suite; private Adaptor[] adaptors = new Adaptor[0]; public delegate void TestMethod (); protected TestCase (string name) { this.suite = new GLib.TestSuite (name); } public void add_test (string name, owned TestMethod test) { var adaptor = new Adaptor (name, (owned)test, this); this.adaptors += adaptor; this.suite.add (new GLib.TestCase (adaptor.name, adaptor.set_up, adaptor.run, adaptor.tear_down )); } public virtual void set_up () { } public virtual void tear_down () { } public GLib.TestSuite get_suite () { return (owned) this.suite; } private class Adaptor { [CCode (notify = false)] public string name { get; private set; } private TestMethod test; private TestCase test_case; public Adaptor (string name, owned TestMethod test, TestCase test_case) { this.name = name; this.test = (owned)test; this.test_case = test_case; } public void set_up (void* fixture) { this.test_case.set_up (); } public void run (void* fixture) { this.test (); } public void tear_down (void* fixture) { this.test_case.tear_down (); } } } dino-0.5.0/plugins/omemo/vapi/0000775000000000000000000000000014776241610014711 5ustar rootrootdino-0.5.0/plugins/omemo/vapi/libgcrypt.vapi0000664000000000000000000000000014776241610017557 0ustar rootrootdino-0.5.0/plugins/omemo/vapi/libomemo-c.vapi0000664000000000000000000010745414776241610017630 0ustar rootrootnamespace Omemo { [CCode (cname = "int", cprefix = "SG_ERR_", cheader_filename = "omemo/signal_protocol.h", has_type_id = false)] public enum ErrorCode { [CCode (cname = "SG_SUCCESS")] SUCCESS, NOMEM, INVAL, UNKNOWN, DUPLICATE_MESSAGE, INVALID_KEY, INVALID_KEY_ID, INVALID_MAC, INVALID_MESSAGE, INVALID_VERSION, LEGACY_MESSAGE, NO_SESSION, STALE_KEY_EXCHANGE, UNTRUSTED_IDENTITY, VRF_SIG_VERIF_FAILED, INVALID_PROTO_BUF, FP_VERSION_MISMATCH, FP_IDENT_MISMATCH; } [CCode (cname = "SG_ERR_MINIMUM", cheader_filename = "omemo/signal_protocol.h")] public const int MIN_ERROR_CODE; [CCode (cname = "int", cprefix = "SG_LOG_", cheader_filename = "omemo/signal_protocol.h", has_type_id = false)] public enum LogLevel { ERROR, WARNING, NOTICE, INFO, DEBUG } [CCode (cname = "signal_throw_gerror_by_code_", cheader_filename = "omemo/signal_protocol.h")] private int throw_by_code(int code, string? message = null) throws GLib.Error { if (code < 0 && code > MIN_ERROR_CODE) { throw new GLib.Error(-1, code, "%s: %s", message ?? "libomemo-c error", ((ErrorCode)code).to_string()); } return code; } [CCode (cname = "int", cprefix = "SG_CIPHER_", cheader_filename = "omemo/signal_protocol.h", has_type_id = false)] public enum Cipher { AES_CTR_NOPADDING, AES_CBC_PKCS5, AES_GCM_NOPADDING } [Compact] [CCode (cname = "signal_type_base", ref_function="signal_type_ref_vapi", unref_function="signal_type_unref_vapi", cheader_filename="omemo/signal_protocol_types.h,native/helper.h")] public class TypeBase { } [Compact] [CCode (cname = "signal_buffer", cprefix = "signal_buffer_", cheader_filename = "omemo/signal_protocol_types.h", free_function="signal_buffer_free")] public class Buffer { [CCode (cname = "signal_buffer_alloc")] public Buffer(size_t len); [CCode (cname = "signal_buffer_create")] public Buffer.from(uint8[] data); public Buffer copy(); public Buffer append(uint8[] data); public int compare(Buffer other); public uint8 get(int i) { return data[i]; } public void set(int i, uint8 val) { data[i] = val; } public uint8[] data { get { int x = (int)len(); unowned uint8[] res = _data(); res.length = x; return res; } } [CCode (array_length = false, cname = "signal_buffer_data")] private unowned uint8[] _data(); private size_t len(); } [Compact] [CCode (cname = "signal_int_list", cheader_filename = "omemo/signal_protocol_types.h", free_function="signal_int_list_free")] public class IntList { [CCode (cname = "signal_int_list_alloc")] public IntList(); [CCode (cname = "signal_int_list_push_back")] public int add(int value); public uint size { [CCode (cname = "signal_int_list_size")] get; } [CCode (cname = "signal_int_list_at")] public int get(uint index); } [Compact] [CCode (cname = "session_builder", cprefix = "session_builder_", free_function="session_builder_free", cheader_filename = "omemo/session_builder.h")] public class SessionBuilder { [CCode (cname = "session_builder_process_pre_key_bundle")] private int process_pre_key_bundle_(PreKeyBundle pre_key_bundle); [CCode (cname = "session_builder_process_pre_key_bundle_")] public void process_pre_key_bundle(PreKeyBundle pre_key_bundle) throws GLib.Error { throw_by_code(process_pre_key_bundle_(pre_key_bundle)); } public int version { get; set; } } [Compact] [CCode (cname = "session_pre_key_bundle", cprefix = "session_pre_key_bundle_", cheader_filename = "omemo/session_pre_key.h")] public class PreKeyBundle : TypeBase { public static int create(out PreKeyBundle bundle, uint32 registration_id, int device_id, uint32 pre_key_id, ECPublicKey? pre_key_public, uint32 signed_pre_key_id, ECPublicKey? signed_pre_key_public, uint8[]? signed_pre_key_signature, ECPublicKey? identity_key); public uint32 registration_id { get; } public int device_id { get; } public uint32 pre_key_id { get; } public ECPublicKey pre_key { owned get; } public uint32 signed_pre_key_id { get; } public ECPublicKey signed_pre_key { owned get; } public Buffer signed_pre_key_signature { owned get; } public ECPublicKey identity_key { owned get; } } [Compact] [CCode (cname = "session_pre_key", cprefix = "session_pre_key_", cheader_filename = "omemo/session_pre_key.h,native/helper.h")] public class PreKeyRecord : TypeBase { public static int create(out PreKeyRecord pre_key, uint32 id, ECKeyPair key_pair); //public static int deserialize(out PreKeyRecord pre_key, uint8[] data, NativeContext global_context); [CCode (instance_pos = 2)] public int serialze(out Buffer buffer); public uint32 id { get; } public ECKeyPair key_pair { get; } } [Compact] [CCode (cname = "session_record", cprefix = "session_record_", cheader_filename = "omemo/signal_protocol_types.h")] public class SessionRecord : TypeBase { public SessionState state { get; } public Buffer user_record { get; } } [Compact] [CCode (cname = "session_state", cprefix = "session_state_", cheader_filename = "omemo/session_state.h")] public class SessionState : TypeBase { //public static int create(out SessionState state, NativeContext context); //public static int deserialize(out SessionState state, uint8[] data, NativeContext context); //public static int copy(out SessionState state, SessionState other_state, NativeContext context); [CCode (instance_pos = 2)] public int serialze(out Buffer buffer); public uint32 session_version { get; set; } public ECPublicKey local_identity_key { get; set; } public ECPublicKey remote_identity_key { get; set; } //public Ratchet.RootKey root_key { get; set; } public uint32 previous_counter { get; set; } public ECPublicKey sender_ratchet_key { get; } public ECKeyPair sender_ratchet_key_pair { get; } //public Ratchet.ChainKey sender_chain_key { get; set; } public uint32 remote_registration_id { get; set; } public uint32 local_registration_id { get; set; } public int needs_refresh { get; set; } public ECPublicKey alice_base_key { get; set; } } [Compact] [CCode (cname = "session_signed_pre_key", cprefix = "session_signed_pre_key_", cheader_filename = "omemo/session_pre_key.h")] public class SignedPreKeyRecord : TypeBase { public static int create(out SignedPreKeyRecord pre_key, uint32 id, uint64 timestamp, ECKeyPair key_pair, uint8[] signature, uint8[]? signature_omemo); [CCode (instance_pos = 2)] public int serialize(out Buffer buffer); public uint32 id { get; } public uint64 timestamp { get; } public ECKeyPair key_pair { get; } public uint8[] signature { [CCode (cname = "session_signed_pre_key_get_signature_")] get { int x = (int)get_signature_len(); unowned uint8[] res = get_signature(); res.length = x; return res; } } public uint8[] signature_omemo { [CCode (cname = "session_signed_pre_key_get_signature_omemo_")] get { int x = (int)get_signature_omemo_len(); unowned uint8[] res = get_signature_omemo(); res.length = x; return res; } } [CCode (array_length = false, cname = "session_signed_pre_key_get_signature")] private unowned uint8[] get_signature(); private size_t get_signature_len(); [CCode (array_length = false, cname = "session_signed_pre_key_get_signature_omemo")] private unowned uint8[] get_signature_omemo(); private size_t get_signature_omemo_len(); } /** * Address of an OMEMO message recipient */ [Compact] [CCode (cname = "signal_protocol_address", cprefix = "signal_protocol_address_", cheader_filename = "omemo/signal_protocol.h,native/helper.h")] public class Address { public Address(string name, int32 device_id); public int32 device_id { get; set; } public string name { owned get; set; } } /** * A representation of a (group + sender + device) tuple */ [Compact] [CCode (cname = "signal_protocol_sender_key_name")] public class SenderKeyName { [CCode (cname = "group_id", array_length_cname="group_id_len")] private char* group_id_; private size_t group_id_len; public Address sender; } [Compact] [CCode (cname = "ec_public_key", cprefix = "ec_public_key_", cheader_filename = "omemo/curve.h,native/helper.h")] public class ECPublicKey : TypeBase { [CCode (cname = "curve_generate_public_key")] public static int generate(out ECPublicKey public_key, ECPrivateKey private_key); [CCode (instance_pos = 1, cname = "ec_public_key_serialize")] private int serialize_([CCode (pos = 0)] out Buffer buffer); [CCode (cname = "ec_public_key_serialize_")] public uint8[] serialize() { Buffer buffer; int code = serialize_(out buffer); if (code < 0 && code > MIN_ERROR_CODE) { // Can only throw for invalid arguments or out of memory. GLib.assert_not_reached(); } return buffer.data; } [CCode (instance_pos = 1, cname = "ec_public_key_serialize_omemo")] private int serialize_omemo_([CCode (pos = 0)] out Buffer buffer); [CCode (cname = "ec_public_key_serialize_omemo_")] public uint8[] serialize_omemo() { Buffer buffer; int code = serialize_omemo_(out buffer); if (code < 0 && code > MIN_ERROR_CODE) { // Can only throw for invalid arguments or out of memory. GLib.assert_not_reached(); } return buffer.data; } public Buffer ed { owned get; } public Buffer mont { owned get; } public int compare(ECPublicKey other); public int memcmp(ECPublicKey other); } [Compact] [CCode (cname = "ec_private_key", cprefix = "ec_private_key_", cheader_filename = "omemo/curve.h,native/helper.h")] public class ECPrivateKey : TypeBase { [CCode (instance_pos = 1, cname = "ec_private_key_serialize")] private int serialize_([CCode (pos = 0)] out Buffer buffer); [CCode (cname = "ec_private_key_serialize_")] public uint8[] serialize() throws GLib.Error { Buffer buffer; int code = serialize_(out buffer); if (code < 0 && code > MIN_ERROR_CODE) { // Can only throw for invalid arguments or out of memory. GLib.assert_not_reached(); } return buffer.data; } public int compare(ECPublicKey other); } [Compact] [CCode (cname = "ec_key_pair", cprefix="ec_key_pair_", cheader_filename = "omemo/curve.h,native/helper.h")] public class ECKeyPair : TypeBase { public static int create(out ECKeyPair key_pair, ECPublicKey public_key, ECPrivateKey private_key); public ECPublicKey public { get; } public ECPrivateKey private { get; } } [CCode (cname = "ratchet_message_keys", cheader_filename = "omemo/ratchet.h")] public class MessageKeys { } [Compact] [CCode (cname = "ratchet_identity_key_pair", cprefix = "ratchet_identity_key_pair_", cheader_filename = "omemo/ratchet.h,native/helper.h")] public class IdentityKeyPair : TypeBase { public static int create(out IdentityKeyPair key_pair, ECPublicKey public_key, ECPrivateKey private_key); public int serialze(out Buffer buffer); public ECPublicKey public { get; } public ECPrivateKey private { get; } } [Compact] [CCode (cname = "ec_public_key_list")] public class PublicKeyList {} /** * The main entry point for OMEMO encrypt/decrypt operations. * * Once a session has been established with session_builder, * this class can be used for all encrypt/decrypt operations within * that session. */ [Compact] [CCode (cname = "session_cipher", cprefix = "session_cipher_", cheader_filename = "omemo/session_cipher.h", free_function = "session_cipher_free")] public class SessionCipher { public void* user_data { get; set; } public DecryptionCallback decryption_callback { set; } [CCode (cname = "session_cipher_encrypt")] private int encrypt_(uint8[] padded_message, out CiphertextMessage encrypted_message); [CCode (cname = "session_cipher_encrypt_")] public CiphertextMessage encrypt(uint8[] padded_message) throws GLib.Error { CiphertextMessage res; throw_by_code(encrypt_(padded_message, out res)); return res; } [CCode (cname = "session_cipher_decrypt_pre_key_signal_message")] private int decrypt_pre_key_message_(PreKeyOmemoMessage ciphertext, void* decrypt_context, out Buffer plaintext); [CCode (cname = "session_cipher_decrypt_pre_key_signal_message_")] public uint8[] decrypt_pre_key_message(PreKeyOmemoMessage ciphertext, void* decrypt_context = null) throws GLib.Error { Buffer res; throw_by_code(decrypt_pre_key_message_(ciphertext, decrypt_context, out res)); return res.data; } [CCode (cname = "session_cipher_decrypt_signal_message")] private int decrypt_message_(OmemoMessage ciphertext, void* decrypt_context, out Buffer plaintext); [CCode (cname = "session_cipher_decrypt_signal_message_")] public uint8[] decrypt_message(OmemoMessage ciphertext, void* decrypt_context = null) throws GLib.Error { Buffer res; throw_by_code(decrypt_message_(ciphertext, decrypt_context, out res)); return res.data; } public int get_remote_registration_id(out uint32 remote_id); [CCode (cname = "session_cipher_get_session_version")] private int get_session_version_(out uint32 version); [CCode (cname = "session_cipher_get_session_version_")] public uint32 get_session_version() throws GLib.Error { uint32 res; throw_by_code(get_session_version_(out res)); return res; } public uint32 version { get; set; } [CCode (has_target = false)] public delegate int DecryptionCallback(SessionCipher cipher, Buffer plaintext, void* decrypt_context); } [CCode (cname = "int", cheader_filename = "omemo/protocol.h", has_type_id = false)] public enum CiphertextType { [CCode (cname = "CIPHERTEXT_SIGNAL_TYPE")] SIGNAL, [CCode (cname = "CIPHERTEXT_PREKEY_TYPE")] PREKEY, [CCode (cname = "CIPHERTEXT_SENDERKEY_TYPE")] SENDERKEY, [CCode (cname = "CIPHERTEXT_SENDERKEY_DISTRIBUTION_TYPE")] SENDERKEY_DISTRIBUTION } [Compact] [CCode (cname = "ciphertext_message", cprefix = "ciphertext_message_", cheader_filename = "omemo/protocol.h,native/helper.h")] public abstract class CiphertextMessage : TypeBase { public CiphertextType type { get; } [CCode (cname = "ciphertext_message_get_serialized")] private unowned Buffer get_serialized_(); public uint8[] serialized { [CCode (cname = "ciphertext_message_get_serialized_")] get { return get_serialized_().data; }} } [Compact] [CCode (cname = "signal_message", cprefix = "signal_message_", cheader_filename = "omemo/protocol.h,native/helper.h")] public class OmemoMessage : CiphertextMessage { public ECPublicKey sender_ratchet_key { get; } public uint8 message_version { get; } public uint32 counter { get; } public Buffer body { get; } //public int verify_mac(uint8 message_version, ECPublicKey sender_identity_key, ECPublicKey receiver_identity_key, uint8[] mac, NativeContext global_context); public static int is_legacy(uint8[] data); } [Compact] [CCode (cname = "pre_key_signal_message", cprefix = "pre_key_signal_message_", cheader_filename = "omemo/protocol.h,native/helper.h")] public class PreKeyOmemoMessage : CiphertextMessage { public uint8 message_version { get; } public ECPublicKey identity_key { get; } public uint32 registration_id { get; } public uint32 pre_key_id { get; } public uint32 signed_pre_key_id { get; } public ECPublicKey base_key { get; } public OmemoMessage message { get; } } [Compact] [CCode (cname = "sender_key_message", cprefix = "sender_key_message_", cheader_filename = "omemo/protocol.h,native/helper.h")] public class SenderKeyMessage : CiphertextMessage { public uint32 key_id { get; } public uint32 iteration { get; } public Buffer ciphertext { get; } } [Compact] [CCode (cname = "sender_key_distribution_message", cprefix = "sender_key_distribution_message_", cheader_filename = "omemo/protocol.h,native/helper.h")] public class SenderKeyDistributionMessage : CiphertextMessage { public uint32 id { get; } public uint32 iteration { get; } public Buffer chain_key { get; } public ECPublicKey signature_key { get; } } [CCode (cname = "signal_vala_encrypt", cheader_filename = "native/helper.h")] private static int aes_encrypt_(out Buffer output, int cipher, uint8[] key, uint8[] iv, uint8[] plaintext, void *user_data); [CCode (cname = "signal_vala_encrypt_")] public uint8[] aes_encrypt(int cipher, uint8[] key, uint8[] iv, uint8[] plaintext) throws GLib.Error { Buffer buf; throw_by_code(aes_encrypt_(out buf, cipher, key, iv, plaintext, null)); return buf.data; } [CCode (cname = "signal_vala_decrypt", cheader_filename = "native/helper.h")] private static int aes_decrypt_(out Buffer output, int cipher, uint8[] key, uint8[] iv, uint8[] ciphertext, void *user_data); [CCode (cname = "signal_vala_decrypt_")] public uint8[] aes_decrypt(int cipher, uint8[] key, uint8[] iv, uint8[] ciphertext) throws GLib.Error { Buffer buf; throw_by_code(aes_decrypt_(out buf, cipher, key, iv, ciphertext, null)); return buf.data; } [Compact] [CCode (cname = "signal_context", cprefix="signal_context_", free_function="signal_context_destroy", cheader_filename = "omemo/signal_protocol.h")] public class NativeContext { public static int create(out NativeContext context, void* user_data); public int set_crypto_provider(NativeCryptoProvider crypto_provider); public int set_locking_functions(LockingFunc lock, LockingFunc unlock); public int set_log_function(LogFunc log); } [CCode (has_target = false)] public delegate void LockingFunc(void* user_data); [CCode (has_target = false)] public delegate void LogFunc(LogLevel level, string message, size_t len, void* user_data); [Compact] [CCode (cname = "signal_crypto_provider", cheader_filename = "omemo/signal_protocol.h")] public struct NativeCryptoProvider { public RandomFunc random_func; public HmacSha256Init hmac_sha256_init_func; public HmacSha256Update hmac_sha256_update_func; public HmacSha256Final hmac_sha256_final_func; public HmacSha256Cleanup hmac_sha256_cleanup_func; public Sha512DigestInit sha512_digest_init_func; public Sha512DigestUpdate sha512_digest_update_func; public Sha512DigestFinal sha512_digest_final_func; public Sha512DigestCleanup sha512_digest_cleanup_func; public CryptFunc encrypt_func; public CryptFunc decrypt_func; public void* user_data; } [CCode (has_target = false)] public delegate int RandomFunc(uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Init(out void* hmac_context, uint8[] key, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Update(void* hmac_context, uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Final(void* hmac_context, out Buffer buffer, void* user_data); [CCode (has_target = false)] public delegate int HmacSha256Cleanup(void* hmac_context, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestInit(out void* digest_context, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestUpdate(void* digest_context, uint8[] data, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestFinal(void* digest_context, out Buffer buffer, void* user_data); [CCode (has_target = false)] public delegate int Sha512DigestCleanup(void* digest_context, void* user_data); [CCode (has_target = false)] public delegate int CryptFunc(out Buffer output, Cipher cipher, uint8[] key, uint8[] iv, uint8[] content, void* user_data); [Compact] [CCode (cname = "signal_protocol_session_store", cheader_filename = "omemo/signal_protocol.h")] public struct NativeSessionStore { public LoadSessionFunc load_session_func; public GetSubDeviceSessionsFunc get_sub_device_sessions_func; public StoreSessionFunc store_session_func; public ContainsSessionFunc contains_session_func; public DeleteSessionFunc delete_session_func; public DeleteAllSessionsFunc delete_all_sessions_func; public DestroyFunc destroy_func; public void* user_data; } [CCode (has_target = false)] public delegate int LoadSessionFunc(out Buffer record, out Buffer user_record, Address address, void* user_data); [CCode (has_target = false)] public delegate int GetSubDeviceSessionsFunc(out IntList sessions, [CCode (array_length_type = "size_t")] char[] name, void* user_data); [CCode (has_target = false)] public delegate int StoreSessionFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] record, [CCode (array_length_type = "size_t")] uint8[] user_record, void* user_data); [CCode (has_target = false)] public delegate int ContainsSessionFunc(Address address, void* user_data); [CCode (has_target = false)] public delegate int DeleteSessionFunc(Address address, void* user_data); [CCode (has_target = false)] public delegate int DeleteAllSessionsFunc([CCode (array_length_type = "size_t")] char[] name, void* user_data); [Compact] [CCode (cname = "signal_protocol_identity_key_store", cheader_filename = "omemo/signal_protocol.h")] public struct NativeIdentityKeyStore { GetIdentityKeyPairFunc get_identity_key_pair; GetLocalRegistrationIdFunc get_local_registration_id; SaveIdentityFunc save_identity; IsTrustedIdentityFunc is_trusted_identity; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int GetIdentityKeyPairFunc(out Buffer public_data, out Buffer private_data, void* user_data); [CCode (has_target = false)] public delegate int GetLocalRegistrationIdFunc(void* user_data, out uint32 registration_id); [CCode (has_target = false)] public delegate int SaveIdentityFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] key, void* user_data); [CCode (has_target = false)] public delegate int IsTrustedIdentityFunc(Address address, [CCode (array_length_type = "size_t")] uint8[] key, void* user_data); [Compact] [CCode (cname = "signal_protocol_pre_key_store", cheader_filename = "omemo/signal_protocol.h")] public struct NativePreKeyStore { LoadPreKeyFunc load_pre_key; StorePreKeyFunc store_pre_key; ContainsPreKeyFunc contains_pre_key; RemovePreKeyFunc remove_pre_key; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int LoadPreKeyFunc(out Buffer record, uint32 pre_key_id, void* user_data); [CCode (has_target = false)] public delegate int StorePreKeyFunc(uint32 pre_key_id, [CCode (array_length_type = "size_t")] uint8[] record, void* user_data); [CCode (has_target = false)] public delegate int ContainsPreKeyFunc(uint32 pre_key_id, void* user_data); [CCode (has_target = false)] public delegate int RemovePreKeyFunc(uint32 pre_key_id, void* user_data); [Compact] [CCode (cname = "signal_protocol_signed_pre_key_store", cheader_filename = "omemo/signal_protocol.h")] public struct NativeSignedPreKeyStore { LoadPreKeyFunc load_signed_pre_key; StorePreKeyFunc store_signed_pre_key; ContainsPreKeyFunc contains_signed_pre_key; RemovePreKeyFunc remove_signed_pre_key; DestroyFunc destroy_func; void* user_data; } [Compact] [CCode (cname = "signal_protocol_sender_key_store")] public struct NativeSenderKeyStore { StoreSenderKeyFunc store_sender_key; LoadSenderKeyFunc load_sender_key; DestroyFunc destroy_func; void* user_data; } [CCode (has_target = false)] public delegate int StoreSenderKeyFunc(SenderKeyName sender_key_name, [CCode (array_length_type = "size_t")] uint8[] record, [CCode (array_length_type = "size_t")] uint8[] user_record, void* user_data); [CCode (has_target = false)] public delegate int LoadSenderKeyFunc(out Buffer record, out Buffer user_record, SenderKeyName sender_key_name, void* user_data); [CCode (has_target = false)] public delegate void DestroyFunc(void* user_data); [Compact] [CCode (cname = "signal_protocol_store_context", cprefix = "signal_protocol_store_context_", free_function="signal_protocol_store_context_destroy", cheader_filename = "omemo/signal_protocol.h")] public class NativeStoreContext { public static int create(out NativeStoreContext context, NativeContext global_context); public int set_session_store(NativeSessionStore store); public int set_pre_key_store(NativePreKeyStore store); public int set_signed_pre_key_store(NativeSignedPreKeyStore store); public int set_identity_key_store(NativeIdentityKeyStore store); public int set_sender_key_store(NativeSenderKeyStore store); } [CCode (cheader_filename = "omemo/signal_protocol.h")] namespace Protocol { /** * Interface to the pre-key store. * These functions will use the callbacks in the provided * signal_protocol_store_context instance and operate in terms of higher level * library data structures. */ [CCode (lower_case_cprefix = "signal_protocol_pre_key_")] namespace PreKey { public int load_key(NativeStoreContext context, out PreKeyRecord pre_key, uint32 pre_key_id); public int store_key(NativeStoreContext context, PreKeyRecord pre_key); public int contains_key(NativeStoreContext context, uint32 pre_key_id); public int remove_key(NativeStoreContext context, uint32 pre_key_id); } [CCode (lower_case_cprefix = "signal_protocol_signed_pre_key_")] namespace SignedPreKey { public int load_key(NativeStoreContext context, out SignedPreKeyRecord pre_key, uint32 pre_key_id); public int store_key(NativeStoreContext context, SignedPreKeyRecord pre_key); public int contains_key(NativeStoreContext context, uint32 pre_key_id); public int remove_key(NativeStoreContext context, uint32 pre_key_id); } /** * Interface to the session store. * These functions will use the callbacks in the provided * signal_protocol_store_context instance and operate in terms of higher level * library data structures. */ [CCode (lower_case_cprefix = "signal_protocol_session_")] namespace Session { public int load_session(NativeStoreContext context, out SessionRecord record, Address address, int32 version = 3); public int get_sub_device_sessions(NativeStoreContext context, out IntList sessions, char[] name); public int store_session(NativeStoreContext context, Address address, SessionRecord record); public int contains_session(NativeStoreContext context, Address address); public int delete_session(NativeStoreContext context, Address address); public int delete_all_sessions(NativeStoreContext context, char[] name); } [CCode (lower_case_cprefix = "signal_protocol_identity_")] namespace Identity { public int get_key_pair(NativeStoreContext store_context, out IdentityKeyPair key_pair); public int get_local_registration_id(NativeStoreContext store_context, out uint32 registration_id); public int save_identity(NativeStoreContext store_context, Address address, ECPublicKey identity_key); public int is_trusted_identity(NativeStoreContext store_context, Address address, ECPublicKey identity_key); } [CCode (cheader_filename = "omemo/key_helper.h", lower_case_cprefix = "signal_protocol_key_helper_")] namespace KeyHelper { [Compact] [CCode (cname = "signal_protocol_key_helper_pre_key_list_node", cprefix = "signal_protocol_key_helper_key_list_", free_function="signal_protocol_key_helper_key_list_free")] public class PreKeyListNode { public PreKeyRecord element(); public PreKeyListNode next(); } public int generate_identity_key_pair(out IdentityKeyPair key_pair, NativeContext global_context); public int generate_registration_id(out int32 registration_id, int extended_range, NativeContext global_context); public int get_random_sequence(out int value, int max, NativeContext global_context); public int generate_pre_keys(out PreKeyListNode head, uint start, uint count, NativeContext global_context); public int generate_last_resort_pre_key(out PreKeyRecord pre_key, NativeContext global_context); public int generate_signed_pre_key(out SignedPreKeyRecord signed_pre_key, IdentityKeyPair identity_key_pair, uint32 signed_pre_key_id, uint64 timestamp, NativeContext global_context); public int upgrade_signed_pre_key(ref SignedPreKeyRecord signed_pre_key, IdentityKeyPair identity_key_pair, NativeContext global_context); public int generate_sender_signing_key(out ECKeyPair key_pair, NativeContext global_context); public int generate_sender_key(out Buffer key_buffer, NativeContext global_context); public int generate_sender_key_id(out int32 key_id, NativeContext global_context); } } [CCode (cheader_filename = "omemo/curve.h")] namespace Curve { [CCode (cname = "curve_calculate_agreement")] public int calculate_agreement([CCode (array_length = false)] out uint8[] shared_key_data, ECPublicKey public_key, ECPrivateKey private_key); [CCode (cname = "curve_calculate_signature")] public int calculate_signature(NativeContext context, out Buffer signature, ECPrivateKey signing_key, uint8[] message); [CCode (cname = "curve_verify_signature")] public int verify_signature(ECPublicKey signing_key, uint8[] message, uint8[] signature); } [CCode (cname = "session_builder_create", cheader_filename = "omemo/session_builder.h")] public static int session_builder_create(out SessionBuilder builder, NativeStoreContext store, Address remote_address, NativeContext global_context); [CCode (cname = "session_cipher_create", cheader_filename = "omemo/session_cipher.h")] public static int session_cipher_create(out SessionCipher cipher, NativeStoreContext store, Address remote_address, NativeContext global_context); [CCode (cname = "pre_key_signal_message_deserialize", cheader_filename = "omemo/protocol.h")] public static int pre_key_message_deserialize(out PreKeyOmemoMessage message, uint8[] data, NativeContext global_context); [CCode (cname = "pre_key_signal_message_deserialize_omemo", cheader_filename = "omemo/protocol.h")] public static int pre_key_message_deserialize_omemo(out PreKeyOmemoMessage message, uint8[] data, uint32 remote_registration_id, NativeContext global_context); [CCode (cname = "pre_key_signal_message_copy", cheader_filename = "omemo/protocol.h")] public static int pre_key_message_copy(out PreKeyOmemoMessage message, PreKeyOmemoMessage other_message, NativeContext global_context); [CCode (cname = "signal_message_create", cheader_filename = "omemo/protocol.h")] public static int message_create(out OmemoMessage message, uint8 message_version, uint8[] mac_key, ECPublicKey sender_ratchet_key, uint32 counter, uint32 previous_counter, uint8[] ciphertext, ECPublicKey sender_identity_key, ECPublicKey receiver_identity_key, NativeContext global_context); [CCode (cname = "signal_message_deserialize", cheader_filename = "omemo/protocol.h")] public static int message_deserialize(out OmemoMessage message, uint8[] data, NativeContext global_context); [CCode (cname = "signal_message_deserialize_omemo", cheader_filename = "omemo/protocol.h")] public static int message_deserialize_omemo(out OmemoMessage message, uint8[] data, NativeContext global_context); [CCode (cname = "signal_message_copy", cheader_filename = "omemo/protocol.h")] public static int message_copy(out OmemoMessage message, OmemoMessage other_message, NativeContext global_context); [CCode (cname = "curve_generate_key_pair", cheader_filename = "omemo/curve.h")] public static int curve_generate_key_pair(NativeContext context, out ECKeyPair key_pair); [CCode (cname = "curve_decode_private_point", cheader_filename = "omemo/curve.h")] public static int curve_decode_private_point(out ECPrivateKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_decode_point", cheader_filename = "omemo/curve.h")] public static int curve_decode_point(out ECPublicKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_decode_point_ed", cheader_filename = "omemo/curve.h")] public static int curve_decode_point_ed(out ECPublicKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_decode_point_mont", cheader_filename = "omemo/curve.h")] public static int curve_decode_point_mont(out ECPublicKey public_key, uint8[] key, NativeContext global_context); [CCode (cname = "curve_generate_private_key", cheader_filename = "omemo/curve.h")] public static int curve_generate_private_key(NativeContext context, out ECPrivateKey private_key); [CCode (cname = "ratchet_identity_key_pair_deserialize", cheader_filename = "omemo/ratchet.h")] public static int ratchet_identity_key_pair_deserialize(out IdentityKeyPair key_pair, uint8[] data, NativeContext global_context); [CCode (cname = "session_signed_pre_key_deserialize", cheader_filename = "omemo/signed_pre_key.h")] public static int session_signed_pre_key_deserialize(out SignedPreKeyRecord pre_key, uint8[] data, NativeContext global_context); [Compact] [CCode (cname = "hkdf_context", cprefix = "hkdf_", free_function = "hkdf_destroy", cheader_filename = "omemo/hkdf.h")] public class NativeHkdfContext { public static int create(out NativeHkdfContext context, int message_version, NativeContext global_context); public int compare(NativeHkdfContext other); public ssize_t derive_secrets([CCode (array_length = false)] out uint8[] output, uint8[] input_key_material, uint8[] salt, uint8[] info, size_t output_len); } [CCode (cname = "setup_signal_vala_crypto_provider", cheader_filename = "native/helper.h")] public static void setup_crypto_provider(NativeContext context); [CCode (cname = "signal_vala_randomize", cheader_filename = "native/helper.h")] public static int native_random(uint8[] data); } dino-0.5.0/plugins/omemo/vapi/libqrencode.vapi0000664000000000000000000000577414776241610020076 0ustar rootroot[CCode (cheader_filename = "qrencode.h")] namespace Qrencode { [CCode (cname = "QRecLevel", cprefix = "QR_ECLEVEL_")] public enum ECLevel { L, M, Q, H } [CCode (cname = "QRencodeMode", cprefix = "QR_MODE_")] public enum EncodeMode { NUL, NUM, AN, [CCode (cname = "QR_MODE_8")] EIGHT_BIT, KANJI, STRUCTURE, ECI, FNC1FIRST, FNC1SECOND } [CCode (cname = "QRcode", free_function = "QRcode_free", has_type_id = false)] [Compact] public class QRcode { private int version; private int width; [CCode (array_length = false)] private uint8[] data; [CCode (cname = "QRcode_encodeString")] public QRcode (string str, int version = 0, ECLevel level = ECLevel.L, EncodeMode hint = EncodeMode.EIGHT_BIT, bool casesensitive = true); public Gdk.Paintable to_paintable(int module_size) { GLib.assert(module_size > 0); var dst_width = width*module_size; var dst_data = new uint8[dst_width*dst_width*3]; expand_and_upsample(data,width,width, dst_data,dst_width,dst_width); return new Gdk.MemoryTexture(dst_width, dst_width, Gdk.MemoryFormat.R8G8B8, new GLib.Bytes.take((owned) dst_data), dst_width*3); } /** Does 2D nearest-neighbor upsampling of an array of single-byte * samples, while expanding the least significant bit of each sample * to three 0-or-255 bytes. */ private void expand_and_upsample( uint8[] src, uint src_w, uint src_h, uint8[] dst, uint dst_w, uint dst_h) { GLib.assert(dst_w % src_w == 0); GLib.assert(dst_h % src_h == 0); var scale_x = dst_w/src_w, scale_y = dst_h/src_h; /* Doing the iteration in the order of destination samples for * improved cache-friendliness (dst is 48 times larger than src in * the typical case of scaling by 4x4). * The choice of multiple nested loops over a single one is for * avoiding a ton of divisions by non-constants. */ for (uint src_y = 0; src_y < src_h; ++src_y) { for (uint repeat_y = 0; repeat_y < scale_y; ++repeat_y) { var dst_y = src_y*scale_y + repeat_y; for (uint src_x = 0; src_x < src_w; ++src_x) { uint8 value = (src[src_y*src_w + src_x] & 1)==1 ? 0:255; for (uint repeat_x = 0; repeat_x < scale_x; ++repeat_x){ var dst_x = src_x*scale_x + repeat_x; var dst_idx = dst_y*dst_w + dst_x; dst[dst_idx*3+0] = value; dst[dst_idx*3+1] = value; dst[dst_idx*3+2] = value; } } } } } } } dino-0.5.0/plugins/openpgp/0000775000000000000000000000000014776241610014306 5ustar rootrootdino-0.5.0/plugins/openpgp/data/0000775000000000000000000000000014776241610015217 5ustar rootrootdino-0.5.0/plugins/openpgp/data/gresource.xml0000664000000000000000000000021414776241610017734 0ustar rootroot dino-0.5.0/plugins/openpgp/meson.build0000664000000000000000000000314514776241610016453 0ustar rootrootsubdir('po') dependencies = [ dep_libadwaita, dep_dino, dep_gee, dep_glib, dep_gmodule, dep_gpgme, dep_gtk4, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/contact_details_provider.vala', 'src/database.vala', 'src/encryption_list_entry.vala', 'src/encryption_preferences_entry.vala', 'src/file_transfer/file_decryptor.vala', 'src/file_transfer/file_encryptor.vala', 'src/gpgme_fix.c', 'src/gpgme_helper.vala', 'src/manager.vala', 'src/plugin.vala', 'src/register_plugin.vala', 'src/stream_flag.vala', 'src/stream_module.vala', 'src/util.vala', 'vapi/gpg-error.vapi', ) sources += gnome.compile_resources( 'resources', 'data/gresource.xml', source_dir: 'data', ) c_args = [ '-DG_LOG_DOMAIN="OpenPGP"', '-DGETTEXT_PACKAGE="dino-openpgp"', '-DLOCALE_INSTALL_DIR="@0@"'.format(get_option('prefix') / get_option('localedir')), ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] if dep_libadwaita.version() == 'unknown' or dep_libadwaita.version().version_compare('>=1.4') vala_args += ['-D', 'Adw_1_4'] endif lib_openpgp = shared_library('openpgp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_openpgp = declare_dependency(link_with: lib_openpgp, include_directories: include_directories('.')) summary('End-to-end encryption using PGP (openpgp)', dep_openpgp, section: 'Plugins') dino-0.5.0/plugins/openpgp/po/0000775000000000000000000000000014776241610014724 5ustar rootrootdino-0.5.0/plugins/openpgp/po/LINGUAS0000664000000000000000000000020714776241610015750 0ustar rootrootar ca cs da de el en eo es et eu fa fi fr gl hu ia id ie is it ja ko lb lt lv nb nl oc pl pt pt_BR ro ru sq sv ta tr uk vi zh_CN zh_TW dino-0.5.0/plugins/openpgp/po/ar.po0000664000000000000000000000361114776241610015667 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-25 10:09+0000\n" "Language-Team: Arabic \n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 5.7.1-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "استعلام GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "خطأ في GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "لا مفتاح متوفر. قم بتوليد واحد !" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "مُعَطَّل" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "المفتاح ليس في سلسلة المفاتيح" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "التعمية" #~ msgid "Key publishing disabled" #~ msgstr "نشر المفاتيح معطل" #~ msgid "Select key" #~ msgstr "اختيار مفتاح" #~ msgid "Loading…" #~ msgstr "جارٍ التحميل…" dino-0.5.0/plugins/openpgp/po/ca.po0000664000000000000000000000315214776241610015650 0ustar rootroot# Catalan translation for dino-openpgp. # This file is distributed under the same license as the dino package. # Jordi Mallach , 2018. # msgid "" msgstr "" "Project-Id-Version: dino-openpgp 20180123\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2018-01-24 11:24+0100\n" "Language-Team: Catalan \n" "Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "S'està consultant al GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "S'ha produït un error al GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "No hi ha claus disponibles. Genereu una!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "La clau no està al clauer" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Xifratge" #~ msgid "Key publishing disabled" #~ msgstr "Publicació de claus inhabilitada" #~ msgid "Select key" #~ msgstr "Seleccioneu una clau" #~ msgid "Loading…" #~ msgstr "S'està carregant…" dino-0.5.0/plugins/openpgp/po/cs.po0000664000000000000000000000331614776241610015674 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-08-21 05:36+0000\n" "Language-Team: none\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Dotazování GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Chyba v GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nejsou k dispozici žádné klíče. Vygenerujte jeden!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Klíč není v klíčence" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Šifrování" #~ msgid "Key publishing disabled" #~ msgstr "Publikování klíčů zakázáno" #~ msgid "Select key" #~ msgstr "Vybrat klíč" #~ msgid "Loading…" #~ msgstr "Načítání…" dino-0.5.0/plugins/openpgp/po/da.po0000664000000000000000000000322314776241610015650 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-01-06 01:07+0000\n" "Language-Team: none\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.4-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Forespørg GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Fejl i GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Ingen nøgler til rådighed. Generer en!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Nøglen er ikke i nøglering" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Kryptering" #~ msgid "Key publishing disabled" #~ msgstr "Nøglepublisering deaktiveret" #~ msgid "Select key" #~ msgstr "Vælg nøgle" #~ msgid "Loading…" #~ msgstr "Henter…" dino-0.5.0/plugins/openpgp/po/de.po0000664000000000000000000000323314776241610015655 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: German \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "GnuPG wird abgefragt" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Schlüssel veröffentlichen" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Fehler in GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Keine Schlüssel vorhanden. Erzeuge einen!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Deaktiviert" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Schlüssel nicht im Schlüsselbund" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Fingerabdruck" #~ msgid "Encryption" #~ msgstr "Verschlüsselung" #~ msgid "Key publishing disabled" #~ msgstr "Schlüsselveröffentlichung deaktiviert" #~ msgid "Select key" #~ msgstr "Wähle einen Schlüssel" #~ msgid "Loading…" #~ msgstr "Lade…" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.5.0/plugins/openpgp/po/dino-openpgp.pot0000664000000000000000000000252414776241610020052 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" dino-0.5.0/plugins/openpgp/po/el.po0000664000000000000000000000354114776241610015667 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-02-04 09:55+0000\n" "Language-Team: none\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Αναζήτηση στο GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Σφάλμα στο GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Δεν υπάρχουν διαθέσιμα κλειδιά. Δημιουργήστε ένα!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Το κλειδί δεν είναι στο keychain" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Κρυπτογράφηση" #~ msgid "Key publishing disabled" #~ msgstr "Η δημοσίευση του κλειδιού είναι απενεργοποιημένη" #~ msgid "Select key" #~ msgstr "Επιλέξτε κλειδί" #~ msgid "Loading…" #~ msgstr "Φόρτωση…" dino-0.5.0/plugins/openpgp/po/en.po0000664000000000000000000000201114776241610015660 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "Language: en\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" dino-0.5.0/plugins/openpgp/po/eo.po0000664000000000000000000000325614776241610015675 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-18 10:09+0000\n" "Language-Team: none\n" "Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Pridemandante GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Eraro pri GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Neniu ŝlosilo estas havebla. Faru ŝlosilon!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Ŝlosilo ne en ŝlosilaro" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #, fuzzy #~ msgid "Encryption" #~ msgstr "Kodo" #~ msgid "Key publishing disabled" #~ msgstr "Eldonado de ŝlosilo estas malŝaltita" #~ msgid "Select key" #~ msgstr "Elekti ŝlosilon" #~ msgid "Loading…" #~ msgstr "Ŝargante…" dino-0.5.0/plugins/openpgp/po/es.po0000664000000000000000000000340014776241610015670 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-04 00:09+0000\n" "Language-Team: Spanish \n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Error en GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "No hay claves disponibles. ¡Genera una!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Desactivado" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "La clave no está en la cadena de claves" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Encriptación" #~ msgid "Key publishing disabled" #~ msgstr "Publicación de claves desactivada" #~ msgid "Select key" #~ msgstr "Seleccionar clave" #~ msgid "Loading…" #~ msgstr "Cargando…" dino-0.5.0/plugins/openpgp/po/et.po0000664000000000000000000000301614776241610015674 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: none\n" "Language: et\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Pärime GnuPG andmekogust" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Teata võtmest" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG viga" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Võtmeid ei leidu. Palun loo!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Pole kasutusel" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Võtit ei leidu võtmerõngas" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Sõrmejälg" #~ msgid "Encryption" #~ msgstr "Krüptimine" dino-0.5.0/plugins/openpgp/po/eu.po0000664000000000000000000000333514776241610015701 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2018-04-03 15:27+0000\n" "Language-Team: Basque \n" "Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.20-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "GnuPGri galdetzen" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG akatsa" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Ez dago gakorik eskuragarri. Sortu ezazu bat!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Gakorik ez giltzatakoan" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Enkriptazioa" #~ msgid "Key publishing disabled" #~ msgstr "Gakoa argitaratzea ezgaituta" #~ msgid "Select key" #~ msgstr "Gakoa hautatu" #~ msgid "Loading…" #~ msgstr "Kargatzen…" dino-0.5.0/plugins/openpgp/po/fa.po0000664000000000000000000000343114776241610015653 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-04-26 15:32+0000\n" "Language-Team: none\n" "Language: fa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "پرس‌و‌جوی گنوپی‌جی" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "خطا در گنوپی‌جی" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "هیچ کلیدی در دسترس نیست. یکی تولید کنید!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "کلید در دسته‌کلید نیست" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "رمزگذاری" #~ msgid "Key publishing disabled" #~ msgstr "انشار کلید غیرفعال‌شده" #~ msgid "Select key" #~ msgstr "انتخاب کلید" #~ msgid "Loading…" #~ msgstr "در حال بارگذاری…" dino-0.5.0/plugins/openpgp/po/fi.po0000664000000000000000000000325214776241610015664 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-02 11:09+0000\n" "Language-Team: none\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Kysellään GnuPG:tä" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Virhe GnuPG:ssä" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Ei avaimia saatavilla. Luo avain!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Pois käytöstä" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Avain ei ole avainnipussa" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Salaus" #~ msgid "Key publishing disabled" #~ msgstr "Avaimen julkaiseminen pois käytöstä" #~ msgid "Select key" #~ msgstr "Valitse avain" #~ msgid "Loading…" #~ msgstr "Ladataan…" dino-0.5.0/plugins/openpgp/po/fr.po0000664000000000000000000000343114776241610015674 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-27 08:09+0000\n" "Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.7.1-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Interrogation de GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Erreur dans GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Aucune clé n’est disponible. Générez-en une !" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Désactivé" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "La clé n’est pas dans le trousseau" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Chiffrement" #~ msgid "Key publishing disabled" #~ msgstr "La publication des clés est désactivée" #~ msgid "Select key" #~ msgstr "Choix d’une clé" #~ msgid "Loading…" #~ msgstr "Chargement…" dino-0.5.0/plugins/openpgp/po/gl.po0000664000000000000000000000342314776241610015670 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Galician \n" "Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Consultando a GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Publicar clave" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Fallo en GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Sen chaves dispoñibles. Crear unha!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Desactivado" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "A chave non está no anel de chaves" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Impresión dixital" #~ msgid "Encryption" #~ msgstr "Cifraxe" #~ msgid "Key publishing disabled" #~ msgstr "Desactivada a publicación de chaves" #~ msgid "Select key" #~ msgstr "Escolle chave" #~ msgid "Loading…" #~ msgstr "Cargando…" dino-0.5.0/plugins/openpgp/po/hu.po0000664000000000000000000000343714776241610015707 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Hungarian \n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "A GnuPG lekérése" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Kulcs bejelentése" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Hiba a GnuPG-ben" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nincsenek elérhető kulcsok. Állítson elő egyet!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Letiltva" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "A kulcs nincs a kulcstartóban" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Ujjlenyomat" #~ msgid "Encryption" #~ msgstr "Titkosítás" #~ msgid "Key publishing disabled" #~ msgstr "Kulcsközzététel letiltva" #~ msgid "Select key" #~ msgstr "Kulcs kiválasztása" #~ msgid "Loading…" #~ msgstr "Betöltés…" dino-0.5.0/plugins/openpgp/po/ia.po0000664000000000000000000000241414776241610015656 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Language-Team: none\n" "Language: ia\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" dino-0.5.0/plugins/openpgp/po/id.po0000664000000000000000000000323114776241610015657 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2021-01-09 11:43+0000\n" "Language-Team: none\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4.1-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Bertanya kepada GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Kesalahan pada GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Kunci tidak tersedia. Buat satu!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Kunci tidak ada di gantungan kunci" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Enkripsi" #~ msgid "Key publishing disabled" #~ msgstr "Penerbitan kunci dinonaktifkan" #~ msgid "Select key" #~ msgstr "Pilih kunci" #~ msgid "Loading…" #~ msgstr "Memuat…" dino-0.5.0/plugins/openpgp/po/ie.po0000664000000000000000000000323514776241610015664 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-02-04 16:50+0000\n" "Language-Team: none\n" "Language: ie\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 3.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Contactante GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Errore de GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Null claves disponibil. Crea ún!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Clave ne es in li porta-clave" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Ciffration" #~ msgid "Key publishing disabled" #~ msgstr "Publication de claves depermisset" #~ msgid "Select key" #~ msgstr "Selecter un clave" #~ msgid "Loading…" #~ msgstr "Cargante…" dino-0.5.0/plugins/openpgp/po/is.po0000664000000000000000000000324014776241610015676 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-10-17 00:56+0000\n" "Language-Team: none\n" "Language: is\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n % 10 != 1 || n % 100 == 11;\n" "X-Generator: Weblate 4.15-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Að spyrja GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Villa hjá GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Engir lyklar í boði. Búðu einn til!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Lykill ekki í lyklakippu" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Dulritun" #~ msgid "Key publishing disabled" #~ msgstr "Deiling lykla óvirkt" #~ msgid "Select key" #~ msgstr "Veldu lykil" #~ msgid "Loading…" #~ msgstr "Hleður…" dino-0.5.0/plugins/openpgp/po/it.po0000664000000000000000000000314414776241610015702 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2018-01-24 12:18+0000\n" "Language-Team: Italian \n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.19-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Interrogando GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Errore in GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nessuna chiave disponibile. Generane una!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "La chiave non è nel portachiavi" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Crittografia" #~ msgid "Key publishing disabled" #~ msgstr "Pubblicazione della chiave disabilitata" #~ msgid "Select key" #~ msgstr "Seleziona una chiave" #~ msgid "Loading…" #~ msgstr "Caricamento…" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.5.0/plugins/openpgp/po/ja.po0000664000000000000000000000341514776241610015661 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-15 20:09+0000\n" "Language-Team: none\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "GnuPG に問い合わせています" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG でのエラー" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "キーがありません。生成しましょう!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "無効" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "キーはキーチェーンにありません" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "暗号化" #~ msgid "Key publishing disabled" #~ msgstr "キーの発行は無効" #~ msgid "Select key" #~ msgstr "キーを選択" #~ msgid "Loading…" #~ msgstr "読み込んでいます…" dino-0.5.0/plugins/openpgp/po/ko.po0000664000000000000000000000324314776241610015677 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2022-10-17 11:49+0000\n" "Language-Team: none\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.15-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG에 에러가 있음" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "사용 가능한 키 없음. 하나 생성하세요!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "키가 키체인에 있지 않음" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "암호화" #, fuzzy #~ msgid "Key publishing disabled" #~ msgstr "키 게재 비활성화됨" #~ msgid "Select key" #~ msgstr "키 선택" #~ msgid "Loading…" #~ msgstr "로딩중…" dino-0.5.0/plugins/openpgp/po/lb.po0000664000000000000000000000316514776241610015666 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2018-01-24 12:20+0000\n" "Language-Team: Luxembourgish \n" "Language: lb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.19-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "GnuPG gëtt ofgefrot" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Feeler am GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Keng Schlëssele verfügbar. Generéier een!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Schlësselen net am Schlësselbond" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Verschlësselung" #~ msgid "Key publishing disabled" #~ msgstr "Schlëssel Verëffentlechung ausgeschalt" #~ msgid "Select key" #~ msgstr "Schlëssel auswielen" #~ msgid "Loading…" #~ msgstr "Lueden…" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.5.0/plugins/openpgp/po/lt.po0000664000000000000000000000335614776241610015712 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-03 22:09+0000\n" "Language-Team: none\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "(n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Užklausiama GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Klaida GnuPG programoje" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nėra prieinamų raktų. Sugeneruokite!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Išjungta" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Rakto nėra raktinėje" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Šifravimas" #~ msgid "Key publishing disabled" #~ msgstr "Rakto paskelbimas išjungtas" #~ msgid "Select key" #~ msgstr "Pasirinkti raktą" #~ msgid "Loading…" #~ msgstr "Įkeliama…" dino-0.5.0/plugins/openpgp/po/lv.po0000664000000000000000000000241414776241610015706 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Language-Team: none\n" "Language: lv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" dino-0.5.0/plugins/openpgp/po/meson.build0000664000000000000000000000003514776241610017064 0ustar rootrooti18n.gettext('dino-openpgp') dino-0.5.0/plugins/openpgp/po/nb.po0000664000000000000000000000343514776241610015670 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2017-11-18 00:23+0000\n" "Language-Team: Norwegian Bokmål \n" "Language: nb\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 2.18-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Sender spørring til GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Feil i GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Ingen nøkler tilgjengelige. Generer én." #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Nøkkelen finnes ikke i nøkkelknippet" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Kryptering" #~ msgid "Key publishing disabled" #~ msgstr "Nøkkelpublisering avskrudd" #~ msgid "Select key" #~ msgstr "Velg nøkkel" #~ msgid "Loading…" #~ msgstr "Laster…" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.5.0/plugins/openpgp/po/nl.po0000664000000000000000000000316014776241610015675 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-openpgp-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-02-25 04:41+0000\n" "Language-Team: Dutch \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10.2-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Bezig met aanroepen van GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Fout in GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Geen sleutels beschikbaar. Genereer een sleutel!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Uitgeschakeld" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Sleutel niet in sleutelbos" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Encryptie" #~ msgid "Key publishing disabled" #~ msgstr "Sleutelpublicatie uitgeschakeld" #~ msgid "Select key" #~ msgstr "Kies een sleutel" #~ msgid "Loading…" #~ msgstr "Bezig met laden…" #~ msgid "OpenPGP" #~ msgstr "OpenPGP" dino-0.5.0/plugins/openpgp/po/oc.po0000664000000000000000000000331714776241610015671 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: none\n" "Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Consultacion de GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Anonciar la clau" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Error dins gnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Cap de clau pas disponibla. Generatz-ne una !" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Desactivat" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "La clau es pas al trocèl" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Emprenta" #~ msgid "Encryption" #~ msgstr "Chiframent" #~ msgid "Key publishing disabled" #~ msgstr "Publicacion de clau desactivada" #~ msgid "Select key" #~ msgstr "Seleccionar una clau" #~ msgid "Loading…" #~ msgstr "Cargament…" dino-0.5.0/plugins/openpgp/po/pl.po0000664000000000000000000000344714776241610015707 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-04 00:09+0000\n" "Language-Team: Polish \n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " "|| n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Odpytuję GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Błąd w GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Brak kluczy. Wygeneruj nowy!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Wyłączono" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Klucz nie znajduje się w pęku kluczy" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Szyfrowanie" #~ msgid "Key publishing disabled" #~ msgstr "Publikowanie kluczy wyłączone" #~ msgid "Select key" #~ msgstr "Wybierz klucz" #~ msgid "Loading…" #~ msgstr "Ładowanie…" dino-0.5.0/plugins/openpgp/po/pt.po0000664000000000000000000000324114776241610015707 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-08-23 21:36+0000\n" "Language-Team: none\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2.1-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Erro no GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nenhuma chave disponível. Gere uma!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "A chave não está no chaveiro" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Criptografia" #~ msgid "Key publishing disabled" #~ msgstr "Publicação da chave desativada" #~ msgid "Select key" #~ msgstr "Selecionar chave" #~ msgid "Loading…" #~ msgstr "Carregando…" dino-0.5.0/plugins/openpgp/po/pt_BR.po0000664000000000000000000000325414776241610016276 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-09-15 20:09+0000\n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 5.8-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Consultando GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Erro no GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nenhuma chave disponível. Gere uma!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Desativado" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "A chave não está no chaveiro" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Criptografia" #~ msgid "Key publishing disabled" #~ msgstr "Publicação da chave desativada" #~ msgid "Select key" #~ msgstr "Selecionar chave" #~ msgid "Loading…" #~ msgstr "Carregando…" dino-0.5.0/plugins/openpgp/po/ro.po0000664000000000000000000000350214776241610015704 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: Romanian \n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < " "20)) ? 1 : 2;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Se interoghează GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Anunță cheie" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Eroare în GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Nici o cheie disponibilă. Generați una!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Dezactivat" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Cheia nu este în șirul de chei" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Amprentă" #~ msgid "Encryption" #~ msgstr "Criptare" #~ msgid "Key publishing disabled" #~ msgstr "Cheie publică dezactivată" #~ msgid "Select key" #~ msgstr "Selectați cheia" #~ msgid "Loading…" #~ msgstr "Se încarcă…" dino-0.5.0/plugins/openpgp/po/ru.po0000664000000000000000000000370114776241610015713 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-12-30 22:56+0000\n" "Language-Team: Russian \n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.10-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Запрос GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Ошибка в GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Нет доступных ключей. Стоило бы сгенерировать один!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Выключено" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Отсутствует ключ в связке" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Шифрование" #~ msgid "Key publishing disabled" #~ msgstr "Публикация ключа отключена" #~ msgid "Select key" #~ msgstr "Выбрать ключ" #~ msgid "Loading…" #~ msgstr "Загрузка…" dino-0.5.0/plugins/openpgp/po/sq.po0000664000000000000000000000332514776241610015712 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 11:50+0000\n" "Language-Team: none\n" "Language: sq\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Po pyetet GnuPG-ja" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Njoftojeni kyçin" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Gabim në GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "S’ka kyçe gati. Prodhoni një të tillë!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "I çaktivizuar" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Kyç jo në varg kyçesh" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Shenja gishtash" #~ msgid "Encryption" #~ msgstr "Fshehtëzim" #~ msgid "Key publishing disabled" #~ msgstr "Publikim kyçesh i çaktivizuar" #~ msgid "Select key" #~ msgstr "Përzgjidhni kyç" #~ msgid "Loading…" #~ msgstr "Po ngarkohet…" dino-0.5.0/plugins/openpgp/po/sv.po0000664000000000000000000000324014776241610015713 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-08-05 20:09+0000\n" "Language-Team: none\n" "Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Frågar GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Fel i GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Inga nycklar tillgängliga. Generera en!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Inaktiverad" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Nyckeln finns inte i nyckelknippan" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Kryptering" #~ msgid "Key publishing disabled" #~ msgstr "Nyckelpublicering inaktiverad" #~ msgid "Select key" #~ msgstr "Välj nyckel" #~ msgid "Loading…" #~ msgstr "Laddar…" dino-0.5.0/plugins/openpgp/po/ta.po0000664000000000000000000000305514776241610015673 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-12-21 08:00+0000\n" "Language-Team: none\n" "Language: ta\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.10-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Gnupg ஐ வினவுகிறது" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "முடக்கப்பட்டது" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "விசை கீச்சினில் இல்லை" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "குறியாக்கம்" dino-0.5.0/plugins/openpgp/po/tr.po0000664000000000000000000000323714776241610015716 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2024-07-02 11:09+0000\n" "Language-Team: none\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.7-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "GnuPG sorgu" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG'de Hata" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Anahtar yok. Bir tane oluşturun!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Devre dışı" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Anahtar anahtarlıkta değil" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Şifreleme" #~ msgid "Key publishing disabled" #~ msgstr "Anahtar yayınlama devre dışı" #~ msgid "Select key" #~ msgstr "Anahtar seçin" #~ msgid "Loading…" #~ msgstr "Yükleniyor…" dino-0.5.0/plugins/openpgp/po/uk.po0000664000000000000000000000364214776241610015710 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: none\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Запит до GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "Оголосити ключ" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Помилка у GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Немає доступних ключей. Сгенеріть!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "Вимкнено" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Відсутній ключ у зв’язці" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "Відбиток пальця" #~ msgid "Encryption" #~ msgstr "Шифрування" #~ msgid "Key publishing disabled" #~ msgstr "Публікація ключа відключена" #~ msgid "Select key" #~ msgstr "Вибрати ключ" #~ msgid "Loading…" #~ msgstr "Завантаження…" dino-0.5.0/plugins/openpgp/po/vi.po0000664000000000000000000000327414776241610015710 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2023-04-28 07:51+0000\n" "Language-Team: none\n" "Language: vi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.18-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "Đang truy vấn GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "Lỗi trong GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "Không có khoá khả dụng. Hãy tạo một khoá!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "Khoá không trong chuỗi khoá" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "Mã hoá" #~ msgid "Key publishing disabled" #~ msgstr "Công khai khoá đã bị vô hiệu hoá" #~ msgid "Select key" #~ msgstr "Chọn khoá" #~ msgid "Loading…" #~ msgstr "Đang tải…" dino-0.5.0/plugins/openpgp/po/zh_CN.po0000664000000000000000000000341014776241610016263 0ustar rootrootmsgid "" msgstr "" "Project-Id-Version: dino-omemo-0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2025-04-10 05:39+0000\n" "Language-Team: Chinese (Simplified) \n" "Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 5.11-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "正在查询 GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "公布密钥" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG 错误" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "没有密钥可用。生成一个吧!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "已禁用" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "密钥不在密钥链中" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "指纹" #~ msgid "Encryption" #~ msgstr "加密" #~ msgid "Key publishing disabled" #~ msgstr "已禁用密钥发布功能" #~ msgid "Select key" #~ msgstr "选择密钥" #~ msgid "Loading…" #~ msgstr "载入中…" #~ msgid "Own fingerprint" #~ msgstr "自己的指纹" #~ msgid "Will be generated on first connection" #~ msgstr "会在第一次连接时生成" #~ msgid "Database error" #~ msgstr "数据库错误" dino-0.5.0/plugins/openpgp/po/zh_TW.po0000664000000000000000000000334014776241610016317 0ustar rootroot# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-04-11 18:06+0200\n" "PO-Revision-Date: 2020-11-24 05:28+0000\n" "Language-Team: Chinese (Traditional) \n" "Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.4-dev\n" #: plugins/openpgp/src/encryption_preferences_entry.vala:18 msgid "Querying GnuPG" msgstr "正在查詢 GnuPG" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 #: plugins/openpgp/src/encryption_preferences_entry.vala:36 #: plugins/openpgp/src/encryption_preferences_entry.vala:42 msgid "Announce key" msgstr "" #: plugins/openpgp/src/encryption_preferences_entry.vala:32 msgid "Error in GnuPG" msgstr "GnuPG 錯誤" #: plugins/openpgp/src/encryption_preferences_entry.vala:36 msgid "No keys available. Generate one!" msgstr "沒有可用的金鑰。生成一個!" #: plugins/openpgp/src/encryption_preferences_entry.vala:53 msgid "Disabled" msgstr "" #: plugins/openpgp/src/contact_details_provider.vala:36 msgid "Key not in keychain" msgstr "密鑰不在密鑰串中" #: plugins/openpgp/src/contact_details_provider.vala:40 msgid "Fingerprint" msgstr "" #~ msgid "Encryption" #~ msgstr "加密" #~ msgid "Key publishing disabled" #~ msgstr "金鑰發佈已經被禁用" #~ msgid "Select key" #~ msgstr "選擇金鑰" #~ msgid "Loading…" #~ msgstr "載入中…" dino-0.5.0/plugins/openpgp/src/0000775000000000000000000000000014776241610015075 5ustar rootrootdino-0.5.0/plugins/openpgp/src/contact_details_provider.vala0000664000000000000000000000302014776241610023007 0ustar rootrootusing Gtk; using Dino.Entities; namespace Dino.Plugins.OpenPgp { public class ContactDetailsProvider : Plugins.ContactDetailsProvider, Object { public string id { get { return "pgp_info"; } } public string tab { get { return "encryption"; } } private StreamInteractor stream_interactor; public ContactDetailsProvider(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public void populate(Conversation conversation, Plugins.ContactDetails contact_details, WidgetType type) { } public Object? get_widget(Conversation conversation) { var preferences_group = new Adw.PreferencesGroup() { title="OpenPGP" }; if (conversation.type_ != Conversation.Type.CHAT) return null; string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); if (key_id == null) return null; Gee.List? keys = null; try { keys = GPGHelper.get_keylist(key_id); } catch (Error e) { } var str = ""; if (keys != null && keys.size > 0) { str = markup_id(keys[0].fpr, true); } else { str = _("Key not in keychain") + "\n" + markup_id(key_id, false); } var view = new Adw.ActionRow() { title = _("Fingerprint"), subtitle = str, #if Adw_1_3 subtitle_selectable = true, #endif }; preferences_group.add(view); return preferences_group; } } } dino-0.5.0/plugins/openpgp/src/database.vala0000664000000000000000000000473014776241610017512 0ustar rootrootusing Qlite; using Dino.Entities; using Xmpp; namespace Dino.Plugins.OpenPgp { public class Database : Qlite.Database { private const int VERSION = 0; public class AccountSetting : Table { public Column account_id = new Column.Integer("account_id") { primary_key = true }; public Column key = new Column.Text("key") { not_null = true }; internal AccountSetting(Database db) { base(db, "account_setting"); init({account_id, key}); } } public class ContactKey : Table { public Column jid = new Column.Text("jid") { primary_key = true }; public Column key = new Column.Text("key") { not_null = true }; internal ContactKey(Database db) { base(db, "contact_key"); init({jid, key}); } } public AccountSetting account_setting_table { get; private set; } public ContactKey contact_key_table { get; private set; } public Database(string filename) { base(filename, VERSION); this.account_setting_table = new AccountSetting(this); this.contact_key_table = new ContactKey(this); init({account_setting_table, contact_key_table}); try { exec("PRAGMA journal_mode = WAL"); exec("PRAGMA synchronous = NORMAL"); exec("PRAGMA secure_delete = ON"); } catch (Error e) { error("Failed to set OpenPGP database properties: %s", e.message); } } public void set_contact_key(Jid jid, string key) { contact_key_table.upsert() .value(contact_key_table.jid, jid.to_string(), true) .value(contact_key_table.key, key) .perform(); } public string? get_contact_key(Jid jid) { return contact_key_table.select({contact_key_table.key}) .with(contact_key_table.jid, "=", jid.to_string())[contact_key_table.key]; } public void set_account_key(Account account, string key) { account_setting_table.upsert() .value(account_setting_table.account_id, account.id, true) .value(account_setting_table.key, key) .perform(); } public string? get_account_key(Account account) { return account_setting_table.select({account_setting_table.key}) .with(account_setting_table.account_id, "=", account.id)[account_setting_table.key]; } public override void migrate(long oldVersion) { } } } dino-0.5.0/plugins/openpgp/src/encryption_list_entry.vala0000664000000000000000000000633714776241610022421 0ustar rootrootusing Gee; using Gtk; using Dino.Entities; using Xmpp; namespace Dino.Plugins.OpenPgp { private class EncryptionListEntry : Plugins.EncryptionListEntry, Object { private StreamInteractor stream_interactor; private Database db; public EncryptionListEntry(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; } public Entities.Encryption encryption { get { return Encryption.PGP; }} public string name { get { return "OpenPGP"; }} public Object? get_encryption_icon(Entities.Conversation conversation, ContentItem content_item) { return null; } public string? get_encryption_icon_name(Entities.Conversation conversation, ContentItem content_item) { return null; } public void encryption_activated(Entities.Conversation conversation, Plugins.SetInputFieldStatus input_status_callback) { try { GPGHelper.get_public_key(db.get_account_key(conversation.account) ?? ""); } catch (Error e) { input_status_callback(new Plugins.InputFieldStatus("You didn't configure OpenPGP for this account. You can do that in the Accounts Dialog.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } if (conversation.type_ == Conversation.Type.CHAT) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); if (key_id == null) { input_status_callback(new Plugins.InputFieldStatus("This contact does not support %s encryption.".printf("OpenPGP"), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } try { GPGHelper.get_keylist(key_id); } catch (Error e) { input_status_callback(new Plugins.InputFieldStatus("This contact's OpenPGP key is not in your keyring.", Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); } } else if (conversation.type_ == Conversation.Type.GROUPCHAT) { Gee.List muc_jids = new Gee.ArrayList(); Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(occupants); Gee.List? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account); if (offline_members != null) muc_jids.add_all(offline_members); foreach (Jid jid in muc_jids) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, jid); if (key_id == null) { input_status_callback(new Plugins.InputFieldStatus("A member's OpenPGP key is not in your keyring: %s / %s.".printf(jid.to_string(), key_id), Plugins.InputFieldStatus.MessageType.ERROR, Plugins.InputFieldStatus.InputState.NO_SEND)); return; } } } } } } dino-0.5.0/plugins/openpgp/src/encryption_preferences_entry.vala0000664000000000000000000000624214776241610023742 0ustar rootrootusing Adw; using Dino.Entities; using Gtk; namespace Dino.Plugins.OpenPgp { public class PgpPreferencesEntry : Plugins.EncryptionPreferencesEntry { private Plugin plugin; public PgpPreferencesEntry(Plugin plugin) { this.plugin = plugin; } public override Object? get_widget(Account account, WidgetType type) { if (type != WidgetType.GTK4) return null; StringList string_list = new StringList(null); string_list.append(_("Querying GnuPG")); Adw.PreferencesGroup preferences_group = new Adw.PreferencesGroup() { title="OpenPGP" }; populate_string_list.begin(account, preferences_group); return preferences_group; } public override string id { get { return "pgp_preferences_encryption"; }} private async void populate_string_list(Account account, Adw.PreferencesGroup preferences_group) { var keys = yield get_pgp_keys(); if (keys == null) { preferences_group.add(new Adw.ActionRow() { title=_("Announce key"), subtitle=_("Error in GnuPG") }); return; } if (keys.size == 0) { preferences_group.add(new Adw.ActionRow() { title=_("Announce key"), subtitle= _("No keys available. Generate one!") }); return; } StringList string_list = new StringList(null); #if Adw_1_4 var drop_down = new Adw.ComboRow() { title = _("Announce key") }; drop_down.model = string_list; preferences_group.add(drop_down); #else var view = new Adw.ActionRow() { title = "Announce key" }; var drop_down = new DropDown(string_list, null) { valign = Align.CENTER }; view.activatable_widget = drop_down; view.add_suffix(drop_down); preferences_group.add(view); #endif string_list.append(_("Disabled")); for (int i = 0; i < keys.size; i++) { string_list.append(@"$(keys[i].uids[0].uid)\n$(keys[i].fpr.substring(24, 16))"); if (keys[i].fpr == plugin.db.get_account_key(account)) { drop_down.selected = i + 1; } } drop_down.notify["selected"].connect(() => { var key_id = drop_down.selected == 0 ? "" : keys[(int)drop_down.selected - 1].fpr; if (plugin.modules.has_key(account)) { plugin.modules[account].set_private_key_id(key_id); } plugin.db.set_account_key(account, key_id); }); } private static async Gee.List get_pgp_keys() { Gee.List keys = null; SourceFunc callback = get_pgp_keys.callback; new Thread (null, () => { // Querying GnuPG might take some time try { keys = GPGHelper.get_keylist(null, true); } catch (Error e) { warning(e.message); } Idle.add((owned)callback); return null; }); yield; return keys; } } }dino-0.5.0/plugins/openpgp/src/file_transfer/0000775000000000000000000000000014776241610017720 5ustar rootrootdino-0.5.0/plugins/openpgp/src/file_transfer/file_decryptor.vala0000664000000000000000000000425414776241610023604 0ustar rootrootusing Dino.Entities; namespace Dino.Plugins.OpenPgp { public class PgpFileDecryptor : FileDecryptor, Object { public Encryption get_encryption() { return Encryption.PGP; } public FileReceiveData prepare_get_meta_info(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { return receive_data; } public FileMeta prepare_download_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data, FileMeta file_meta) { return file_meta; } public bool can_decrypt_file(Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) { return file_transfer.file_name.has_suffix("pgp") || file_transfer.mime_type == "application/pgp-encrypted"; } public async InputStream decrypt_file(InputStream encrypted_stream, Conversation conversation, FileTransfer file_transfer, FileReceiveData receive_data) throws FileReceiveError { try { uint8[] buf = new uint8[256]; ByteArray data = new ByteArray(); size_t len = -1; do { len = yield encrypted_stream.read_async(buf); data.append(buf[0:len]); } while(len > 0); GPGHelper.DecryptedData clear_data = GPGHelper.decrypt_data(data.data); file_transfer.encryption = Encryption.PGP; if (clear_data.filename != null && clear_data.filename != "") { debug("Decrypting file %s from %s", clear_data.filename, file_transfer.file_name); file_transfer.file_name = clear_data.filename; } else if (file_transfer.file_name.has_suffix(".pgp")) { debug("Decrypting file %s from %s", file_transfer.file_name.substring(0, file_transfer.file_name.length - 4), file_transfer.file_name); file_transfer.file_name = file_transfer.file_name.substring(0, file_transfer.file_name.length - 4); } return new MemoryInputStream.from_data(clear_data.data, GLib.free); } catch (Error e) { throw new FileReceiveError.DECRYPTION_FAILED("PGP file decryption error: %s".printf(e.message)); } } } } dino-0.5.0/plugins/openpgp/src/file_transfer/file_encryptor.vala0000664000000000000000000000336614776241610023621 0ustar rootrootusing Dino.Entities; namespace Dino.Plugins.OpenPgp { public class PgpFileEncryptor : Dino.FileEncryptor, Object { StreamInteractor stream_interactor; public PgpFileEncryptor(StreamInteractor stream_interactor) { this.stream_interactor = stream_interactor; } public bool can_encrypt_file(Conversation conversation, FileTransfer file_transfer) { return conversation.encryption == Encryption.PGP; } public FileMeta encrypt_file(Conversation conversation, FileTransfer file_transfer) throws FileSendError { FileMeta file_meta = new FileMeta(); try { GPG.Key[] keys = stream_interactor.get_module(Manager.IDENTITY).get_key_fprs(conversation); uint8[] enc_content = GPGHelper.encrypt_file(file_transfer.get_file().get_path(), keys, GPG.EncryptFlags.ALWAYS_TRUST, file_transfer.file_name); file_transfer.input_stream = new MemoryInputStream.from_data(enc_content, GLib.free); file_transfer.encryption = Encryption.PGP; file_transfer.server_file_name = Xmpp.random_uuid() + ".pgp"; file_meta.size = enc_content.length; } catch (Error e) { throw new FileSendError.ENCRYPTION_FAILED("PGP file encryption error: %s".printf(e.message)); } debug("Encrypting file %s as %s", file_transfer.file_name, file_transfer.server_file_name); return file_meta; } public FileSendData? preprocess_send_file(Conversation conversation, FileTransfer file_transfer, FileSendData file_send_data, FileMeta file_meta) { HttpFileSendData? send_data = file_send_data as HttpFileSendData; if (send_data == null) return null; send_data.encrypt_message = false; return file_send_data; } } } dino-0.5.0/plugins/openpgp/src/gpgme_fix.c0000664000000000000000000000037314776241610017211 0ustar rootroot#include GRecMutex gpgme_global_mutex = {0}; gpgme_key_t gpgme_key_ref_vapi (gpgme_key_t key) { gpgme_key_ref(key); return key; } gpgme_key_t gpgme_key_unref_vapi (gpgme_key_t key) { gpgme_key_unref(key); return key; } dino-0.5.0/plugins/openpgp/src/gpgme_fix.h0000664000000000000000000000034114776241610017211 0ustar rootroot#ifndef GPGME_FIX #define GPGME_FIX 1 #include #include extern GRecMutex gpgme_global_mutex; gpgme_key_t gpgme_key_ref_vapi (gpgme_key_t key); gpgme_key_t gpgme_key_unref_vapi (gpgme_key_t key); #endif dino-0.5.0/plugins/openpgp/src/gpgme_helper.vala0000664000000000000000000001234714776241610020407 0ustar rootrootusing Gee; using GPG; namespace GPGHelper { private static bool initialized = false; public static string encrypt_armor(string plain, Key[] keys, EncryptFlags flags) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_memory(plain.data, false); Context context = Context.create(); context.set_armor(true); Data enc_data = context.op_encrypt(keys, flags, plain_data); return get_string_from_data(enc_data); } finally { global_mutex.unlock(); } } public static uint8[] encrypt_file(string uri, Key[] keys, EncryptFlags flags, string file_name) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_file(uri); plain_data.set_file_name(file_name); Context context = Context.create(); context.set_armor(true); Data enc_data = context.op_encrypt(keys, flags, plain_data); return get_uint8_from_data(enc_data); } finally { global_mutex.unlock(); } } public static string decrypt(string encr) throws GLib.Error { global_mutex.lock(); try { initialize(); Data enc_data = Data.create_from_memory(encr.data, false); Context context = Context.create(); Data dec_data = context.op_decrypt(enc_data); return get_string_from_data(dec_data); } finally { global_mutex.unlock(); } } public class DecryptedData { public uint8[] data { get; set; } public string filename { get; set; } } public static DecryptedData decrypt_data(uint8[] data) throws GLib.Error { global_mutex.lock(); try { initialize(); Data enc_data = Data.create_from_memory(data, false); Context context = Context.create(); Data dec_data = context.op_decrypt(enc_data); DecryptResult* dec_res = context.op_decrypt_result(); return new DecryptedData() { data=get_uint8_from_data(dec_data), filename=dec_res->file_name}; } finally { global_mutex.unlock(); } } public static string sign(string plain, SigMode mode, Key? key = null) throws GLib.Error { global_mutex.lock(); try { initialize(); Data plain_data = Data.create_from_memory(plain.data, false); Context context = Context.create(); if (key != null) context.signers_add(key); Data signed_data = context.op_sign(plain_data, mode); return get_string_from_data(signed_data); } finally { global_mutex.unlock(); } } public static string? get_sign_key(string signature, string? text) throws GLib.Error { global_mutex.lock(); try { initialize(); Data sig_data = Data.create_from_memory(signature.data, false); Data text_data; if (text != null) { text_data = Data.create_from_memory(text.data, false); } else { text_data = Data.create(); } Context context = Context.create(); context.op_verify(sig_data, text_data); VerifyResult* verify_res = context.op_verify_result(); if (verify_res == null || verify_res.signatures == null) return null; return verify_res.signatures.fpr; } finally { global_mutex.unlock(); } } public static Gee.List get_keylist(string? pattern = null, bool secret_only = false) throws GLib.Error { global_mutex.lock(); try { initialize(); Gee.List keys = new ArrayList(); Context context = Context.create(); context.op_keylist_start(pattern, secret_only ? 1 : 0); try { while (true) { Key key = context.op_keylist_next(); keys.add(key); } } catch (Error e) { if (e.code != GPGError.ErrorCode.EOF) throw e; } context.op_keylist_end(); return keys; } finally { global_mutex.unlock(); } } public static Key? get_public_key(string sig) throws GLib.Error { return get_key(sig, false); } public static Key? get_private_key(string sig) throws GLib.Error { return get_key(sig, true); } private static Key? get_key(string sig, bool priv) throws GLib.Error { global_mutex.lock(); try { initialize(); Context context = Context.create(); Key key = context.get_key(sig, priv); return key; } finally { global_mutex.unlock(); } } private static string get_string_from_data(Data data) { const size_t BUF_SIZE = 256; data.seek(0); uint8[] buf = new uint8[BUF_SIZE + 1]; ssize_t len = 0; string res = ""; do { len = data.read(buf, BUF_SIZE); if (len > 0) { buf[len] = 0; res += (string) buf; } } while (len > 0); return res; } private static uint8[] get_uint8_from_data(Data data) { const size_t BUF_SIZE = 256; data.seek(0); uint8[] buf = new uint8[BUF_SIZE + 1]; ssize_t len = 0; ByteArray res = new ByteArray(); do { len = data.read(buf, BUF_SIZE); if (len > 0) { res.append(buf[0:len]); } } while (len > 0); return res.data; } private static void initialize() { if (!initialized) { check_version(); initialized = true; } } } dino-0.5.0/plugins/openpgp/src/manager.vala0000664000000000000000000001205214776241610017354 0ustar rootrootusing Gee; using Xmpp; using Xmpp; using Dino.Entities; namespace Dino.Plugins.OpenPgp { public class Manager : StreamInteractionModule, Object { public static ModuleIdentity IDENTITY = new ModuleIdentity("pgp_manager"); public string id { get { return IDENTITY.id; } } public const string MESSAGE_ENCRYPTED = "pgp"; private StreamInteractor stream_interactor; private Database db; private HashMap pgp_key_ids = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); private ReceivedMessageListener received_message_listener = new ReceivedMessageListener(); public static void start(StreamInteractor stream_interactor, Database db) { Manager m = new Manager(stream_interactor, db); stream_interactor.add_module(m); } private Manager(StreamInteractor stream_interactor, Database db) { this.stream_interactor = stream_interactor; this.db = db; stream_interactor.account_added.connect(on_account_added); stream_interactor.get_module(MessageProcessor.IDENTITY).received_pipeline.connect(received_message_listener); stream_interactor.get_module(MessageProcessor.IDENTITY).pre_message_send.connect(check_encypt); } public GPG.Key[] get_key_fprs(Conversation conversation) throws Error { Gee.List keys = new Gee.ArrayList(); keys.add(db.get_account_key(conversation.account)); if (conversation.type_ == Conversation.Type.GROUPCHAT) { Gee.List muc_jids = new Gee.ArrayList(); Gee.List? occupants = stream_interactor.get_module(MucManager.IDENTITY).get_occupants(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(occupants); Gee.List? offline_members = stream_interactor.get_module(MucManager.IDENTITY).get_offline_members(conversation.counterpart, conversation.account); if (occupants != null) muc_jids.add_all(offline_members); foreach (Jid jid in muc_jids) { string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, jid); if (key_id != null && GPGHelper.get_keylist(key_id).size > 0 && !keys.contains(key_id)) { keys.add(key_id); } } } else { string? key_id = get_key_id(conversation.account, conversation.counterpart); if (key_id != null) { keys.add(key_id); } } GPG.Key[] gpgkeys = new GPG.Key[keys.size]; for (int i = 0; i < keys.size; i++) { try { GPG.Key key = GPGHelper.get_public_key(keys[i]); if (key != null) gpgkeys[i] = key; } catch (Error e) {} } return gpgkeys; } private void check_encypt(Entities.Message message, Xmpp.MessageStanza message_stanza, Conversation conversation) { try { if (message.encryption == Encryption.PGP) { GPG.Key[] keys = get_key_fprs(conversation); XmppStream? stream = stream_interactor.get_stream(conversation.account); if (stream != null) { bool encrypted = stream.get_module(Module.IDENTITY).encrypt(message_stanza, keys); if (!encrypted) message.marked = Entities.Message.Marked.WONTSEND; } } } catch (Error e) { message.marked = Entities.Message.Marked.WONTSEND; } } public string? get_key_id(Account account, Jid jid) { Jid search_jid = stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account) ? jid : jid.bare_jid; return db.get_contact_key(search_jid); } private void on_account_added(Account account) { stream_interactor.module_manager.get_module(account, Module.IDENTITY).received_jid_key_id.connect((stream, jid, key_id) => { on_jid_key_received(account, jid, key_id); }); } private void on_jid_key_received(Account account, Jid jid, string key_id) { lock (pgp_key_ids) { if (!pgp_key_ids.has_key(jid) || pgp_key_ids[jid] != key_id) { Jid set_jid = stream_interactor.get_module(MucManager.IDENTITY).is_groupchat_occupant(jid, account) ? jid : jid.bare_jid; db.set_contact_key(set_jid, key_id); } pgp_key_ids[jid] = key_id; } } private class ReceivedMessageListener : MessageListener { public string[] after_actions_const = new string[]{ }; public override string action_group { get { return "DECRYPT"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(Entities.Message message, Xmpp.MessageStanza stanza, Conversation conversation) { if (MessageFlag.get_flag(stanza) != null && MessageFlag.get_flag(stanza).decrypted) { message.encryption = Encryption.PGP; } return false; } } } } dino-0.5.0/plugins/openpgp/src/plugin.vala0000664000000000000000000000374014776241610017244 0ustar rootrootusing Gee; using Dino.Entities; extern const string GETTEXT_PACKAGE; extern const string LOCALE_INSTALL_DIR; namespace Dino.Plugins.OpenPgp { public class Plugin : Plugins.RootInterface, Object { public Dino.Application app; public Database db; public HashMap modules = new HashMap(Account.hash_func, Account.equals_func); private EncryptionListEntry list_entry; private ContactDetailsProvider contact_details_provider; public void registered(Dino.Application app) { this.app = app; this.db = new Database(Path.build_filename(Application.get_storage_dir(), "pgp.db")); this.list_entry = new EncryptionListEntry(app.stream_interactor, db); this.contact_details_provider = new ContactDetailsProvider(app.stream_interactor); app.plugin_registry.register_encryption_list_entry(list_entry); app.plugin_registry.register_encryption_preferences_entry(new PgpPreferencesEntry(this)); app.plugin_registry.register_contact_details_entry(contact_details_provider); app.stream_interactor.module_manager.initialize_account_modules.connect(on_initialize_account_modules); Manager.start(app.stream_interactor, db); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_encryptor(new PgpFileEncryptor(app.stream_interactor)); app.stream_interactor.get_module(FileManager.IDENTITY).add_file_decryptor(new PgpFileDecryptor()); JingleFileHelperRegistry.instance.add_encryption_helper(Encryption.PGP, new JingleFileEncryptionHelperTransferOnly()); internationalize(GETTEXT_PACKAGE, app.search_path_generator.get_locale_path(GETTEXT_PACKAGE, LOCALE_INSTALL_DIR)); } public void shutdown() { } private void on_initialize_account_modules(Account account, ArrayList modules) { Module module = new Module(db.get_account_key(account)); this.modules[account] = module; modules.add(module); } } } dino-0.5.0/plugins/openpgp/src/register_plugin.vala0000664000000000000000000000013714776241610021145 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.OpenPgp.Plugin); }dino-0.5.0/plugins/openpgp/src/stream_flag.vala0000664000000000000000000000105214776241610020224 0ustar rootrootusing Gee; using Xmpp; namespace Dino.Plugins.OpenPgp { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "pgp"); public HashMap key_ids = new HashMap(Jid.hash_bare_func, Jid.equals_bare_func); public string? get_key_id(Jid jid) { return key_ids[jid]; } public void set_key_id(Jid jid, string key) { key_ids[jid] = key; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/plugins/openpgp/src/stream_module.vala0000664000000000000000000001661614776241610020614 0ustar rootrootusing GPG; using Xmpp; namespace Dino.Plugins.OpenPgp { private const string NS_URI = "jabber:x"; private const string NS_URI_ENCRYPTED = NS_URI + ":encrypted"; private const string NS_URI_SIGNED = NS_URI + ":signed"; public class Module : XmppStreamModule { public static Xmpp.ModuleIdentity IDENTITY = new Xmpp.ModuleIdentity(NS_URI, "0027_current_pgp_usage"); public signal void received_jid_key_id(XmppStream stream, Jid jid, string key_id); private string? signed_status = null; private Key? own_key = null; private ReceivedPipelineDecryptListener received_pipeline_decrypt_listener = new ReceivedPipelineDecryptListener(); public Module(string? own_key_id = null) { set_private_key_id(own_key_id); } public void set_private_key_id(string? own_key_id) { if (own_key_id != null) { try { own_key = GPGHelper.get_private_key(own_key_id); if (own_key == null) warning("Can't get PGP private key"); } catch (Error e) { } if (own_key != null) { signed_status = gpg_sign("", own_key); } } } public bool encrypt(MessageStanza message, GPG.Key[] keys) { string? enc_body = gpg_encrypt(message.body, keys); if (enc_body != null) { message.stanza.put_node(new StanzaNode.build("x", NS_URI_ENCRYPTED).add_self_xmlns().put_node(new StanzaNode.text(enc_body))); message.body = "[This message is OpenPGP encrypted (see XEP-0027)]"; Xep.ExplicitEncryption.add_encryption_tag_to_message(message, NS_URI_ENCRYPTED); return true; } return false; } public override void attach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).received_presence.connect(on_received_presence); stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.connect(on_pre_send_presence_stanza); stream.get_module(MessageModule.IDENTITY).received_pipeline.connect(received_pipeline_decrypt_listener); stream.add_flag(new Flag()); } public override void detach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).received_presence.disconnect(on_received_presence); stream.get_module(Presence.Module.IDENTITY).pre_send_presence_stanza.disconnect(on_pre_send_presence_stanza); stream.get_module(MessageModule.IDENTITY).received_pipeline.disconnect(received_pipeline_decrypt_listener); } public static void require(XmppStream stream) { if (stream.get_module(IDENTITY) == null) stream.add_module(new Module()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private void on_received_presence(XmppStream stream, Presence.Stanza presence) { StanzaNode x_node = presence.stanza.get_subnode("x", NS_URI_SIGNED); if (x_node == null) return; string? sig = x_node.get_string_content(); if (sig == null) return; new Thread (null, () => { string signed_data = presence.status == null ? "" : presence.status; string? key_id = get_sign_key(sig, signed_data); if (key_id != null) { stream.get_flag(Flag.IDENTITY).set_key_id(presence.from, key_id); Idle.add(() => { received_jid_key_id(stream, presence.from, key_id); return false; }); } return null; }); } private void on_pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence) { if (presence.type_ == Presence.Stanza.TYPE_AVAILABLE && signed_status != null) { presence.stanza.put_node(new StanzaNode.build("x", NS_URI_SIGNED).add_self_xmlns().put_node(new StanzaNode.text(signed_status))); } } private static string? gpg_encrypt(string plain, GPG.Key[] keys) { string encr; try { encr = GPGHelper.encrypt_armor(plain, keys, GPG.EncryptFlags.ALWAYS_TRUST); } catch (Error e) { return null; } int encryption_start = encr.index_of("\n\n") + 2; return encr.substring(encryption_start, encr.length - "\n-----END PGP MESSAGE-----".length - encryption_start); } private static string? get_sign_key(string sig, string signed_text) { string armor = "-----BEGIN PGP MESSAGE-----\n\n" + sig + "\n-----END PGP MESSAGE-----"; string? sign_key = null; try { sign_key = GPGHelper.get_sign_key(armor, signed_text); } catch (Error e) { } return sign_key; } private static string? gpg_sign(string str, Key key) { string signed; try { signed = GPGHelper.sign(str, GPG.SigMode.CLEAR, key); } catch (Error e) { return null; } int signature_start = signed.index_of("-----BEGIN PGP SIGNATURE-----"); signature_start = signed.index_of("\n\n", signature_start) + 2; return signed.substring(signature_start, signed.length - "\n-----END PGP SIGNATURE-----".length - signature_start); } } public class MessageFlag : Xmpp.MessageFlag { public const string id = "pgp"; public bool decrypted = false; public static MessageFlag? get_flag(MessageStanza message) { return (MessageFlag) message.get_flag(NS_URI, id); } public override string get_ns() { return NS_URI; } public override string get_id() { return id; } } public class ReceivedPipelineDecryptListener : StanzaListener { private string[] after_actions_const = {"MODIFY_BODY"}; public override string action_group { get { return "ENCRYPT_BODY"; } } public override string[] after_actions { get { return after_actions_const; } } public override async bool run(XmppStream stream, MessageStanza message) { string? encrypted = get_cyphertext(message); if (encrypted != null) { MessageFlag flag = new MessageFlag(); message.add_flag(flag); string? decrypted = yield gpg_decrypt(encrypted); if (decrypted != null) { flag.decrypted = true; message.body = decrypted; } } return false; } private static async string? gpg_decrypt(string enc) { SourceFunc callback = gpg_decrypt.callback; string? res = null; new Thread (null, () => { string armor = "-----BEGIN PGP MESSAGE-----\n\n" + enc + "\n-----END PGP MESSAGE-----"; try { res = GPGHelper.decrypt(armor); } catch (Error e) { res = null; } Idle.add((owned) callback); return null; }); yield; return res; } private string? get_cyphertext(MessageStanza message) { StanzaNode? x_node = message.stanza.get_subnode("x", NS_URI_ENCRYPTED); return x_node == null ? null : x_node.get_string_content(); } } } dino-0.5.0/plugins/openpgp/src/util.vala0000664000000000000000000000075714776241610016730 0ustar rootrootusing Gtk; using Dino.Entities; using Xmpp.Util; namespace Dino.Plugins.OpenPgp { public static string markup_id(string s, bool is_fingerprint) { string markup = is_fingerprint ? "" : "0x"; for (int i = 0; i < s.length; i += 4) { string four_chars = s.substring(i, 4).down(); if (i == 4 * 5) markup += "\n"; markup += four_chars; if (is_fingerprint) markup += " "; } return "" + markup + ""; } } dino-0.5.0/plugins/openpgp/vapi/0000775000000000000000000000000014776241610015245 5ustar rootrootdino-0.5.0/plugins/openpgp/vapi/gpg-error.vapi0000664000000000000000000001612714776241610020041 0ustar rootroot/* gcrypt.vapi * * Copyright: * 2008 Jiqing Qiang * 2008, 2010, 2012-2013 Evan Nemerson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Author: * Jiqing Qiang * Evan Nemerson */ [CCode (cheader_filename = "gpg-error.h")] namespace GPGError { [CCode (cname = "gpg_err_source_t", cprefix = "GPG_ERR_SOURCE_")] public enum ErrorSource { UNKNOWN, GCRYPT, GPG, GPGSM, GPGAGENT, PINENTRY, SCD, GPGME, KEYBOX, KSBA, DIRMNGR, GSTI, ANY, USER_1, USER_2, USER_3, USER_4, /* This is one more than the largest allowed entry. */ DIM } [CCode (cname = "gpg_err_code_t", cprefix = "GPG_ERR_")] public enum ErrorCode { NO_ERROR, GENERAL, UNKNOWN_PACKET, UNKNOWN_VERSION, PUBKEY_ALGO, DIGEST_ALGO, BAD_PUBKEY, BAD_SECKEY, BAD_SIGNATURE, NO_PUBKEY, CHECKSUM, BAD_PASSPHRASE, CIPHER_ALGO, KEYRING_OPEN, INV_PACKET, INV_ARMOR, NO_USER_ID, NO_SECKEY, WRONG_SECKEY, BAD_KEY, COMPR_ALGO, NO_PRIME, NO_ENCODING_METHOD, NO_ENCRYPTION_SCHEME, NO_SIGNATURE_SCHEME, INV_ATTR, NO_VALUE, NOT_FOUND, VALUE_NOT_FOUND, SYNTAX, BAD_MPI, INV_PASSPHRASE, SIG_CLASS, RESOURCE_LIMIT, INV_KEYRING, TRUSTDB, BAD_CERT, INV_USER_ID, UNEXPECTED, TIME_CONFLICT, KEYSERVER, WRONG_PUBKEY_ALGO, TRIBUTE_TO_D_A, WEAK_KEY, INV_KEYLEN, INV_ARG, BAD_URI, INV_URI, NETWORK, UNKNOWN_HOST, SELFTEST_FAILED, NOT_ENCRYPTED, NOT_PROCESSED, UNUSABLE_PUBKEY, UNUSABLE_SECKEY, INV_VALUE, BAD_CERT_CHAIN, MISSING_CERT, NO_DATA, BUG, NOT_SUPPORTED, INV_OP, TIMEOUT, INTERNAL, EOF_GCRYPT, INV_OBJ, TOO_SHORT, TOO_LARGE, NO_OBJ, NOT_IMPLEMENTED, CONFLICT, INV_CIPHER_MODE, INV_FLAG, INV_HANDLE, TRUNCATED, INCOMPLETE_LINE, INV_RESPONSE, NO_AGENT, AGENT, INV_DATA, ASSUAN_SERVER_FAULT, ASSUAN, INV_SESSION_KEY, INV_SEXP, UNSUPPORTED_ALGORITHM, NO_PIN_ENTRY, PIN_ENTRY, BAD_PIN, INV_NAME, BAD_DATA, INV_PARAMETER, WRONG_CARD, NO_DIRMNGR, DIRMNGR, CERT_REVOKED, NO_CRL_KNOWN, CRL_TOO_OLD, LINE_TOO_LONG, NOT_TRUSTED, CANCELED, BAD_CA_CERT, CERT_EXPIRED, CERT_TOO_YOUNG, UNSUPPORTED_CERT, UNKNOWN_SEXP, UNSUPPORTED_PROTECTION, CORRUPTED_PROTECTION, AMBIGUOUS_NAME, CARD, CARD_RESET, CARD_REMOVED, INV_CARD, CARD_NOT_PRESENT, NO_PKCS15_APP, NOT_CONFIRMED, CONFIGURATION, NO_POLICY_MATCH, INV_INDEX, INV_ID, NO_SCDAEMON, SCDAEMON, UNSUPPORTED_PROTOCOL, BAD_PIN_METHOD, CARD_NOT_INITIALIZED, UNSUPPORTED_OPERATION, WRONG_KEY_USAGE, NOTHING_FOUND, WRONG_BLOB_TYPE, MISSING_VALUE, HARDWARE, PIN_BLOCKED, USE_CONDITIONS, PIN_NOT_SYNCED, INV_CRL, BAD_BER, INV_BER, ELEMENT_NOT_FOUND, IDENTIFIER_NOT_FOUND, INV_TAG, INV_LENGTH, INV_KEYINFO, UNEXPECTED_TAG, NOT_DER_ENCODED, NO_CMS_OBJ, INV_CMS_OBJ, UNKNOWN_CMS_OBJ, UNSUPPORTED_CMS_OBJ, UNSUPPORTED_ENCODING, UNSUPPORTED_CMS_VERSION, UNKNOWN_ALGORITHM, INV_ENGINE, PUBKEY_NOT_TRUSTED, DECRYPT_FAILED, KEY_EXPIRED, SIG_EXPIRED, ENCODING_PROBLEM, INV_STATE, DUP_VALUE, MISSING_ACTION, MODULE_NOT_FOUND, INV_OID_STRING, INV_TIME, INV_CRL_OBJ, UNSUPPORTED_CRL_VERSION, INV_CERT_OBJ, UNKNOWN_NAME, LOCALE_PROBLEM, NOT_LOCKED, PROTOCOL_VIOLATION, INV_MAC, INV_REQUEST, UNKNOWN_EXTN, UNKNOWN_CRIT_EXTN, LOCKED, UNKNOWN_OPTION, UNKNOWN_COMMAND, BUFFER_TOO_SHORT, SEXP_INV_LEN_SPEC, SEXP_STRING_TOO_LONG, SEXP_UNMATCHED_PAREN, SEXP_NOT_CANONICAL, SEXP_BAD_CHARACTER, SEXP_BAD_QUOTATION, SEXP_ZERO_PREFIX, SEXP_NESTED_DH, SEXP_UNMATCHED_DH, SEXP_UNEXPECTED_PUNC, SEXP_BAD_HEX_CHAR, SEXP_ODD_HEX_NUMBERS, SEXP_BAD_OCT_CHAR, ASS_GENERAL, ASS_ACCEPT_FAILED, ASS_CONNECT_FAILED, ASS_INV_RESPONSE, ASS_INV_VALUE, ASS_INCOMPLETE_LINE, ASS_LINE_TOO_LONG, ASS_NESTED_COMMANDS, ASS_NO_DATA_CB, ASS_NO_INQUIRE_CB, ASS_NOT_A_SERVER, ASS_NOT_A_CLIENT, ASS_SERVER_START, ASS_READ_ERROR, ASS_WRITE_ERROR, ASS_TOO_MUCH_DATA, ASS_UNEXPECTED_CMD, ASS_UNKNOWN_CMD, ASS_SYNTAX, ASS_CANCELED, ASS_NO_INPUT, ASS_NO_OUTPUT, ASS_PARAMETER, ASS_UNKNOWN_INQUIRE, USER_1, USER_2, USER_3, USER_4, USER_5, USER_6, USER_7, USER_8, USER_9, USER_10, USER_11, USER_12, USER_13, USER_14, USER_15, USER_16, MISSING_ERRNO, UNKNOWN_ERRNO, EOF, E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, EADV, EAFNOSUPPORT, EAGAIN, EALREADY, EAUTH, EBACKGROUND, EBADE, EBADF, EBADFD, EBADMSG, EBADR, EBADRPC, EBADRQC, EBADSLT, EBFONT, EBUSY, ECANCELED, ECHILD, ECHRNG, ECOMM, ECONNABORTED, ECONNREFUSED, ECONNRESET, ED, EDEADLK, EDEADLOCK, EDESTADDRREQ, EDIED, EDOM, EDOTDOT, EDQUOT, EEXIST, EFAULT, EFBIG, EFTYPE, EGRATUITOUS, EGREGIOUS, EHOSTDOWN, EHOSTUNREACH, EIDRM, EIEIO, EILSEQ, EINPROGRESS, EINTR, EINVAL, EIO, EISCONN, EISDIR, EISNAM, EL2HLT, EL2NSYNC, EL3HLT, EL3RST, ELIBACC, ELIBBAD, ELIBEXEC, ELIBMAX, ELIBSCN, ELNRNG, ELOOP, EMEDIUMTYPE, EMFILE, EMLINK, EMSGSIZE, EMULTIHOP, ENAMETOOLONG, ENAVAIL, ENEEDAUTH, ENETDOWN, ENETRESET, ENETUNREACH, ENFILE, ENOANO, ENOBUFS, ENOCSI, ENODATA, ENODEV, ENOENT, ENOEXEC, ENOLCK, ENOLINK, ENOMEDIUM, ENOMEM, ENOMSG, ENONET, ENOPKG, ENOPROTOOPT, ENOSPC, ENOSR, ENOSTR, ENOSYS, ENOTBLK, ENOTCONN, ENOTDIR, ENOTEMPTY, ENOTNAM, ENOTSOCK, ENOTSUP, ENOTTY, ENOTUNIQ, ENXIO, EOPNOTSUPP, EOVERFLOW, EPERM, EPFNOSUPPORT, EPIPE, EPROCLIM, EPROCUNAVAIL, EPROGMISMATCH, EPROGUNAVAIL, EPROTO, EPROTONOSUPPORT, EPROTOTYPE, ERANGE, EREMCHG, EREMOTE, EREMOTEIO, ERESTART, EROFS, ERPCMISMATCH, ESHUTDOWN, ESOCKTNOSUPPORT, ESPIPE, ESRCH, ESRMNT, ESTALE, ESTRPIPE, ETIME, ETIMEDOUT, ETOOMANYREFS, ETXTBSY, EUCLEAN, EUNATCH, EUSERS, EWOULDBLOCK, EXDEV, EXFULL, /* This is one more than the largest allowed entry. */ CODE_DIM } [CCode (cname = "gpg_err_code_t", cprefix = "gpg_err_")] public struct Error : uint { [CCode (cname = "gpg_err_make")] public Error (ErrorSource source, ErrorCode code); [CCode (cname = "gpg_err_make_from_errno")] public Error.from_errno (ErrorSource source, int err); public ErrorCode code { [CCode (cname = "gpg_err_code")] get; } public ErrorSource source { [CCode (cname = "gpg_err_source")] get; } } } dino-0.5.0/plugins/openpgp/vapi/gpgme.vapi0000664000000000000000000004566314776241610017243 0ustar rootroot/* libgpgme.vapi * * Copyright (C) 2009 Sebastian Reichel * Copyright (C) 2022 Itay Grudev * * 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. * */ [CCode (lower_case_cprefix = "gpgme_", cheader_filename = "gpgme.h,gpgme_fix.h")] namespace GPG { public static GLib.RecMutex global_mutex; [CCode (cname = "struct _gpgme_engine_info")] public struct EngineInfo { EngineInfo* next; Protocol protocol; string file_name; string version; string req_version; string? home_dir; } [Compact] [CCode (cname = "struct _gpgme_key", ref_function = "gpgme_key_ref_vapi", unref_function = "gpgme_key_unref_vapi", free_function = "gpgme_key_release")] public class Key { public bool revoked; public bool expired; public bool disabled; public bool invalid; public bool can_encrypt; public bool can_sign; public bool can_certify; public bool secret; public bool can_authenticate; public bool is_qualified; public Protocol protocol; public string issuer_serial; public string issuer_name; public string chain_id; public Validity owner_trust; [CCode (array_length = false, array_null_terminated = true)] public SubKey[] subkeys; [CCode (array_length = false, array_null_terminated = true)] public UserID[] uids; public KeylistMode keylist_mode; public string fpr { get { return subkeys[0].fpr; } } } [CCode (cname = "struct _gpgme_sig_notation")] public struct SigNotation { SigNotation* next; string? name; string value; int name_len; int value_len; SigNotationFlags flags; bool human_readable; bool critical; } [CCode (cname = "struct _gpgme_subkey")] public struct SubKey { SubKey* next; bool revoked; bool expired; bool disabled; bool invalid; bool can_encrypt; bool can_sign; bool can_certify; bool secret; bool can_authenticate; bool is_qualified; bool is_cardkey; PublicKeyAlgorithm algo; uint length; string keyid; string fpr; long timestamp; long expires; string? card_number; } [CCode (cname = "struct _gpgme_key_sig")] public struct KeySig { KeySig* next; bool revoked; bool expired; bool invalid; bool exportable; PublicKeyAlgorithm algo; string keyid; long timestamp; long expires; GPGError.Error status; string uid; string name; string email; string comment; uint sig_class; SigNotation notations; } [CCode (cname = "struct _gpgme_user_id")] public struct UserID { UserID* next; bool revoked; bool invalid; Validity validity; string uid; string name; string email; string comment; KeySig signatures; } [CCode (cname = "struct _gpgme_op_verify_result")] public struct VerifyResult { Signature* signatures; string? file_name; } [CCode (cname = "struct _gpgme_op_sign_result")] public struct SignResult { InvalidKey invalid_signers; Signature* signatures; } [CCode (cname = "struct _gpgme_op_encrypt_result")] public struct EncryptResult { InvalidKey invalid_signers; } [CCode (cname = "struct _gpgme_op_decrypt_result")] public struct DecryptResult { string unsupported_algorithm; bool wrong_key_usage; Recipient recipients; string file_name; } [CCode (cname = "struct _gpgme_recipient")] public struct Recipient { Recipient *next; string keyid; PublicKeyAlgorithm pubkey_algo; GPGError.Error status; } [CCode (cname = "struct _gpgme_invalid_key")] public struct InvalidKey { InvalidKey *next; string fpr; GPGError.Error reason; } [CCode (cname = "struct _gpgme_signature")] public struct Signature { Signature *next; Sigsum summary; string fpr; GPGError.Error status; SigNotation notations; ulong timestamp; ulong exp_timestamp; bool wrong_key_usage; PKAStatus pka_trust; bool chain_model; Validity validity; GPGError.Error validity_reason; PublicKeyAlgorithm pubkey_algo; HashAlgorithm hash_algo; string? pka_address; } public enum PKAStatus { NOT_AVAILABLE, BAD, OKAY, RFU } [CCode (cname = "gpgme_sigsum_t", cprefix = "GPGME_SIGSUM_")] public enum Sigsum { VALID, GREEN, RED, KEY_REVOKED, KEY_EXPIRED, SIG_EXPIRED, KEY_MISSING, CRL_MISSING, CRL_TOO_OLD, BAD_POLICY, SYS_ERROR } [CCode (cname = "gpgme_data_encoding_t", cprefix = "GPGME_DATA_ENCODING_")] public enum DataEncoding { NONE, BINARY, BASE64, ARMOR, URL, URLESC, URL0 } [CCode (cname = "gpgme_pubkey_algo_t", cprefix = "GPGME_PK_")] public enum PublicKeyAlgorithm { RSA, RSA_E, RSA_S, ELG_E, DSA, ELG } [CCode (cname = "gpgme_hash_algo_t", cprefix = "GPGME_MD_")] public enum HashAlgorithm { NONE, MD5, SHA1, RMD160, MD2, TIGER, HAVAL, SHA256, SHA384, SHA512, MD4, CRC32, CRC32_RFC1510, CRC24_RFC2440 } [CCode (cname = "gpgme_sig_mode_t", cprefix = "GPGME_SIG_MODE_")] public enum SigMode { NORMAL, DETACH, CLEAR } [CCode (cname = "gpgme_validity_t", cprefix = "GPGME_VALIDITY_")] public enum Validity { UNKNOWN, UNDEFINED, NEVER, MARGINAL, FULL, ULTIMATE } [CCode (cname = "gpgme_protocol_t", cprefix = "GPGME_PROTOCOL_")] public enum Protocol { OpenPGP, CMS, GPGCONF, ASSUAN, UNKNOWN } [CCode (cname = "gpgme_keylist_mode_t", cprefix = "GPGME_KEYLIST_MODE_")] public enum KeylistMode { LOCAL, EXTERN, SIGS, SIG_NOTATIONS, EPHEMERAL, VALIDATE } [CCode (cname = "gpgme_export_mode_t", cprefix = "GPGME_EXPORT_MODE_")] public enum ExportMode { EXTERN } [CCode (cprefix = "GPGME_AUDITLOG_")] public enum AuditLogFlag { HTML, WITH_HELP } [CCode (cname = "gpgme_sig_notation_flags_t", cprefix = "GPGME_SIG_NOTATION_")] public enum SigNotationFlags { HUMAN_READABLE, CRITICAL } [CCode (cname = "gpgme_encrypt_flags_t", cprefix = "GPGME_ENCRYPT_")] public enum EncryptFlags { ALWAYS_TRUST, NO_ENCRYPT_TO } [CCode (cname = "gpgme_status_code_t", cprefix = "GPGME_STATUS_")] public enum StatusCode { EOF, ENTER, LEAVE, ABORT, GOODSIG, BADSIG, ERRSIG, BADARMOR, RSA_OR_IDEA, KEYEXPIRED, KEYREVOKED, TRUST_UNDEFINED, TRUST_NEVER, TRUST_MARGINAL, TRUST_FULLY, TRUST_ULTIMATE, SHM_INFO, SHM_GET, SHM_GET_BOOL, SHM_GET_HIDDEN, NEED_PASSPHRASE, VALIDSIG, SIG_ID, SIG_TO, ENC_TO, NODATA, BAD_PASSPHRASE, NO_PUBKEY, NO_SECKEY, NEED_PASSPHRASE_SYM, DECRYPTION_FAILED, DECRYPTION_OKAY, MISSING_PASSPHRASE, GOOD_PASSPHRASE, GOODMDC, BADMDC, ERRMDC, IMPORTED, IMPORT_OK, IMPORT_PROBLEM, IMPORT_RES, FILE_START, FILE_DONE, FILE_ERROR, BEGIN_DECRYPTION, END_DECRYPTION, BEGIN_ENCRYPTION, END_ENCRYPTION, DELETE_PROBLEM, GET_BOOL, GET_LINE, GET_HIDDEN, GOT_IT, PROGRESS, SIG_CREATED, SESSION_KEY, NOTATION_NAME, NOTATION_DATA, POLICY_URL, BEGIN_STREAM, END_STREAM, KEY_CREATED, USERID_HINT, UNEXPECTED, INV_RECP, NO_RECP, ALREADY_SIGNED, SIGEXPIRED, EXPSIG, EXPKEYSIG, TRUNCATED, ERROR, NEWSIG, REVKEYSIG, SIG_SUBPACKET, NEED_PASSPHRASE_PIN, SC_OP_FAILURE, SC_OP_SUCCESS, CARDCTRL, BACKUP_KEY_CREATED, PKA_TRUST_BAD, PKA_TRUST_GOOD, PLAINTEXT } [Compact] [CCode (cname = "struct gpgme_context", free_function = "gpgme_release", cprefix = "gpgme_")] public class Context { private static GPGError.Error new(out Context ctx); public static Context create() throws GLib.Error { Context ctx; throw_if_error(@new(out ctx)); return ctx; } public GPGError.Error set_protocol(Protocol p); public Protocol get_protocol(); public void set_armor(bool yes); public bool get_armor(); public void set_textmode(bool yes); public bool get_textmode(); public GPGError.Error set_keylist_mode(KeylistMode mode); public KeylistMode get_keylist_mode(); public void set_include_certs(int nr_of_certs = -256); public int get_include_certs(); public void set_passphrase_cb(passphrase_callback cb, void* hook_value = null); public void get_passphrase_cb(out passphrase_callback cb, out void* hook_value); public GPGError.Error set_locale(int category, string val); [CCode (cname = "gpgme_ctx_get_engine_info")] public EngineInfo* get_engine_info(); [CCode (cname = "gpgme_ctx_set_engine_info")] public GPGError.Error set_engine_info(Protocol proto, string file_name, string home_dir); public void signers_clear(); public GPGError.Error signers_add(Key key); public Key* signers_enum(int n); public void sig_notation_clear(); public GPGError.Error sig_notation_add(string name, string val, SigNotationFlags flags); public SigNotation* sig_notation_get(); [CCode (cname = "gpgme_get_key")] private GPGError.Error get_key_(string fpr, out Key key, bool secret); [CCode (cname = "gpgme_get_key_")] public Key get_key(string fpr, bool secret) throws GLib.Error { Key key; throw_if_error(get_key_(fpr, out key, secret)); return key; } public Context* wait(out GPGError.Error status, bool hang); public SignResult* op_sign_result(); [CCode (cname = "gpgme_op_sign")] public GPGError.Error op_sign_(Data plain, Data sig, SigMode mode); [CCode (cname = "gpgme_op_sign_")] public Data op_sign(Data plain, SigMode mode) throws GLib.Error { Data sig = Data.create(); throw_if_error(op_sign_(plain, sig, mode)); return sig; } public VerifyResult* op_verify_result(); [CCode (cname = "gpgme_op_verify")] public GPGError.Error op_verify_(Data sig, Data signed_text, Data? plaintext); [CCode (cname = "gpgme_op_verify_")] public Data op_verify(Data sig, Data signed_text) throws GLib.Error { Data plaintext = Data.create(); throw_if_error(op_verify_(sig, signed_text, plaintext)); return plaintext; } public EncryptResult* op_encrypt_result(); [CCode (cname = "gpgme_op_encrypt")] public GPGError.Error op_encrypt_([CCode (array_length = false)] Key[] recp, EncryptFlags flags, Data plain, Data cipher); [CCode (cname = "gpgme_op_encrypt_")] public Data op_encrypt(Key[] recp, EncryptFlags flags, Data plain) throws GLib.Error { Data cipher = Data.create(); throw_if_error(op_encrypt_(recp, flags, plain, cipher)); return cipher; } public DecryptResult* op_decrypt_result(); [CCode (cname = "gpgme_op_decrypt")] public GPGError.Error op_decrypt_(Data cipher, Data plain); [CCode (cname = "gpgme_op_decrypt_")] public Data op_decrypt(Data cipher) throws GLib.Error { Data plain = Data.create(); throw_if_error(op_decrypt_(cipher, plain)); return plain; } public GPGError.Error op_export(string? pattern, ExportMode mode, Data keydata); public GPGError.Error op_import(Data keydata); public unowned ImportResult op_import_result(); [CCode (cname = "gpgme_op_keylist_start")] private GPGError.Error op_keylist_start_(string? pattern = null, int secret_only = 0); [CCode (cname = "gpgme_op_keylist_start_")] public void op_keylist_start(string? pattern = null, int secret_only = 0) throws GLib.Error { throw_if_error(op_keylist_start_(pattern, secret_only)); } [CCode (cname = "gpgme_op_keylist_next")] private GPGError.Error op_keylist_next_(out Key key); [CCode (cname = "gpgme_op_keylist_next_")] public Key op_keylist_next() throws GLib.Error { Key key; throw_if_error(op_keylist_next_(out key)); return key; } [CCode (cname = "gpgme_op_keylist_end")] private GPGError.Error op_keylist_end_(); [CCode (cname = "gpgme_op_keylist_end_")] public void op_keylist_end() throws GLib.Error { throw_if_error(op_keylist_end_()); } public KeylistResult op_keylist_result(); } [Flags] [CCode (cname="unsigned int")] public enum ImportStatusFlags { [CCode (cname = "GPGME_IMPORT_NEW")] NEW, [CCode (cname = "GPGME_IMPORT_UID")] UID, [CCode (cname = "GPGME_IMPORT_SIG")] SIG, [CCode (cname = "GPGME_IMPORT_SUBKEY")] SUBKEY, [CCode (cname = "GPGME_IMPORT_SECRET")] SECRET } [Compact] [CCode (cname = "struct _gpgme_import_status")] public class ImportStatus { public ImportStatus? next; public string fpr; public GPGError.Error result; public ImportStatusFlags status; } [Compact] [CCode (cname = "struct _gpgme_op_import_result")] public class ImportResult { public int considered; public int no_user_id; public int imported; public int imported_rsa; public int unchanged; public int new_user_ids; public int new_sub_keys; public int new_signatures; public int new_revocations; public int secret_read; public int secret_imported; public int secret_unchanged; public int not_imported; public ImportStatus imports; } [Compact] [CCode (cname = "struct _gpgme_op_keylist_result")] public class KeylistResult { uint truncated; } [Compact] [CCode (cname = "struct gpgme_data", free_function = "gpgme_data_release", cprefix = "gpgme_data_")] public class Data { public static GPGError.Error new(out Data d); public static Data create() throws GLib.Error { Data data; throw_if_error(@new(out data)); return data; } [CCode (cname = "gpgme_data_new_from_mem")] public static GPGError.Error new_from_memory(out Data d, char[] buffer, bool copy); public static Data create_from_memory(uint8[] buffer, bool copy) throws GLib.Error { Data data; throw_if_error(new_from_memory(out data, (char[]) buffer, copy)); return data; } [CCode (cname = "gpgme_data_new_from_file")] public static GPGError.Error new_from_file(out Data d, string filename, int copy = 1); public static Data create_from_file(string filename, int copy = 1) throws GLib.Error { Data data; throw_if_error(new_from_file(out data, filename, copy)); return data; } [CCode (cname = "gpgme_data_release_and_get_mem")] public string release_and_get_mem(out size_t len); public ssize_t read([CCode (array_length = false)] uint8[] buf, size_t len); public ssize_t write(uint8[] buf); public long seek(long offset, int whence=0); public GPGError.Error set_file_name(string file_name); public DataEncoding *get_encoding(); public GPGError.Error set_encoding(DataEncoding enc); } [CCode (cname = "gpgme_get_protocol_name")] public unowned string get_protocol_name(Protocol p); [CCode (cname = "gpgme_pubkey_algo_name")] public unowned string get_public_key_algorithm_name(PublicKeyAlgorithm algo); [CCode (cname = "gpgme_hash_algo_name")] public unowned string get_hash_algorithm_name(HashAlgorithm algo); [CCode (cname = "gpgme_passphrase_cb_t", has_target = false)] public delegate GPGError.Error passphrase_callback(void* hook, string uid_hint, string passphrase_info, bool prev_was_bad, int fd); [CCode (cname = "gpgme_check_version")] public unowned string check_version(string? required_version = null); [CCode (cname = "gpgme_engine_check_version")] public GPGError.Error engine_check_version(Protocol proto); [CCode (cname = "gpgme_get_engine_info")] public GPGError.Error get_engine_info(out EngineInfo? engine_info); [CCode (cname = "gpgme_strerror_r")] public int strerror_r(GPGError.Error err, uint8[] buf); [CCode (cname = "gpgme_strerror")] public unowned string strerror(GPGError.Error err); private void throw_if_error(GPGError.Error error) throws GLib.Error { if (error.code != GPGError.ErrorCode.NO_ERROR) { throw new GLib.Error(-1, error.code, "%s", error.to_string()); } } } dino-0.5.0/plugins/rtp/0000775000000000000000000000000014776241610013443 5ustar rootrootdino-0.5.0/plugins/rtp/meson.build0000664000000000000000000000705514776241610015614 0ustar rootrootdependencies = [ dep_crypto_vala, dep_dino, dep_gee, dep_glib, dep_gmodule, dep_gstreamer, dep_gstreamer_app, dep_gstreamer_audio, dep_gstreamer_rtp, dep_gstreamer_video, dep_gtk4, dep_m, dep_qlite, dep_xmpp_vala, ] sources = files( 'src/codec_util.vala', 'src/device.vala', 'src/gst_fixes.c', 'src/module.vala', 'src/plugin.vala', 'src/register_plugin.vala', 'src/stream.vala', 'src/video_widget.vala', ) c_args = [ '-DG_LOG_DOMAIN="rtp"', ] vala_args = [] if dep_webrtc_audio_processing.found() and get_option('plugin-rtp-webrtc-audio-processing').allowed() voice_dependencies = [ dep_gstreamer_audio, dep_webrtc_audio_processing, ] voice_sources = files( 'src/voice_processor_native.cpp', ) voice_c_args = c_args if dep_webrtc_audio_processing.version().version_compare('>=2.0') voice_c_args += ['-DWEBRTC2'] elif dep_webrtc_audio_processing.version().version_compare('>=1.0') voice_c_args += ['-DWEBRTC1'] else voice_c_args += ['-DWEBRTC0'] endif lib_rtp_voice_processor = static_library('rtp-voice-processor', voice_sources, c_args: voice_c_args, cpp_args: voice_c_args, dependencies: voice_dependencies, install: false) dep_rtp_voice_processor = declare_dependency(link_with: lib_rtp_voice_processor) vala_args += ['-D', 'WITH_VOICE_PROCESSOR'] dependencies += [dep_rtp_voice_processor] sources += files( 'src/voice_processor.vala', ) elif get_option('plugin-rtp-webrtc-audio-processing').enabled() error('No compatible webrtc-audio-processing found, but plugin-rtp-webrtc-audio-processing option set.') endif if dep_gstreamer_rtp.version() == 'unknown' or dep_gstreamer_rtp.version().version_compare('>=1.16') vala_args += ['-D', 'GST_1_16'] endif if dep_gstreamer_rtp.version() == 'unknown' or dep_gstreamer_rtp.version().version_compare('>=1.18') vala_args += ['-D', 'GST_1_18'] endif if dep_gstreamer_rtp.version() == 'unknown' or dep_gstreamer_rtp.version().version_compare('>=1.20') vala_args += ['-D', 'GST_1_20'] endif if meson.get_compiler('vala').version().version_compare('<0.56.1') vala_args += ['--vapidir', meson.current_source_dir() / 'vapi'] endif if get_option('plugin-rtp-h264').allowed() vala_args += ['-D', 'ENABLE_H264'] endif if get_option('plugin-rtp-msdk').allowed() vala_args += ['-D', 'ENABLE_MSDK'] endif if get_option('plugin-rtp-vaapi').allowed() vala_args += ['-D', 'ENABLE_VAAPI'] endif if get_option('plugin-rtp-vp9').allowed() vala_args += ['-D', 'ENABLE_VP9'] endif lib_rtp = shared_library('rtp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / get_option('plugindir'), install_rpath: default_install_rpath) dep_rtp = declare_dependency(link_with: lib_rtp, include_directories: include_directories('.')) summary('Voice/video calls (rtp)', dep_rtp, section: 'Plugins') if dep_rtp.found() summary('H264 codec', get_option('plugin-rtp-h264').allowed(), section: 'RTP configuration') summary('VP9 codec', get_option('plugin-rtp-vp9').allowed(), section: 'RTP configuration') summary('Intel MediaSDK', get_option('plugin-rtp-msdk').allowed(), section: 'RTP configuration') summary('Video Acceleration API', get_option('plugin-rtp-vaapi').allowed(), section: 'RTP configuration') summary('Voice preprocessing', dep_webrtc_audio_processing.found(), section: 'RTP configuration') endif dino-0.5.0/plugins/rtp/src/0000775000000000000000000000000014776241610014232 5ustar rootrootdino-0.5.0/plugins/rtp/src/codec_util.vala0000664000000000000000000004352314776241610017220 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.CodecUtil { private Set supported_elements = new HashSet(); private Set unsupported_elements = new HashSet(); public static Gst.Caps get_caps(string media, JingleRtp.PayloadType payload_type, bool incoming) { Gst.Caps caps = new Gst.Caps.simple("application/x-rtp", "media", typeof(string), media, "payload", typeof(int), payload_type.id); //"channels", typeof(int), payloadType.channels, //"max-ptime", typeof(int), payloadType.maxptime); unowned Gst.Structure s = caps.get_structure(0); if (payload_type.clockrate != 0) { s.set("clock-rate", typeof(int), payload_type.clockrate); } if (payload_type.name != null) { s.set("encoding-name", typeof(string), payload_type.name.up()); } if (incoming) { foreach (JingleRtp.RtcpFeedback rtcp_fb in payload_type.rtcp_fbs) { if (rtcp_fb.subtype == null) { s.set(@"rtcp-fb-$(rtcp_fb.type_)", typeof(bool), true); } else { s.set(@"rtcp-fb-$(rtcp_fb.type_)-$(rtcp_fb.subtype)", typeof(bool), true); } } } return caps; } public static string? get_codec_from_payload(string media, JingleRtp.PayloadType payload_type) { if (payload_type.name != null) return payload_type.name.down(); if (media == "audio") { switch (payload_type.id) { case 0: return "pcmu"; case 8: return "pcma"; } } return null; } public static string? get_media_type_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_media_type(media, get_codec_from_payload(media, payload_type)); } public static string? get_media_type(string media, string? codec) { if (codec == null) return null; if (media == "audio") { switch (codec) { case "pcma": return "audio/x-alaw"; case "pcmu": return "audio/x-mulaw"; } } return @"$media/x-$codec"; } public static string? get_rtp_pay_element_name_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_pay_candidate(media, get_codec_from_payload(media, payload_type)); } public static string? get_pay_candidate(string media, string? codec) { if (codec == null) return null; return @"rtp$(codec)pay"; } public static string? get_rtp_depay_element_name_from_payload(string media, JingleRtp.PayloadType payload_type) { return get_depay_candidate(media, get_codec_from_payload(media, payload_type)); } public static string? get_depay_candidate(string media, string? codec) { if (codec == null) return null; return @"rtp$(codec)depay"; } public static string[] get_encode_candidates(string media, string? codec) { if (codec == null) return new string[0]; if (media == "audio") { switch (codec) { case "opus": return new string[] {"opusenc"}; case "speex": return new string[] {"speexenc"}; case "pcma": return new string[] {"alawenc"}; case "pcmu": return new string[] {"mulawenc"}; case "g722": return new string[] {"avenc_g722"}; } } else if (media == "video") { switch (codec) { case "h264": return new string[] { #if ENABLE_MSDK "msdkh264enc", #endif #if ENABLE_VAAPI "vaapih264enc", #endif "x264enc" }; case "vp9": return new string[] { #if ENABLE_MSDK "msdkvp9enc", #endif #if ENABLE_VAAPI "vaapivp9enc", #endif "vp9enc" }; case "vp8": return new string[] { #if ENABLE_MSDK "msdkvp8enc", #endif #if ENABLE_VAAPI "vaapivp8enc", #endif "vp8enc" }; } } return new string[0]; } public static string[] get_decode_candidates(string media, string? codec) { if (codec == null) return new string[0]; if (media == "audio") { switch (codec) { case "opus": return new string[] {"opusdec"}; case "speex": return new string[] {"speexdec"}; case "pcma": return new string[] {"alawdec"}; case "pcmu": return new string[] {"mulawdec"}; case "g722": return new string[] {"avdec_g722"}; } } else if (media == "video") { switch (codec) { case "h264": return new string[] { #if ENABLE_MSDK "msdkh264dec", #endif #if ENABLE_VAAPI "vaapih264dec", #endif null }; case "vp9": return new string[] { #if ENABLE_MSDK "msdkvp9dec", #endif #if ENABLE_VAAPI "vaapivp9dec", #endif "vp9dec" }; case "vp8": return new string[] { #if ENABLE_MSDK "msdkvp8dec", #endif #if ENABLE_VAAPI "vaapivp8dec", #endif "vp8dec" }; } } return new string[0]; } public static string? get_encode_prefix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { if (encode == "msdkh264enc") return "capsfilter caps=video/x-raw,format=NV12 ! "; if (encode == "vaapih264enc") return "capsfilter caps=video/x-raw,format=NV12 ! "; return null; } public static string? get_encode_args(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { // H264 if (encode == "msdkh264enc") return @" rate-control=vbr"; if (encode == "vaapih264enc") return @" rate-control=vbr"; if (encode == "x264enc") return @" byte-stream=1 speed-preset=ultrafast tune=zerolatency bframes=0 cabac=false dct8x8=false"; // VP8 if (encode == "vaapivp8enc" || encode == "msdkvp8enc") return " rate-control=vbr target-percentage=90"; if (encode == "vp8enc") return " deadline=1 error-resilient=3 lag-in-frames=0 resize-allowed=true threads=8 dropframe-threshold=30 end-usage=vbr cpu-used=4"; // VP9 if (encode == "msdkvp9enc" || encode == "vaapivp9enc") return " rate-control=vbr target-percentage=90"; if (encode == "vp9enc") return " deadline=1 error-resilient=3 lag-in-frames=0 resize-allowed=true threads=8 dropframe-threshold=30 end-usage=vbr cpu-used=4"; // OPUS if (encode == "opusenc") { if (payload_type != null && payload_type.parameters.has("useinbandfec", "1")) return " audio-type=voice inband-fec=true"; return " audio-type=voice"; } return null; } public static string? get_encode_suffix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { // H264 if (media == "video" && codec == "h264") return " ! capsfilter caps=video/x-h264,profile=constrained-baseline ! h264parse"; if (media == "video" && codec == "vp8" && encode == "vp8enc") return " ! capsfilter caps=video/x-vp8,profile=(string)1"; return null; } public uint update_bitrate(string media, JingleRtp.PayloadType payload_type, Gst.Element encode_element, uint bitrate) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return 0; string? codec = get_codec_from_payload(media, payload_type); string? encode_name = get_encode_element_name(media, codec); if (encode_name == null) return 0; Gst.Element encode = encode_bin.get_by_name(@"$(encode_bin.name)_encode"); switch (encode_name) { case "msdkh264enc": case "vaapih264enc": case "x264enc": case "msdkvp9enc": case "vaapivp9enc": case "msdkvp8enc": case "vaapivp8enc": bitrate = uint.min(2048000, bitrate); encode.set("bitrate", bitrate); return bitrate; case "vp9enc": case "vp8enc": bitrate = uint.min(2147483, bitrate); encode.set("target-bitrate", bitrate * 1024); return bitrate; } return 0; } public void update_rescale_caps(Gst.Element encode_element, Gst.Caps caps) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return; Gst.Element rescale_caps = encode_bin.get_by_name(@"$(encode_bin.name)_rescale_caps"); rescale_caps.set("caps", caps); } public Gst.Caps? get_rescale_caps(Gst.Element encode_element) { Gst.Bin? encode_bin = encode_element as Gst.Bin; if (encode_bin == null) return null; Gst.Element rescale_caps = encode_bin.get_by_name(@"$(encode_bin.name)_rescale_caps"); Gst.Caps caps; rescale_caps.get("caps", out caps); return caps; } public static string? get_decode_prefix(string media, string codec, string decode, JingleRtp.PayloadType? payload_type) { return null; } public static string? get_decode_args(string media, string codec, string decode, JingleRtp.PayloadType? payload_type) { if (decode == "opusdec" && payload_type != null && payload_type.parameters.has("useinbandfec", "1")) return " use-inband-fec=true"; if (decode == "vaapivp9dec" || decode == "vaapivp8dec" || decode == "vaapih264dec") return " max-errors=100"; if (decode == "vp8dec" || decode == "vp9dec") return " threads=8"; return null; } public static string? get_decode_suffix(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { return null; } public static string? get_depay_args(string media, string codec, string encode, JingleRtp.PayloadType? payload_type) { #if GST_1_18 if (codec == "vp8") return " wait-for-keyframe=true"; #endif return null; } public bool is_element_supported(string? element_name) { if (element_name == null) return false; if (unsupported_elements.contains(element_name)) return false; if (supported_elements.contains(element_name)) return true; var test_element = Gst.ElementFactory.make(element_name, @"test-$element_name"); if (test_element != null) { supported_elements.add(element_name); return true; } else { warning("%s is not installed or supported on this system", element_name); unsupported_elements.add(element_name); return false; } } public string? get_encode_element_name(string media, string? codec) { if (get_pay_element_name(media, codec) == null) return null; foreach (string candidate in get_encode_candidates(media, codec)) { if (is_element_supported(candidate)) return candidate; } return null; } public string? get_pay_element_name(string media, string? codec) { string? candidate = get_pay_candidate(media, codec); if (candidate != null && is_element_supported(candidate)) return candidate; return null; } public string? get_decode_element_name(string media, string? codec) { if (get_depay_element_name(media, codec) == null) return null; foreach (string candidate in get_decode_candidates(media, codec)) { if (is_element_supported(candidate)) return candidate; } return null; } public string? get_depay_element_name(string media, string? codec) { string? candidate = get_depay_candidate(media, codec); if (candidate != null && is_element_supported(candidate)) return candidate; return null; } public void mark_element_unsupported(string element_name) { unsupported_elements.add(element_name); } public string? get_decode_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode-$codec-$(Random.next_int())"; string? depay = get_depay_element_name(media, codec); string? decode = element_name ?? get_decode_element_name(media, codec); if (depay == null || decode == null) return null; string decode_prefix = get_decode_prefix(media, codec, decode, payload_type) ?? ""; string decode_args = get_decode_args(media, codec, decode, payload_type) ?? ""; string decode_suffix = get_decode_suffix(media, codec, decode, payload_type) ?? ""; string depay_args = get_depay_args(media, codec, decode, payload_type) ?? ""; string resample = media == "audio" ? @" ! audioresample name=$(base_name)_resample" : ""; return @"queue ! $depay$depay_args name=$(base_name)_rtp_depay ! $decode_prefix$decode$decode_args name=$(base_name)_$(codec)_decode$decode_suffix ! $(media)convert name=$(base_name)_convert$resample"; } public Gst.Element? get_decode_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"decode_$(codec)_$(Random.next_int())"; string? desc = get_decode_bin_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to decode %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public string? get_encode_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { string? desc1 = get_encode_bin_without_payloader_description(media, codec, payload_type, element_name, name); string? desc2 = get_payloader_bin_description(media, codec, payload_type, name); return @"$desc1 ! $desc2"; } public string? get_payloader_bin_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? pay = get_pay_element_name(media, codec); if (pay == null) return null; return @"$pay pt=$(payload_type != null ? payload_type.id : 96) name=$(base_name)_rtp_pay"; } public string? get_encode_bin_without_payloader_description(string media, string? codec, JingleRtp.PayloadType? payload_type, string? element_name = null, string? name = null) { if (codec == null) return null; string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? encode = element_name ?? get_encode_element_name(media, codec); if (encode == null) return null; string encode_prefix = get_encode_prefix(media, codec, encode, payload_type) ?? ""; string encode_args = get_encode_args(media, codec, encode, payload_type) ?? ""; string encode_suffix = get_encode_suffix(media, codec, encode, payload_type) ?? ""; string rescale = media == "audio" ? @" ! audioresample name=$(base_name)_resample" : @" ! videoscale name=$(base_name)_rescale ! capsfilter name=$(base_name)_rescale_caps"; return @"$(media)convert name=$(base_name)_convert$rescale ! queue ! $encode_prefix$encode$encode_args name=$(base_name)_encode$encode_suffix"; } public Gst.Element? get_encode_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_encode_bin_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to encode %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public Gst.Element? get_encode_bin_without_payloader(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_encode_bin_without_payloader_description(media, codec, payload_type, null, base_name); if (desc == null) return null; debug("Pipeline to encode %s %s without payloader: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } public Gst.Element? get_payloader_bin(string media, JingleRtp.PayloadType payload_type, string? name = null) { string? codec = get_codec_from_payload(media, payload_type); string base_name = name ?? @"encode_$(codec)_$(Random.next_int())"; string? desc = get_payloader_bin_description(media, codec, payload_type, base_name); if (desc == null) return null; debug("Pipeline to payload %s %s: %s", media, codec, desc); Gst.Element bin = Gst.parse_bin_from_description(desc, true); bin.name = name; return bin; } } dino-0.5.0/plugins/rtp/src/device.vala0000664000000000000000000006311014776241610016337 0ustar rootrootusing Xmpp.Xep.JingleRtp; using Gee; public enum Dino.Plugins.Rtp.DeviceProtocol { OTHER, PIPEWIRE, V4L2, PULSEAUDIO, ALSA } public class Dino.Plugins.Rtp.Device : MediaDevice, Object { private const int[] common_widths = {320, 360, 400, 480, 640, 960, 1280, 1920, 2560, 3840}; public Plugin plugin { get; private set; } public CodecUtil codec_util { get { return plugin.codec_util; } } public Gst.Device device { get; private set; } public string id { owned get { return device_name; }} public string display_name { owned get { return device_display_name; }} public string? detail_name { owned get { if (device.properties == null) return null; if (device.properties.has_field("alsa.card_name")) return device.properties.get_string("alsa.card_name"); if (device.properties.has_field("alsa.name")) return device.properties.get_string("alsa.name"); if (device.properties.has_field("alsa.id")) return device.properties.get_string("alsa.id"); if (device.properties.has_field("api.v4l2.cap.card")) return device.properties.get_string("api.v4l2.cap.card"); return null; }} public string? media { owned get { if (device.has_classes("Audio")) { return "audio"; } else if (device.has_classes("Video")) { return "video"; } else { return null; } }} public bool incoming { get { return is_sink; } } public Gst.Pipeline pipe { get { return plugin.pipe; }} public bool is_source { get { return device.has_classes("Source"); }} public bool is_sink { get { return device.has_classes("Sink"); }} public bool is_monitor { get { return (device.properties != null && device.properties.get_string("device.class") == "monitor") || (protocol == DeviceProtocol.PIPEWIRE && device.has_classes("Stream")); } } public bool is_default { get { if (device.properties == null) return false; bool ret; device.properties.get_boolean("is-default", out ret); return ret; }} public DeviceProtocol protocol { get { if (device.properties == null) return DeviceProtocol.OTHER; if (device.properties.has_name("pulse-proplist")) return DeviceProtocol.PULSEAUDIO; if (device.properties.has_name("pipewire-proplist")) return DeviceProtocol.PIPEWIRE; if (device.properties.has_name("v4l2deviceprovider")) return DeviceProtocol.V4L2; return DeviceProtocol.OTHER; }} private string device_name; private string device_display_name; private Gst.Caps device_caps; private Gst.Element element; private Gst.Element tee; private Gst.Element dsp; private Gst.Base.Aggregator mixer; private Gst.Element filter; private int links; // Codecs private Gee.Map codecs = new HashMap(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map codec_tees = new HashMap(PayloadType.hash_func, PayloadType.equals_func); // Payloaders private Gee.Map> payloaders = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map> payloader_tees = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private Gee.Map> payloader_links = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); // Bitrate private Gee.Map> codec_bitrates = new HashMap>(PayloadType.hash_func, PayloadType.equals_func); private class CodecBitrate { public uint bitrate; public int64 timestamp; public CodecBitrate(uint bitrate) { this.bitrate = bitrate; this.timestamp = get_monotonic_time(); } } public Device(Plugin plugin, Gst.Device device) { this.plugin = plugin; update(device); } public bool matches(Gst.Device device) { if (this.device.name == device.name) return true; return false; } public void update(Gst.Device device) { this.device = device; this.device_name = device.name; this.device_display_name = device.display_name; } public Gst.Element? link_sink() { if (!is_sink) return null; if (element == null) create(); links++; if (mixer != null) { Gst.Element rate = Gst.ElementFactory.make("audiorate", @"$(id)_rate_$(Random.next_int())"); pipe.add(rate); rate.link(mixer); return rate; } if (media == "audio") return filter; return element; } public Gst.Element? link_source(PayloadType? payload_type = null, uint ssrc = 0, int seqnum_offset = -1, uint32 timestamp_offset = 0) { if (!is_source) return null; if (element == null) create(); links++; if (payload_type != null && ssrc != 0 && tee != null) { bool new_codec = false; string? codec = CodecUtil.get_codec_from_payload(media, payload_type); if (!codecs.has_key(payload_type)) { codecs[payload_type] = codec_util.get_encode_bin_without_payloader(media, payload_type, @"$(id)_$(codec)_encoder"); pipe.add(codecs[payload_type]); new_codec = true; } if (!codec_tees.has_key(payload_type)) { codec_tees[payload_type] = Gst.ElementFactory.make("tee", @"$(id)_$(codec)_tee"); codec_tees[payload_type].@set("allow-not-linked", true); pipe.add(codec_tees[payload_type]); codecs[payload_type].link(codec_tees[payload_type]); } if (!payloaders.has_key(payload_type)) { payloaders[payload_type] = new HashMap(); } if (!payloaders[payload_type].has_key(ssrc)) { payloaders[payload_type][ssrc] = codec_util.get_payloader_bin(media, payload_type, @"$(id)_$(codec)_$(ssrc)"); var payload = (Gst.RTP.BasePayload) ((Gst.Bin) payloaders[payload_type][ssrc]).get_by_name(@"$(id)_$(codec)_$(ssrc)_rtp_pay"); payload.ssrc = ssrc; payload.seqnum_offset = seqnum_offset; if (timestamp_offset != 0) { payload.timestamp_offset = timestamp_offset; } pipe.add(payloaders[payload_type][ssrc]); codec_tees[payload_type].link(payloaders[payload_type][ssrc]); debug("Payload for %s with %s using ssrc %u, seqnum_offset %u, timestamp_offset %u", media, codec, ssrc, seqnum_offset, timestamp_offset); } if (!payloader_tees.has_key(payload_type)) { payloader_tees[payload_type] = new HashMap(); } if (!payloader_tees[payload_type].has_key(ssrc)) { payloader_tees[payload_type][ssrc] = Gst.ElementFactory.make("tee", @"$(id)_$(codec)_$(ssrc)_tee"); payloader_tees[payload_type][ssrc].@set("allow-not-linked", true); pipe.add(payloader_tees[payload_type][ssrc]); payloaders[payload_type][ssrc].link(payloader_tees[payload_type][ssrc]); } if (!payloader_links.has_key(payload_type)) { payloader_links[payload_type] = new HashMap(); } if (!payloader_links[payload_type].has_key(ssrc)) { payloader_links[payload_type][ssrc] = 1; } else { payloader_links[payload_type][ssrc] = payloader_links[payload_type][ssrc] + 1; } if (new_codec) { tee.link(codecs[payload_type]); } return payloader_tees[payload_type][ssrc]; } if (tee != null) return tee; return element; } private static double get_target_bitrate(Gst.Caps caps) { if (caps == null || caps.get_size() == 0) return uint.MAX; unowned Gst.Structure? that = caps.get_structure(0); int num = 0, den = 0, width = 0, height = 0; if (!that.has_field("width") || !that.get_int("width", out width)) return uint.MAX; if (!that.has_field("height") || !that.get_int("height", out height)) return uint.MAX; if (!that.has_field("framerate")) return uint.MAX; Value framerate = that.get_value("framerate"); if (framerate.type() != typeof(Gst.Fraction)) return uint.MAX; num = Gst.Value.get_fraction_numerator(framerate); den = Gst.Value.get_fraction_denominator(framerate); double pxs = ((double)num/(double)den) * (double)width * (double)height; double br = Math.sqrt(Math.sqrt(pxs)) * 100.0 - 3700.0; if (br < 128.0) return 128.0; return br; } private Gst.Caps get_active_caps(PayloadType payload_type) { return codec_util.get_rescale_caps(codecs[payload_type]) ?? device_caps; } private void apply_caps(PayloadType payload_type, Gst.Caps caps) { plugin.pause(); debug("Set scaled caps to %s", caps.to_string()); codec_util.update_rescale_caps(codecs[payload_type], caps); plugin.unpause(); } private void apply_width(PayloadType payload_type, int new_width, uint bitrate) { int device_caps_width, device_caps_height, active_caps_width, device_caps_framerate_num, device_caps_framerate_den; device_caps.get_structure(0).get_int("width", out device_caps_width); device_caps.get_structure(0).get_int("height", out device_caps_height); device_caps.get_structure(0).get_fraction("framerate", out device_caps_framerate_num, out device_caps_framerate_den); Gst.Caps active_caps = get_active_caps(payload_type); if (active_caps != null && active_caps.get_size() > 0) { active_caps.get_structure(0).get_int("width", out active_caps_width); } else { active_caps_width = device_caps_width; } if (new_width == active_caps_width) return; int new_height = device_caps_height * new_width / device_caps_width; Gst.Caps new_caps; if (device_caps_framerate_den != 0) { new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, "framerate", typeof(Gst.Fraction), device_caps_framerate_num, device_caps_framerate_den, null); } else { new_caps = new Gst.Caps.simple("video/x-raw", "width", typeof(int), new_width, "height", typeof(int), new_height, null); } double required_bitrate = get_target_bitrate(new_caps); debug("Changing resolution width from %d to %d (requires bitrate %f, current target is %u)", active_caps_width, new_width, required_bitrate, bitrate); if (bitrate < required_bitrate && new_width > active_caps_width) return; apply_caps(payload_type, new_caps); } public void update_bitrate(PayloadType payload_type, uint bitrate) { if (codecs.has_key(payload_type)) { lock(codec_bitrates); if (!codec_bitrates.has_key(payload_type)) { codec_bitrates[payload_type] = new ArrayList(); } codec_bitrates[payload_type].add(new CodecBitrate(bitrate)); var remove = new ArrayList(); foreach (CodecBitrate rate in codec_bitrates[payload_type]) { if (rate.timestamp < get_monotonic_time() - 5000000L) { remove.add(rate); continue; } if (rate.bitrate < bitrate) { bitrate = rate.bitrate; } } codec_bitrates[payload_type].remove_all(remove); if (media == "video") { if (bitrate < 128) bitrate = 128; Gst.Caps active_caps = get_active_caps(payload_type); double max_bitrate = get_target_bitrate(device_caps) * 2; double current_target_bitrate = get_target_bitrate(active_caps); int device_caps_width, active_caps_width; device_caps.get_structure(0).get_int("width", out device_caps_width); if (active_caps != null && active_caps.get_size() > 0) { active_caps.get_structure(0).get_int("width", out active_caps_width); } else { active_caps_width = device_caps_width; } if (bitrate < 0.75 * current_target_bitrate && active_caps_width > common_widths[0]) { // Lower video resolution int i = 1; for(; i < common_widths.length && common_widths[i] < active_caps_width; i++);if (common_widths[i] != active_caps_width) { debug("Decrease resolution to ensure target bitrate (%u) is in reach (current resolution target bitrate is %f)", bitrate, current_target_bitrate); } apply_width(payload_type, common_widths[i-1], bitrate); } else if (bitrate > 2 * current_target_bitrate && active_caps_width < device_caps_width) { // Higher video resolution int i = 0; for(; i < common_widths.length && common_widths[i] <= active_caps_width; i++); if (common_widths[i] != active_caps_width) { debug("Increase resolution to make use of available bandwidth of target bitrate (%u) (current resolution target bitrate is %f)", bitrate, current_target_bitrate); } if (common_widths[i] > device_caps_width) { // We never scale up, so just stick with what the device gives apply_width(payload_type, device_caps_width, bitrate); } else if (common_widths[i] != active_caps_width) { apply_width(payload_type, common_widths[i], bitrate); } } if (bitrate > max_bitrate) bitrate = (uint) max_bitrate; } codec_util.update_bitrate(media, payload_type, codecs[payload_type], bitrate); unlock(codec_bitrates); } } public void unlink(Gst.Element? link = null) { if (links <= 0) { critical("Link count below zero."); return; } if (link != null && is_source && tee != null) { PayloadType payload_type = payloader_tees.first_match((entry) => entry.value.any_match((entry) => entry.value == link)).key; uint ssrc = payloader_tees[payload_type].first_match((entry) => entry.value == link).key; payloader_links[payload_type][ssrc] = payloader_links[payload_type][ssrc] - 1; if (payloader_links[payload_type][ssrc] == 0) { plugin.pause(); codec_tees[payload_type].unlink(payloaders[payload_type][ssrc]); payloaders[payload_type][ssrc].set_locked_state(true); payloaders[payload_type][ssrc].set_state(Gst.State.NULL); payloaders[payload_type][ssrc].unlink(payloader_tees[payload_type][ssrc]); pipe.remove(payloaders[payload_type][ssrc]); payloaders[payload_type].unset(ssrc); payloader_tees[payload_type][ssrc].set_locked_state(true); payloader_tees[payload_type][ssrc].set_state(Gst.State.NULL); pipe.remove(payloader_tees[payload_type][ssrc]); payloader_tees[payload_type].unset(ssrc); payloader_links[payload_type].unset(ssrc); plugin.unpause(); } if (payloader_links[payload_type].size == 0) { plugin.pause(); tee.unlink(codecs[payload_type]); codecs[payload_type].set_locked_state(true); codecs[payload_type].set_state(Gst.State.NULL); codecs[payload_type].unlink(codec_tees[payload_type]); pipe.remove(codecs[payload_type]); codecs.unset(payload_type); codec_tees[payload_type].set_locked_state(true); codec_tees[payload_type].set_state(Gst.State.NULL); pipe.remove(codec_tees[payload_type]); codec_tees.unset(payload_type); payloaders.unset(payload_type); payloader_tees.unset(payload_type); payloader_links.unset(payload_type); plugin.unpause(); } } if (link != null && is_sink && mixer != null) { plugin.pause(); link.set_locked_state(true); Gst.Base.AggregatorPad mixer_sink_pad = (Gst.Base.AggregatorPad) link.get_static_pad("src").get_peer(); link.get_static_pad("src").unlink(mixer_sink_pad); mixer_sink_pad.set_active(false); link.set_state(Gst.State.NULL); pipe.remove(link); mixer.release_request_pad(mixer_sink_pad); plugin.unpause(); } links--; if (links == 0) { destroy(); } } private Gst.Caps get_best_caps() { if (media == "audio") { return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1"); } else if (media == "video" && device.caps.get_size() > 0) { int best_index = -1; Value? best_fraction = null; int best_fps = 0; int best_width = 0; int best_height = 0; for (int i = 0; i < device.caps.get_size(); i++) { unowned Gst.Structure? that = device.caps.get_structure(i); Value? best_fraction_now = null; if (!that.has_name("video/x-raw")) continue; int num = 0, den = 0, width = 0, height = 0; if (!that.has_field("framerate")) continue; Value framerate = that.get_value("framerate"); if (framerate.type() == typeof(Gst.Fraction)) { num = Gst.Value.get_fraction_numerator(framerate); den = Gst.Value.get_fraction_denominator(framerate); } else if (framerate.type() == typeof(Gst.ValueList)) { for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { Value fraction = Gst.ValueList.get_value(framerate, j); int in_num = Gst.Value.get_fraction_numerator(fraction); int in_den = Gst.Value.get_fraction_denominator(fraction); int fps = den > 0 ? (num/den) : 0; int in_fps = in_den > 0 ? (in_num/in_den) : 0; if (in_fps > fps) { best_fraction_now = fraction; num = in_num; den = in_den; } } } else { debug("Unknown type for framerate: %s", framerate.type_name()); } if (den == 0) continue; if (!that.has_field("width") || !that.get_int("width", out width)) continue; if (!that.has_field("height") || !that.get_int("height", out height)) continue; int fps = num/den; if (best_fps < fps || best_fps == fps && best_width < width || best_fps == fps && best_width == width && best_height < height) { best_fps = fps; best_width = width; best_height = height; best_index = i; best_fraction = best_fraction_now; } } if (best_index == -1) { // No caps in first round, try without framerate for (int i = 0; i < device.caps.get_size(); i++) { unowned Gst.Structure? that = device.caps.get_structure(i); if (!that.has_name("video/x-raw")) continue; int width = 0, height = 0; if (!that.has_field("width") || !that.get_int("width", out width)) continue; if (!that.has_field("height") || !that.get_int("height", out height)) continue; if (best_width < width || best_width == width && best_height < height) { best_width = width; best_height = height; best_index = i; } } } Gst.Caps res = caps_copy_nth(device.caps, best_index); unowned Gst.Structure? that = res.get_structure(0); Value? framerate = that.get_value("framerate"); if (framerate != null && framerate.type() == typeof(Gst.ValueList) && best_fraction != null) { that.set_value("framerate", best_fraction); } debug("Selected caps %s", res.to_string()); return res; } else if (device.caps.get_size() > 0) { return caps_copy_nth(device.caps, 0); } else { return new Gst.Caps.any(); } } // Backport from gst_caps_copy_nth added in GStreamer 1.16 private static Gst.Caps caps_copy_nth(Gst.Caps source, uint index) { Gst.Caps target = new Gst.Caps.empty(); target.flags = source.flags; target.append_structure_full(source.get_structure(index).copy(), source.get_features(index).copy()); return target; } private void create() { debug("Creating device %s", id); plugin.pause(); element = device.create_element(id); if (is_sink) { element.@set("async", false); element.@set("sync", false); } pipe.add(element); device_caps = get_best_caps(); if (is_source) { element.@set("do-timestamp", true); filter = Gst.ElementFactory.make("capsfilter", @"caps_filter_$id"); filter.@set("caps", device_caps); pipe.add(filter); element.link(filter); #if WITH_VOICE_PROCESSOR if (media == "audio" && plugin.echoprobe != null) { dsp = new VoiceProcessor(plugin.echoprobe as EchoProbe, element as Gst.Audio.StreamVolume); dsp.name = @"dsp_$id"; pipe.add(dsp); filter.link(dsp); } #endif tee = Gst.ElementFactory.make("tee", @"tee_$id"); tee.@set("allow-not-linked", true); pipe.add(tee); (dsp ?? filter).link(tee); } if (is_sink && media == "audio") { mixer = (Gst.Base.Aggregator) Gst.ElementFactory.make("audiomixer", @"mixer_$id"); pipe.add(mixer); if (plugin.echoprobe != null && !plugin.echoprobe.get_static_pad("src").is_linked()) { mixer.link(plugin.echoprobe); plugin.echoprobe.link(element); } else { filter = Gst.ElementFactory.make("capsfilter", @"caps_filter_$id"); filter.@set("caps", device_caps); pipe.add(filter); mixer.link(filter); filter.link(element); } } plugin.unpause(); } private void destroy() { if (is_sink) { if (mixer != null) { int linked_sink_pads = 0; mixer.foreach_sink_pad((_, pad) => { if (pad.is_linked()) linked_sink_pads++; return true; }); if (linked_sink_pads > 0) { warning("%s-mixer still has %i sink pads while being destroyed", id, linked_sink_pads); } mixer.unlink(plugin.echoprobe ?? element); } if (filter != null) { filter.set_locked_state(true); filter.set_state(Gst.State.NULL); filter.unlink(element); pipe.remove(filter); filter = null; } if (plugin.echoprobe != null) { plugin.echoprobe.unlink(element); } } element.set_locked_state(true); element.set_state(Gst.State.NULL); if (filter != null) element.unlink(filter); else if (is_source) element.unlink(tee); pipe.remove(element); element = null; if (mixer != null) { mixer.set_locked_state(true); mixer.set_state(Gst.State.NULL); pipe.remove(mixer); mixer = null; } if (is_source) { if (filter != null) { filter.set_locked_state(true); filter.set_state(Gst.State.NULL); filter.unlink(dsp ?? tee); pipe.remove(filter); filter = null; } if (dsp != null) { dsp.set_locked_state(true); dsp.set_state(Gst.State.NULL); dsp.unlink(tee); pipe.remove(dsp); dsp = null; } if (tee != null) { int linked_src_pads = 0; tee.foreach_src_pad((_, pad) => { if (pad.is_linked()) linked_src_pads++; return true; }); if (linked_src_pads != 0) { warning("%s-tee still has %d src pads while being destroyed", id, linked_src_pads); } tee.set_locked_state(true); tee.set_state(Gst.State.NULL); pipe.remove(tee); tee = null; } } debug("Destroyed device %s", id); } } dino-0.5.0/plugins/rtp/src/gst_fixes.c0000664000000000000000000000043414776241610016372 0ustar rootroot#include GstVideoInfo *gst_video_frame_get_video_info(GstVideoFrame *frame) { return &frame->info; } void *gst_video_frame_get_data(GstVideoFrame *frame, size_t* length) { *length = frame->info.height * frame->info.stride[0]; return frame->data[0]; }dino-0.5.0/plugins/rtp/src/module.vala0000664000000000000000000002415414776241610016372 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.Module : JingleRtp.Module { private Set supported_codecs = new HashSet(); private Set unsupported_codecs = new HashSet(); public Plugin plugin { get; private set; } public CodecUtil codec_util { get { return plugin.codec_util; }} public Module(Plugin plugin) { base(); this.plugin = plugin; } private async bool pipeline_works(string media, string element_desc) { var supported = false; string pipeline_desc = @"$(media)testsrc is-live=true ! $element_desc ! appsink name=output"; try { var pipeline = Gst.parse_launch(pipeline_desc); var output = ((Gst.Bin) pipeline).get_by_name("output") as Gst.App.Sink; SourceFunc callback = pipeline_works.callback; var finished = false; output.emit_signals = true; output.new_sample.connect(() => { if (!finished) { finished = true; supported = true; Idle.add(() => { callback(); return Source.REMOVE; }); } return Gst.FlowReturn.EOS; }); pipeline.bus.add_watch(Priority.DEFAULT, (_, message) => { if (message.type == Gst.MessageType.ERROR && !finished) { Error e; string d; message.parse_error(out e, out d); debug("pipeline [%s] failed: %s", pipeline_desc, e.message); debug(d); finished = true; callback(); } return true; }); Timeout.add(2000, () => { if (!finished) { finished = true; callback(); } return Source.REMOVE; }); pipeline.set_state(Gst.State.PLAYING); yield; pipeline.set_state(Gst.State.NULL); } catch (Error e) { debug("pipeline [%s] failed: %s", pipeline_desc, e.message); } return supported; } public override async bool is_payload_supported(string media, JingleRtp.PayloadType payload_type) { string? codec = CodecUtil.get_codec_from_payload(media, payload_type); if (codec == null) return false; if (unsupported_codecs.contains(codec)) return false; if (supported_codecs.contains(codec)) return true; string? encode_element = codec_util.get_encode_element_name(media, codec); string? decode_element = codec_util.get_decode_element_name(media, codec); if (encode_element == null || decode_element == null) { warning("No suitable encoder or decoder found for %s", codec); unsupported_codecs.add(codec); return false; } string encode_bin = codec_util.get_encode_bin_description(media, codec, null, encode_element); while (!(yield pipeline_works(media, encode_bin))) { debug("%s not suited for encoding %s", encode_element, codec); codec_util.mark_element_unsupported(encode_element); encode_element = codec_util.get_encode_element_name(media, codec); if (encode_element == null) { warning("No suitable encoder found for %s", codec); unsupported_codecs.add(codec); return false; } encode_bin = codec_util.get_encode_bin_description(media, codec, null, encode_element); } debug("using %s to encode %s", encode_element, codec); string decode_bin = codec_util.get_decode_bin_description(media, codec, null, decode_element); while (!(yield pipeline_works(media, @"$encode_bin ! $decode_bin"))) { debug("%s not suited for decoding %s", decode_element, codec); codec_util.mark_element_unsupported(decode_element); decode_element = codec_util.get_decode_element_name(media, codec); if (decode_element == null) { warning("No suitable decoder found for %s", codec); unsupported_codecs.add(codec); return false; } decode_bin = codec_util.get_decode_bin_description(media, codec, null, decode_element); } debug("using %s to decode %s", decode_element, codec); supported_codecs.add(codec); return true; } public override bool is_header_extension_supported(string media, JingleRtp.HeaderExtension ext) { if (media == "video" && ext.uri == "urn:3gpp:video-orientation") return true; return false; } public override Gee.List get_suggested_header_extensions(string media) { Gee.List exts = new ArrayList(); if (media == "video") { exts.add(new JingleRtp.HeaderExtension(1, "urn:3gpp:video-orientation")); } return exts; } public async void add_if_supported(Gee.List list, string media, JingleRtp.PayloadType payload_type) { if (yield is_payload_supported(media, payload_type)) { list.add(payload_type); } } public override async Gee.List get_supported_payloads(string media) { Gee.List list = new ArrayList(JingleRtp.PayloadType.equals_func); if (media == "audio") { var opus = new JingleRtp.PayloadType() { channels = 1, clockrate = 48000, name = "opus", id = 111, channels = 2 }; opus.parameters["useinbandfec"] = "1"; var speex32 = new JingleRtp.PayloadType() { channels = 1, clockrate = 32000, name = "speex", id = 112 }; var speex16 = new JingleRtp.PayloadType() { channels = 1, clockrate = 16000, name = "speex", id = 113 }; var speex8 = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "speex", id = 114 }; var g722 = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "G722", id = 9 }; var pcmu = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "PCMU", id = 0 }; var pcma = new JingleRtp.PayloadType() { channels = 1, clockrate = 8000, name = "PCMA", id = 8 }; yield add_if_supported(list, media, opus); yield add_if_supported(list, media, speex32); yield add_if_supported(list, media, speex16); yield add_if_supported(list, media, speex8); yield add_if_supported(list, media, g722); yield add_if_supported(list, media, pcmu); yield add_if_supported(list, media, pcma); } else if (media == "video") { var rtcp_fbs = new ArrayList(); rtcp_fbs.add(new JingleRtp.RtcpFeedback("goog-remb")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("ccm", "fir")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("nack")); rtcp_fbs.add(new JingleRtp.RtcpFeedback("nack", "pli")); #if ENABLE_H264 var h264 = new JingleRtp.PayloadType() { clockrate = 90000, name = "H264", id = 96 }; yield add_if_supported(list, media, h264); h264.rtcp_fbs.add_all(rtcp_fbs); #endif #if ENABLE_VP9 var vp9 = new JingleRtp.PayloadType() { clockrate = 90000, name = "VP9", id = 97 }; vp9.rtcp_fbs.add_all(rtcp_fbs); yield add_if_supported(list, media, vp9); #endif var vp8 = new JingleRtp.PayloadType() { clockrate = 90000, name = "VP8", id = 98 }; vp8.rtcp_fbs.add_all(rtcp_fbs); yield add_if_supported(list, media, vp8); } else { warning("Unsupported media type: %s", media); } return list; } public override async JingleRtp.PayloadType? pick_payload_type(string media, Gee.List payloads) { if (media == "audio" || media == "video") { foreach (JingleRtp.PayloadType type in payloads) { if (yield is_payload_supported(media, type)) return adjust_payload_type(media, type.clone()); } } else { warning("Unsupported media type: %s", media); } return null; } public JingleRtp.PayloadType adjust_payload_type(string media, JingleRtp.PayloadType type) { var iter = type.rtcp_fbs.iterator(); while (iter.next()) { var fb = iter.@get(); switch (fb.type_) { case "goog-remb": if (fb.subtype != null) iter.remove(); break; case "ccm": if (fb.subtype != "fir") iter.remove(); break; case "nack": if (fb.subtype != null && fb.subtype != "pli") iter.remove(); break; default: iter.remove(); break; } } return type; } public override JingleRtp.Stream create_stream(Jingle.Content content) { return plugin.open_stream(content); } public override void close_stream(JingleRtp.Stream stream) { var rtp_stream = stream as Rtp.Stream; plugin.close_stream(rtp_stream); } public override JingleRtp.Crypto? generate_local_crypto() { uint8[] key_and_salt = new uint8[30]; Crypto.randomize(key_and_salt); return JingleRtp.Crypto.create(JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_80, key_and_salt); } public override JingleRtp.Crypto? pick_remote_crypto(Gee.List cryptos) { foreach (JingleRtp.Crypto crypto in cryptos) { if (crypto.is_valid) return crypto; } return null; } public override JingleRtp.Crypto? pick_local_crypto(JingleRtp.Crypto? remote) { if (remote == null || !remote.is_valid) return null; uint8[] key_and_salt = new uint8[30]; Crypto.randomize(key_and_salt); return remote.rekey(key_and_salt); } } dino-0.5.0/plugins/rtp/src/participant.vala0000664000000000000000000000204614776241610017417 0ustar rootrootusing Gee; using Xmpp; public class Dino.Plugins.Rtp.Participant { public Jid full_jid { get; private set; } protected Gst.Pipeline pipe; private Map ssrcs = new HashMap(); public Participant(Gst.Pipeline pipe, Jid full_jid) { this.pipe = pipe; this.full_jid = full_jid; } public uint32 get_ssrc(Stream stream) { if (ssrcs.has_key(stream)) { return ssrcs[stream]; } return 0; } public void set_ssrc(Stream stream, uint32 ssrc) { if (ssrcs.has_key(stream)) { warning("Learning ssrc %ul for %s in %s when it is already known as %ul", ssrc, full_jid.to_string(), stream.to_string(), ssrcs[stream]); } else { stream.on_destroy.connect(unset_ssrc); } ssrcs[stream] = ssrc; } public void unset_ssrc(Stream stream) { ssrcs.unset(stream); stream.on_destroy.disconnect(unset_ssrc); } public string to_string() { return @"participant $full_jid"; } }dino-0.5.0/plugins/rtp/src/plugin.vala0000664000000000000000000004612014776241610016400 0ustar rootrootusing Gee; using Xmpp; using Xmpp.Xep; public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object { public Dino.Application app { get; private set; } public CodecUtil codec_util { get; private set; } public Gst.DeviceMonitor? device_monitor { get; private set; } public Gst.Pipeline? pipe { get; private set; } public Gst.Bin? rtpbin { get; private set; } public Gst.Element? echoprobe { get; private set; } private Gee.List streams = new ArrayList(); private Gee.List devices = new ArrayList(); private int64 last_devices_refresh = 0; // private Gee.List participants = new ArrayList(); public void registered(Dino.Application app) { this.app = app; this.codec_util = new CodecUtil(); app.add_option_group(Gst.init_get_option_group()); app.stream_interactor.module_manager.initialize_account_modules.connect((account, list) => { list.add(new Module(this)); }); app.plugin_registry.video_call_plugin = this; } private int pause_count = 0; public void pause() { // if (pause_count == 0) { // debug("Pausing pipe for modifications"); // pipe.set_state(Gst.State.PAUSED); // } pause_count++; } public void unpause() { pause_count--; if (pause_count == 0) { debug("Continue pipe after modifications"); pipe.set_state(Gst.State.PLAYING); } if (pause_count < 0) warning("Pause count below zero!"); } private void handle_existing_devices(Gst.DeviceMonitor device_monitor) { var new_devices = new ArrayList(); foreach (Gst.Device device in device_monitor.get_devices()) { if (device.properties == null) continue; if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue; if (device.properties.get_string("device.class") == "monitor") continue; var pre_device = devices.first_match((it) => it.matches(device)); if (pre_device != null) { if (!new_devices.contains(pre_device)) new_devices.add(pre_device); continue; } var new_device = new Device(this, device); devices.add(new_device); new_devices.add(new_device); } devices.retain_all(new_devices); } private void refresh_devices() { if (device_monitor != null) return; // No manual refresh needed, we're monitoring actively. if (last_devices_refresh > 0 && last_devices_refresh + 5000000 > get_monotonic_time()) return; // Already current var device_monitor = new Gst.DeviceMonitor(); device_monitor.show_all = true; device_monitor.start(); handle_existing_devices(device_monitor); device_monitor.stop(); last_devices_refresh = get_monotonic_time(); } private void start_device_monitor() { if (device_monitor != null) return; device_monitor = new Gst.DeviceMonitor(); device_monitor.show_all = true; device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message); device_monitor.start(); handle_existing_devices(device_monitor); } private void stop_device_monitor() { if (device_monitor == null) return; device_monitor.stop(); device_monitor = null; } private void init_call_pipe() { if (pipe != null) return; debug("Creating call pipe."); start_device_monitor(); pipe = new Gst.Pipeline(null); // RTP rtpbin = Gst.ElementFactory.make("rtpbin", null) as Gst.Bin; if (rtpbin == null) { warning("RTP not supported"); pipe = null; return; } rtpbin.pad_added.connect(on_rtp_pad_added); rtpbin.@set("latency", 100); rtpbin.@set("do-lost", true); // rtpbin.@set("do-sync-event", true); rtpbin.@set("drop-on-latency", true); rtpbin.connect("signal::request-pt-map", request_pt_map, this); pipe.add(rtpbin); #if WITH_VOICE_PROCESSOR // Audio echo probe echoprobe = new EchoProbe(); if (echoprobe != null) pipe.add(echoprobe); #endif // Pipeline pipe.auto_flush_bus = true; pipe.bus.add_watch(GLib.Priority.DEFAULT, (_, message) => { on_pipe_bus_message(message); return true; }); pipe.set_state(Gst.State.PLAYING); } public void destroy_call_pipe_if_unused() { if (streams.is_empty && !VideoWidget.has_instances()) { destroy_call_pipe(); } } private void destroy_call_pipe() { if (pipe == null) return; pipe.set_state(Gst.State.NULL); rtpbin = null; #if WITH_VOICE_PROCESSOR echoprobe = null; #endif pipe = null; stop_device_monitor(); debug("Call pipe destroyed"); } private static Gst.Caps? request_pt_map(Gst.Element rtpbin, uint session, uint pt, Plugin plugin) { debug("request-pt-map"); return null; } private void on_rtp_pad_added(Gst.Pad pad) { debug("pad added: %s", pad.name); if (pad.name.has_prefix("recv_rtp_src_")) { string[] split = pad.name.split("_"); uint8 rtpid = (uint8)int.parse(split[3]); foreach (Stream stream in streams) { if (stream.rtpid == rtpid) { stream.on_ssrc_pad_added((uint32) split[4].to_uint64(), pad); } } } if (pad.name.has_prefix("send_rtp_src_")) { string[] split = pad.name.split("_"); uint8 rtpid = (uint8)int.parse(split[3]); debug("pad %s for stream %hhu", pad.name, rtpid); foreach (Stream stream in streams) { if (stream.rtpid == rtpid) { stream.on_send_rtp_src_added(pad); } } } } private void on_pipe_bus_message(Gst.Message message) { switch (message.type) { case Gst.MessageType.ERROR: Error error; string str; message.parse_error(out error, out str); warning("Error in pipeline: %s", error.message); debug(str); break; case Gst.MessageType.WARNING: Error error; string str; message.parse_warning(out error, out str); warning("Warning in pipeline: %s", error.message); debug(str); break; case Gst.MessageType.CLOCK_LOST: debug("Clock lost. Restarting"); pipe.set_state(Gst.State.READY); pipe.set_state(Gst.State.PLAYING); break; case Gst.MessageType.STATE_CHANGED: // Ignore break; case Gst.MessageType.STREAM_STATUS: Gst.StreamStatusType status; Gst.Element owner; message.parse_stream_status(out status, out owner); if (owner != null) { debug("%s stream changed status to %s", owner.name, status.to_string()); } break; case Gst.MessageType.ELEMENT: unowned Gst.Structure struc = message.get_structure(); if (struc != null && message.src is Gst.Element) { debug("Message from %s in pipeline: %s", ((Gst.Element)message.src).name, struc.to_string()); } break; case Gst.MessageType.NEW_CLOCK: debug("New clock."); break; case Gst.MessageType.TAG: // Ignore break; case Gst.MessageType.QOS: // Ignore break; case Gst.MessageType.LATENCY: if (message.src != null && message.src.name != null && message.src is Gst.Element) { Gst.Query latency_query = new Gst.Query.latency(); if (((Gst.Element)message.src).query(latency_query)) { bool live; Gst.ClockTime min_latency, max_latency; latency_query.parse_latency(out live, out min_latency, out max_latency); debug("Latency message from %s: live=%s, min_latency=%s, max_latency=%s", message.src.name, live.to_string(), min_latency.to_string(), max_latency.to_string()); } } break; default: debug("Pipe bus message: %s", message.type.to_string()); break; } } private bool on_device_monitor_message(Gst.Bus bus, Gst.Message message) { Gst.Device? old_gst_device = null; Gst.Device? gst_device = null; Device? device = null; switch (message.type) { case Gst.MessageType.DEVICE_ADDED: message.parse_device_added(out gst_device); if (devices.any_match((it) => it.matches(gst_device))) return Source.CONTINUE; device = new Device(this, gst_device); devices.add(device); break; #if GST_1_16 case Gst.MessageType.DEVICE_CHANGED: message.parse_device_changed(out gst_device, out old_gst_device); device = devices.first_match((it) => it.matches(old_gst_device)); if (device != null) device.update(gst_device); break; #endif case Gst.MessageType.DEVICE_REMOVED: message.parse_device_removed(out gst_device); device = devices.first_match((it) => it.matches(gst_device)); if (device != null) devices.remove(device); break; default: break; } if (device != null) { devices_changed(device.media, device.is_sink); } return Source.CONTINUE; } public uint8 next_free_id() { uint8 rtpid = 0; while (streams.size < 100 && streams.any_match((stream) => stream.rtpid == rtpid)) { rtpid++; } return rtpid; } // public Participant get_participant(Jid full_jid, bool self) { // foreach (Participant participant in participants) { // if (participant.full_jid.equals(full_jid)) { // return participant; // } // } // Participant participant; // if (self) { // participant = new SelfParticipant(pipe, full_jid); // } else { // participant = new Participant(pipe, full_jid); // } // participants.add(participant); // return participant; // } public Stream open_stream(Xmpp.Xep.Jingle.Content content) { init_call_pipe(); var content_params = content.content_params as Xmpp.Xep.JingleRtp.Parameters; if (content_params == null) return null; Stream stream; if (content_params.media == "video") { stream = new VideoStream(this, content); } else { stream = new Stream(this, content); } streams.add(stream); return stream; } public void close_stream(Stream stream) { streams.remove(stream); stream.destroy(); destroy_call_pipe_if_unused(); } public void shutdown() { stop_device_monitor(); destroy_call_pipe(); Gst.deinit(); } public bool supported() { return codec_util.is_element_supported("rtpbin"); } public VideoCallWidget? create_widget(WidgetType type) { init_call_pipe(); if (type == WidgetType.GTK4) { return new VideoWidget(this); } return null; } public Gee.List get_devices(string media, bool incoming) { refresh_devices(); Gee.List devices; if (media == "video" && !incoming) { devices = get_video_sources(); } else if (media == "audio") { devices = get_audio_devices(incoming); } else { devices = new ArrayList(); devices.add_all_iterator(this.devices.filter(it => it.media == media && (incoming && it.is_sink || !incoming && it.is_source) && !it.is_monitor)); } devices.sort((media_left, media_right) => { return strcmp(media_left.id, media_right.id); }); return devices; } public Gee.List get_audio_devices(bool incoming) { ArrayList pulse_devices = new ArrayList(); ArrayList other_devices = new ArrayList(); foreach (Device device in devices) { if (device.media != "audio") continue; if (incoming && !device.is_sink || !incoming && !device.is_source) continue; // Skip monitors if (device.is_monitor) continue; if (device.protocol == DeviceProtocol.PULSEAUDIO) { pulse_devices.add(device); } else { other_devices.add(device); } } // If we have any pulseaudio devices, present only those. Don't want duplicated devices from pipewire and pulseaudio. return pulse_devices.size > 0 ? pulse_devices : other_devices; } public Gee.List get_video_sources() { ArrayList pipewire_devices = new ArrayList(); ArrayList other_devices = new ArrayList(); foreach (Device device in devices) { if (device.media != "video") continue; if (device.is_sink) continue; // Skip monitors if (device.is_monitor) continue; bool is_color = false; for (int i = 0; i < device.device.caps.get_size(); i++) { unowned Gst.Structure structure = device.device.caps.get_structure(i); if (!structure.has_field("format")) continue; // "format" might be an array and get_string() will then return null. We just assume arrays to be fine. string? format = structure.get_string("format"); if (format == null || !format.has_prefix("GRAY")) { is_color = true; } } // Don't allow grey-scale devices if (!is_color) continue; if (device.protocol == DeviceProtocol.PIPEWIRE) { pipewire_devices.add(device); } else { other_devices.add(device); } } // If we have any pipewire devices, present only those. Don't want duplicated devices from pipewire and video for linux. return pipewire_devices.size > 0 ? pipewire_devices : other_devices; } private int get_max_fps(Device device) { int fps = 0; for (int i = 0; i < device.device.caps.get_size(); i++) { unowned Gst.Structure structure = device.device.caps.get_structure(i); if (structure.has_field("framerate")) { Value framerate = structure.get_value("framerate"); if (framerate.type() == typeof(Gst.Fraction)) { int num = Gst.Value.get_fraction_numerator(framerate); int den = Gst.Value.get_fraction_denominator(framerate); fps = int.max(fps, num / den); } else if (framerate.type() == typeof(Gst.ValueList)) { for(uint j = 0; j < Gst.ValueList.get_size(framerate); j++) { Value fraction = Gst.ValueList.get_value(framerate, j); int num = Gst.Value.get_fraction_numerator(fraction); int den = Gst.Value.get_fraction_denominator(fraction); fps = int.max(fps, num / den); } } else { debug("Unknown type for framerate %s on device %s", framerate.type_name(), device.display_name); } } } debug("Max framerate for device %s: %d", device.display_name, fps); return fps; } public MediaDevice? get_preferred_device(string media, bool incoming) { Gee.List devices = new ArrayList(); foreach (MediaDevice media_device in get_devices(media, incoming)) { if (media_device is Device) devices.add((Device)media_device); } if (devices.is_empty) { warning("No preferred device for %s %s. Media will not be processed.", incoming ? "incoming" : "outgoing", media); return null; } // Take default if present foreach (Device device in devices) { if (device.is_default) { debug("Using %s for %s %s as it's default", device.display_name, incoming ? "incoming" : "outgoing", media); return device; } } if (media == "video") { // Pick best FPS int max_fps = -1; Device? max_fps_device = null; foreach (Device device in devices) { int fps = get_max_fps(device); if (fps > max_fps || max_fps_device == null) { max_fps = fps; max_fps_device = device; } } debug("Using %s for %s %s as it has max FPS (%d)", max_fps_device.display_name, incoming ? "incoming" : "outgoing", media, max_fps); return max_fps_device; } else { // Pick any Device? device = devices.first(); debug("Using %s for %s %s as it's first pick", device.display_name, incoming ? "incoming" : "outgoing", media); return device; } } public MediaDevice? get_device(Xmpp.Xep.JingleRtp.Stream? stream, bool incoming) { Stream? plugin_stream = stream as Stream?; if (plugin_stream == null) return null; MediaDevice? device = incoming ? plugin_stream.output_device : plugin_stream.input_device; return device ?? get_preferred_device(stream.media, incoming); } public void dump_dot() { if (pipe == null) return; string name = @"pipe-$(pipe.clock.get_time())-$(pipe.current_state)"; Gst.Debug.bin_to_dot_file(pipe, Gst.DebugGraphDetails.ALL, name); print(@"Stored pipe details as $name\n"); } public void set_pause(Xmpp.Xep.JingleRtp.Stream? stream, bool pause) { Stream? plugin_stream = stream as Stream?; if (plugin_stream == null) return; if (pause) { plugin_stream.pause(); } else { plugin_stream.unpause(); } } public void set_device(Xmpp.Xep.JingleRtp.Stream? stream, MediaDevice? device) { Device? real_device = device as Device?; Stream? plugin_stream = stream as Stream?; if (real_device == null || plugin_stream == null) return; if (real_device.is_source) { plugin_stream.input_device = real_device; } else if (real_device.is_sink) { plugin_stream.output_device = real_device; } } } dino-0.5.0/plugins/rtp/src/register_plugin.vala0000664000000000000000000000013414776241610020277 0ustar rootrootpublic Type register_plugin(Module module) { return typeof (Dino.Plugins.Rtp.Plugin); } dino-0.5.0/plugins/rtp/src/stream.vala0000664000000000000000000010344314776241610016377 0ustar rootrootusing Gee; using Xmpp; public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { public uint8 rtpid { get; private set; } public Plugin plugin { get; private set; } public Gst.Pipeline pipe { get { return plugin.pipe; }} public Gst.Element rtpbin { get { return plugin.rtpbin; }} public CodecUtil codec_util { get { return plugin.codec_util; }} private Gst.App.Sink send_rtp; private Gst.App.Sink send_rtcp; private Gst.App.Src recv_rtp; private Gst.App.Src recv_rtcp; private Gst.Element decode; private Gst.RTP.BaseDepayload decode_depay; private Gst.Element input; private Gst.Pad input_pad; private Gst.Element output; private Gst.Element session; private Device _input_device; public Device input_device { get { return _input_device; } set { if (sending && !paused) { var input = this.input; set_input(value != null ? value.link_source(payload_type, our_ssrc, next_seqnum_offset, next_timestamp_offset) : null); if (this._input_device != null) this._input_device.unlink(input); } this._input_device = value; }} private Device _output_device; public Device output_device { get { return _output_device; } set { if (output != null) remove_output(output); if (value != null && receiving) add_output(value.link_sink()); this._output_device = value; }} public bool created { get; private set; default = false; } public bool paused { get; private set; default = false; } private bool push_recv_data = false; private uint our_ssrc = Random.next_int(); private int next_seqnum_offset = -1; private uint32 next_timestamp_offset_base = 0; private int64 next_timestamp_offset_stamp = 0; private uint32 next_timestamp_offset { get { if (next_timestamp_offset_base == 0) return 0; int64 monotonic_diff = get_monotonic_time() - next_timestamp_offset_stamp; return next_timestamp_offset_base + (uint32)((double)monotonic_diff / 1000000.0 * payload_type.clockrate); } } private uint32 participant_ssrc = 0; private Gst.Pad recv_rtcp_sink_pad; private Gst.Pad recv_rtp_sink_pad; private Gst.Pad recv_rtp_src_pad; private Gst.Pad send_rtcp_src_pad; private Gst.Pad send_rtp_sink_pad; private Gst.Pad send_rtp_src_pad; private Crypto.Srtp.Session? crypto_session = new Crypto.Srtp.Session(); public Stream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(content); this.plugin = plugin; this.rtpid = plugin.next_free_id(); content.notify["senders"].connect_after(on_senders_changed); } public void on_senders_changed() { if (sending && input == null) { input_device = input_device; } if (receiving && output == null) { output_device = output_device; } } public override void create() { plugin.pause(); // Create i/o if needed if (input == null && sending) { input_device = input_device; } if (output == null && receiving && media == "audio") { output_device = output_device; } // Create app elements send_rtp = Gst.ElementFactory.make("appsink", @"rtp_sink_$rtpid") as Gst.App.Sink; send_rtp.async = false; send_rtp.caps = CodecUtil.get_caps(media, payload_type, false); send_rtp.emit_signals = true; send_rtp.sync = true; send_rtp.drop = true; send_rtp.wait_on_eos = false; send_rtp.new_sample.connect(on_new_sample); #if GST_1_20 send_rtp.new_serialized_event.connect(on_new_event); #endif send_rtp.connect("signal::eos", on_eos_static, this); pipe.add(send_rtp); send_rtcp = Gst.ElementFactory.make("appsink", @"rtcp_sink_$rtpid") as Gst.App.Sink; send_rtcp.async = false; send_rtcp.caps = new Gst.Caps.empty_simple("application/x-rtcp"); send_rtcp.emit_signals = true; send_rtcp.sync = true; send_rtcp.drop = true; send_rtcp.wait_on_eos = false; send_rtcp.new_sample.connect(on_new_sample); send_rtcp.connect("signal::eos", on_eos_static, this); pipe.add(send_rtcp); recv_rtp = Gst.ElementFactory.make("appsrc", @"rtp_src_$rtpid") as Gst.App.Src; recv_rtp.caps = CodecUtil.get_caps(media, payload_type, true); recv_rtp.do_timestamp = true; recv_rtp.format = Gst.Format.TIME; recv_rtp.is_live = true; pipe.add(recv_rtp); recv_rtcp = Gst.ElementFactory.make("appsrc", @"rtcp_src_$rtpid") as Gst.App.Src; recv_rtcp.caps = new Gst.Caps.empty_simple("application/x-rtcp"); recv_rtcp.do_timestamp = true; recv_rtcp.format = Gst.Format.TIME; recv_rtcp.is_live = true; pipe.add(recv_rtcp); // Connect RTCP send_rtcp_src_pad = rtpbin.get_request_pad(@"send_rtcp_src_$rtpid"); send_rtcp_src_pad.link(send_rtcp.get_static_pad("sink")); recv_rtcp_sink_pad = rtpbin.get_request_pad(@"recv_rtcp_sink_$rtpid"); recv_rtcp.get_static_pad("src").link(recv_rtcp_sink_pad); // Connect input send_rtp_sink_pad = rtpbin.get_request_pad(@"send_rtp_sink_$rtpid"); if (input != null) { input_pad = input.get_request_pad(@"src_$rtpid"); input_pad.link(send_rtp_sink_pad); } // Connect output decode = codec_util.get_decode_bin(media, payload_type, @"decode_$rtpid"); decode_depay = (Gst.RTP.BaseDepayload)((Gst.Bin)decode).get_by_name(@"decode_$(rtpid)_rtp_depay"); pipe.add(decode); if (output != null) { decode.link(output); } // Connect RTP recv_rtp_sink_pad = rtpbin.get_request_pad(@"recv_rtp_sink_$rtpid"); recv_rtp.get_static_pad("src").link(recv_rtp_sink_pad); created = true; push_recv_data = true; plugin.unpause(); GLib.Signal.emit_by_name(rtpbin, "get-session", rtpid, out session); if (session != null && remb_enabled) { Object internal_session; session.@get("internal-session", out internal_session); if (internal_session != null) { internal_session.connect("signal::on-feedback-rtcp", on_feedback_rtcp, this); } Timeout.add(1000, () => remb_adjust()); } if (input_device != null && media == "video") { input_device.update_bitrate(payload_type, target_send_bitrate); } } private int last_packets_lost = -1; private uint64 last_packets_received = 0; private uint64 last_octets_received = 0; private uint max_target_receive_bitrate = 0; private int64 last_remb_time = 0; private bool remb_adjust() { unowned Gst.Structure? stats; if (session == null) { debug("Session for %u finished, turning off remb adjustment", rtpid); return Source.REMOVE; } session.get("stats", out stats); if (stats == null) { warning("No stats for session %u", rtpid); return Source.REMOVE; } unowned ValueArray? source_stats; stats.get("source-stats", typeof(ValueArray), out source_stats); if (source_stats == null) { warning("No source-stats for session %u", rtpid); return Source.REMOVE; } if (input_device == null) return Source.CONTINUE; foreach (Value value in source_stats.values) { unowned Gst.Structure source_stat = (Gst.Structure) value.get_boxed(); uint32 ssrc; if (!source_stat.get_uint("ssrc", out ssrc)) continue; if (ssrc == participant_ssrc) { int packets_lost; uint64 packets_received, octets_received; source_stat.get_int("packets-lost", out packets_lost); source_stat.get_uint64("packets-received", out packets_received); source_stat.get_uint64("octets-received", out octets_received); int new_lost = packets_lost - last_packets_lost; if (new_lost < 0) new_lost = 0; uint64 new_received = packets_received - last_packets_received; if (packets_received < last_packets_received) new_received = 0; uint64 new_octets = octets_received - last_octets_received; if (octets_received < last_octets_received) octets_received = 0; if (new_received == 0) continue; last_packets_lost = packets_lost; last_packets_received = packets_received; last_octets_received = octets_received; double loss_rate = (double)new_lost / (double)(new_lost + new_received); uint new_target_receive_bitrate; if (new_lost <= 0 || loss_rate < 0.02) { new_target_receive_bitrate = (uint)(1.08 * (double)target_receive_bitrate); } else if (loss_rate > 0.1) { new_target_receive_bitrate = (uint)((1.0 - 0.5 * loss_rate) * (double)target_receive_bitrate); } else { new_target_receive_bitrate = target_receive_bitrate; } if (last_remb_time == 0) { last_remb_time = get_monotonic_time(); } else { int64 time_now = get_monotonic_time(); int64 time_diff = time_now - last_remb_time; last_remb_time = time_now; uint actual_bitrate = (uint)(((double)new_octets * 8.0) * (double)time_diff / 1000.0 / 1000000.0); new_target_receive_bitrate = uint.max(new_target_receive_bitrate, (uint)(0.9 * (double)actual_bitrate)); max_target_receive_bitrate = uint.max((uint)(1.5 * (double)actual_bitrate), max_target_receive_bitrate); new_target_receive_bitrate = uint.min(new_target_receive_bitrate, max_target_receive_bitrate); } new_target_receive_bitrate = uint.max(16, new_target_receive_bitrate); // Never go below 16 if (new_target_receive_bitrate != target_receive_bitrate) { target_receive_bitrate = new_target_receive_bitrate; uint8[] data = new uint8[] { 143, 206, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 'R', 'E', 'M', 'B', 1, 0, 0, 0, 0, 0, 0, 0 }; data[4] = (uint8)((our_ssrc >> 24) & 0xff); data[5] = (uint8)((our_ssrc >> 16) & 0xff); data[6] = (uint8)((our_ssrc >> 8) & 0xff); data[7] = (uint8)(our_ssrc & 0xff); uint8 br_exp = 0; uint32 br_mant = target_receive_bitrate * 1000; uint8 bits = (uint8)Math.log2(br_mant); if (bits > 16) { br_exp = (uint8)bits - 16; br_mant = br_mant >> br_exp; } data[17] = (uint8)((br_exp << 2) | ((br_mant >> 16) & 0x3)); data[18] = (uint8)((br_mant >> 8) & 0xff); data[19] = (uint8)(br_mant & 0xff); data[20] = (uint8)((ssrc >> 24) & 0xff); data[21] = (uint8)((ssrc >> 16) & 0xff); data[22] = (uint8)((ssrc >> 8) & 0xff); data[23] = (uint8)(ssrc & 0xff); encrypt_and_send_rtcp(data); } } } return Source.CONTINUE; } private static void on_feedback_rtcp(Gst.Element session, uint type, uint fbtype, uint sender_ssrc, uint media_ssrc, Gst.Buffer? fci, Stream self) { if (self.input_device != null && self.media == "video" && type == 206 && fbtype == 15 && fci != null && sender_ssrc == self.participant_ssrc) { // https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03 uint8[] data; fci.extract_dup(0, fci.get_size(), out data); if (data[0] != 'R' || data[1] != 'E' || data[2] != 'M' || data[3] != 'B') return; uint8 br_exp = data[5] >> 2; uint32 br_mant = (((uint32)data[5] & 0x3) << 16) + ((uint32)data[6] << 8) + (uint32)data[7]; self.target_send_bitrate = (br_mant << br_exp) / 1000; self.input_device.update_bitrate(self.payload_type, self.target_send_bitrate); } } private void prepare_local_crypto() { if (local_crypto != null && local_crypto.is_valid && !crypto_session.has_encrypt) { crypto_session.set_encryption_key(local_crypto.crypto_suite, local_crypto.key, local_crypto.salt); debug("Setting up encryption with key params %s", local_crypto.key_params); } } bool flip = false; uint8 rotation = 0; #if GST_1_20 private bool on_new_event(Gst.App.Sink sink) { if (sink == null || sink != send_rtp) { return false; } Gst.MiniObject obj = sink.try_pull_object(0); if (obj.type == typeof(Gst.Event)) { unowned Gst.TagList tags; if (((Gst.Event)obj).type == Gst.EventType.TAG) { ((Gst.Event)obj).parse_tag(out tags); Gst.Video.OrientationMethod orientation_method; Gst.Video.Orientation.from_tag(tags, out orientation_method); switch (orientation_method) { case Gst.Video.OrientationMethod.IDENTITY: case Gst.Video.OrientationMethod.VERT: default: rotation = 0; break; case Gst.Video.OrientationMethod.@90R: case Gst.Video.OrientationMethod.UL_LR: rotation = 1; break; case Gst.Video.OrientationMethod.@180: case Gst.Video.OrientationMethod.HORIZ: rotation = 2; break; case Gst.Video.OrientationMethod.@90L: case Gst.Video.OrientationMethod.UR_LL: rotation = 3; break; } switch (orientation_method) { case Gst.Video.OrientationMethod.IDENTITY: case Gst.Video.OrientationMethod.@90R: case Gst.Video.OrientationMethod.@180: case Gst.Video.OrientationMethod.@90L: default: flip = false; break; case Gst.Video.OrientationMethod.VERT: case Gst.Video.OrientationMethod.UL_LR: case Gst.Video.OrientationMethod.HORIZ: case Gst.Video.OrientationMethod.UR_LL: flip = true; break; } } } return false; } #endif private Gst.FlowReturn on_new_sample(Gst.App.Sink sink) { if (sink == null) { debug("Sink is null"); return Gst.FlowReturn.EOS; } if (sink != send_rtp && sink != send_rtcp) { warning("unknown sample"); return Gst.FlowReturn.NOT_SUPPORTED; } Gst.Sample sample = sink.pull_sample(); Gst.Buffer buffer = sample.get_buffer(); if (sink == send_rtp) { uint buffer_ssrc = 0, buffer_seq = 0; Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { buffer_ssrc = rtp_buffer.get_ssrc(); buffer_seq = rtp_buffer.get_seq(); next_seqnum_offset = rtp_buffer.get_seq() + 1; next_timestamp_offset_base = rtp_buffer.get_timestamp(); next_timestamp_offset_stamp = get_monotonic_time(); rtp_buffer.unmap(); } #if GLIB_2_64 if (our_ssrc != buffer_ssrc) { warning_once("Sending RTP %s buffer seq %u with SSRC %u when our ssrc is %u", media, buffer_seq, buffer_ssrc, our_ssrc); } #endif } #if GST_1_20 if (sink == send_rtp) { Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation"); if (ext != null) { buffer = (Gst.Buffer) buffer.make_writable(); Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.WRITE, out rtp_buffer)) { uint8[] extension_data = new uint8[1]; bool camera = false; extension_data[0] = extension_data[0] | (rotation & 0x3); if (flip) extension_data[0] = extension_data[0] | 0x4; if (camera) extension_data[0] = extension_data[0] | 0x8; rtp_buffer.add_extension_onebyte_header(ext.id, extension_data); } } } #endif prepare_local_crypto(); uint8[] data; buffer.extract_dup(0, buffer.get_size(), out data); if (sink == send_rtp) { encrypt_and_send_rtp((owned) data); } else if (sink == send_rtcp) { encrypt_and_send_rtcp((owned) data); } return Gst.FlowReturn.OK; } private void encrypt_and_send_rtp(owned uint8[] data) { Bytes bytes; if (crypto_session.has_encrypt) { bytes = new Bytes.take(crypto_session.encrypt_rtp(data)); } else { bytes = new Bytes.take(data); } on_send_rtp_data(bytes); } private void encrypt_and_send_rtcp(owned uint8[] data) { Bytes bytes; if (crypto_session.has_encrypt) { bytes = new Bytes.take(crypto_session.encrypt_rtcp(data)); } else { bytes = new Bytes.take(data); } if (rtcp_mux) { on_send_rtp_data(bytes); } else { on_send_rtcp_data(bytes); } } private static Gst.PadProbeReturn drop_probe() { return Gst.PadProbeReturn.DROP; } private static void on_eos_static(Gst.App.Sink sink, Stream self) { debug("EOS on %s", sink.name); if (sink == self.send_rtp) { Idle.add(() => { self.on_send_rtp_eos(); return Source.REMOVE; }); } else if (sink == self.send_rtcp) { Idle.add(() => { self.on_send_rtcp_eos(); return Source.REMOVE; }); } } private void on_send_rtp_eos() { if (send_rtp_src_pad != null) { send_rtp_src_pad.unlink(send_rtp.get_static_pad("sink")); send_rtp_src_pad = null; } send_rtp.set_locked_state(true); send_rtp.set_state(Gst.State.NULL); // This happens async, so pipe might be gone by now. if (pipe != null) pipe.remove(send_rtp); send_rtp = null; debug("Stopped sending RTP for %u", rtpid); } private void on_send_rtcp_eos() { send_rtcp.set_locked_state(true); send_rtcp.set_state(Gst.State.NULL); // This happens async, so pipe might be gone by now. if (pipe != null) pipe.remove(send_rtcp); send_rtcp = null; debug("Stopped sending RTCP for %u", rtpid); } public override void destroy() { // Stop network communication push_recv_data = false; if (recv_rtp != null) recv_rtp.end_of_stream(); if (recv_rtcp != null) recv_rtcp.end_of_stream(); if (send_rtp != null) send_rtp.new_sample.disconnect(on_new_sample); if (send_rtcp != null) send_rtcp.new_sample.disconnect(on_new_sample); // Disconnect input device if (input != null) { input_pad.unlink(send_rtp_sink_pad); input.release_request_pad(input_pad); input_pad = null; } if (this._input_device != null) { if (!paused) this._input_device.unlink(input); this._input_device = null; this.input = null; } // Inject EOS if (send_rtp_sink_pad != null) { send_rtp_sink_pad.send_event(new Gst.Event.eos()); } // Disconnect decode if (recv_rtp_src_pad != null) { recv_rtp_src_pad.add_probe(Gst.PadProbeType.BLOCK, drop_probe); recv_rtp_src_pad.unlink(decode.get_static_pad("sink")); } // Disconnect output if (output != null) { decode.get_static_pad("src").add_probe(Gst.PadProbeType.BLOCK, drop_probe); decode.unlink(output); } // Disconnect output device if (this._output_device != null) { this._output_device.unlink(output); this._output_device = null; } output = null; // Destroy decode if (decode != null) { decode.set_locked_state(true); decode.set_state(Gst.State.NULL); pipe.remove(decode); decode = null; decode_depay = null; } // Disconnect and remove RTP input if (recv_rtp != null) { recv_rtp.get_static_pad("src").unlink(recv_rtp_sink_pad); recv_rtp.set_locked_state(true); recv_rtp.set_state(Gst.State.NULL); pipe.remove(recv_rtp); recv_rtp = null; } // Disconnect and remove RTCP input if (recv_rtcp != null) { recv_rtcp.get_static_pad("src").unlink(recv_rtcp_sink_pad); recv_rtcp.set_locked_state(true); recv_rtcp.set_state(Gst.State.NULL); pipe.remove(recv_rtcp); recv_rtcp = null; } // Release rtp pads if (send_rtp_sink_pad != null) { rtpbin.release_request_pad(send_rtp_sink_pad); send_rtp_sink_pad = null; } if (recv_rtp_sink_pad != null) { rtpbin.release_request_pad(recv_rtp_sink_pad); recv_rtp_sink_pad = null; } if (send_rtcp_src_pad != null) { rtpbin.release_request_pad(send_rtcp_src_pad); send_rtcp_src_pad = null; } if (recv_rtcp_sink_pad != null) { rtpbin.release_request_pad(recv_rtcp_sink_pad); recv_rtcp_sink_pad = null; } } private void prepare_remote_crypto() { if (remote_crypto != null && remote_crypto.is_valid && !crypto_session.has_decrypt) { crypto_session.set_decryption_key(remote_crypto.crypto_suite, remote_crypto.key, remote_crypto.salt); debug("Setting up decryption with key params %s", remote_crypto.key_params); } } private uint16 previous_incoming_video_orientation_degree = uint16.MAX; public signal void incoming_video_orientation_changed(uint16 degree); public override void on_recv_rtp_data(Bytes bytes) { if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) { on_recv_rtcp_data(bytes); return; } #if GST_1_16 { Gst.Buffer buffer = new Gst.Buffer.wrapped_bytes(bytes); Gst.RTP.Buffer rtp_buffer; uint buffer_ssrc = 0, buffer_seq = 0; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { buffer_ssrc = rtp_buffer.get_ssrc(); buffer_seq = rtp_buffer.get_seq(); rtp_buffer.unmap(); } } #endif if (push_recv_data) { prepare_remote_crypto(); Gst.Buffer buffer; if (crypto_session.has_decrypt) { try { buffer = new Gst.Buffer.wrapped(crypto_session.decrypt_rtp(bytes.get_data())); } catch (Error e) { warning("%s (%d)", e.message, e.code); return; } } else { #if GST_1_16 buffer = new Gst.Buffer.wrapped_bytes(bytes); #else buffer = new Gst.Buffer.wrapped(bytes.get_data()); #endif } Gst.RTP.Buffer rtp_buffer; if (Gst.RTP.Buffer.map(buffer, Gst.MapFlags.READ, out rtp_buffer)) { if (rtp_buffer.get_extension()) { Xmpp.Xep.JingleRtp.HeaderExtension? ext = header_extensions.first_match((it) => it.uri == "urn:3gpp:video-orientation"); if (ext != null) { unowned uint8[] extension_data; if (rtp_buffer.get_extension_onebyte_header(ext.id, 0, out extension_data) && extension_data.length == 1) { bool camera = (extension_data[0] & 0x8) > 0; bool flip = (extension_data[0] & 0x4) > 0; uint8 rotation = extension_data[0] & 0x3; uint16 rotation_degree = uint16.MAX; switch(rotation) { case 0: rotation_degree = 0; break; case 1: rotation_degree = 90; break; case 2: rotation_degree = 180; break; case 3: rotation_degree = 270; break; } if (rotation_degree != previous_incoming_video_orientation_degree) { incoming_video_orientation_changed(rotation_degree); previous_incoming_video_orientation_degree = rotation_degree; } } } } rtp_buffer.unmap(); } #if VALA_0_50 recv_rtp.push_buffer((owned) buffer); #else Gst.FlowReturn ret; GLib.Signal.emit_by_name(recv_rtp, "push-buffer", buffer, out ret); #endif } } public override void on_recv_rtcp_data(Bytes bytes) { if (push_recv_data) { prepare_remote_crypto(); Gst.Buffer buffer; if (crypto_session.has_decrypt) { try { buffer = new Gst.Buffer.wrapped(crypto_session.decrypt_rtcp(bytes.get_data())); } catch (Error e) { warning("%s (%d)", e.message, e.code); return; } } else { #if GST_1_16 buffer = new Gst.Buffer.wrapped_bytes(bytes); #else buffer = new Gst.Buffer.wrapped(bytes.get_data()); #endif } #if VALA_0_50 recv_rtcp.push_buffer((owned) buffer); #else Gst.FlowReturn ret; GLib.Signal.emit_by_name(recv_rtcp, "push-buffer", buffer, out ret); #endif } } public override void on_rtp_ready() { // If full frame has been sent before the connection was ready, the counterpart would only display our video after the next full frame. // Send a full frame to let the counterpart display our video asap rtpbin.send_event(new Gst.Event.custom( Gst.EventType.CUSTOM_UPSTREAM, new Gst.Structure("GstForceKeyUnit", "all-headers", typeof(bool), true, null)) ); } public override void on_rtcp_ready() { int rtp_session_id = (int) rtpid; uint64 max_delay = int.MAX; Object rtp_session; bool rtp_sent; GLib.Signal.emit_by_name(rtpbin, "get-internal-session", rtp_session_id, out rtp_session); GLib.Signal.emit_by_name(rtp_session, "send-rtcp-full", max_delay, out rtp_sent); debug("RTCP is ready, resending rtcp: %s", rtp_sent.to_string()); } public void on_ssrc_pad_added(uint32 ssrc, Gst.Pad pad) { debug("New ssrc %u with pad %s", ssrc, pad.name); if (participant_ssrc != 0 && participant_ssrc != ssrc) { warning("Got second ssrc on stream (old: %u, new: %u), ignoring", participant_ssrc, ssrc); return; } participant_ssrc = ssrc; recv_rtp_src_pad = pad; if (decode != null) { plugin.pause(); debug("Link %s to %s decode for %s", recv_rtp_src_pad.name, media, name); recv_rtp_src_pad.link(decode.get_static_pad("sink")); plugin.unpause(); } } public void on_send_rtp_src_added(Gst.Pad pad) { send_rtp_src_pad = pad; if (send_rtp != null) { plugin.pause(); debug("Link %s to %s send_rtp for %s", send_rtp_src_pad.name, media, name); send_rtp_src_pad.link(send_rtp.get_static_pad("sink")); plugin.unpause(); } } public void set_input(Gst.Element? input) { set_input_and_pause(input, paused); } private void set_input_and_pause(Gst.Element? input, bool paused) { if (created && this.input != null) { this.input_pad.unlink(send_rtp_sink_pad); this.input.release_request_pad(this.input_pad); this.input_pad = null; this.input = null; } this.input = input; this.paused = paused; if (created && sending && !paused && input != null) { plugin.pause(); input_pad = input.get_request_pad(@"src_$rtpid"); input_pad.link(send_rtp_sink_pad); plugin.unpause(); } } public void pause() { if (paused) return; var input = this.input; set_input_and_pause(null, true); if (input != null && input_device != null) input_device.unlink(input); } public void unpause() { if (!paused) return; set_input_and_pause(input_device != null ? input_device.link_source(payload_type, our_ssrc, next_seqnum_offset, next_timestamp_offset) : null, false); input_device.update_bitrate(payload_type, target_send_bitrate); } public uint get_participant_ssrc(Xmpp.Jid participant) { if (participant.equals(content.session.peer_full_jid)) { return participant_ssrc; } return 0; } ulong block_probe_handler_id = 0; public virtual void add_output(Gst.Element element, Xmpp.Jid? participant = null) { if (output != null) { critical("add_output() invoked more than once"); return; } if (participant != null) { critical("add_output() invoked with participant when not supported"); return; } this.output = element; if (created) { plugin.pause(); decode.link(element); if (block_probe_handler_id != 0) { decode.get_static_pad("src").remove_probe(block_probe_handler_id); } plugin.unpause(); } } public virtual void remove_output(Gst.Element element) { if (output != element) { critical("remove_output() invoked without prior add_output()"); return; } if (created) { block_probe_handler_id = decode.get_static_pad("src").add_probe(Gst.PadProbeType.BLOCK, drop_probe); decode.unlink(element); } if (this._output_device != null) { this._output_device.unlink(element); this._output_device = null; } this.output = null; } } public class Dino.Plugins.Rtp.VideoStream : Stream { private Gee.List outputs = new ArrayList(); private Gst.Element output_tee; private Gst.Element rotate; private ulong incoming_video_orientation_changed_handler; public VideoStream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(plugin, content); if (media != "video") critical("VideoStream created for non-video media"); } public override void create() { incoming_video_orientation_changed_handler = incoming_video_orientation_changed.connect(on_video_orientation_changed); plugin.pause(); rotate = Gst.ElementFactory.make("videoflip", @"video_rotate_$rtpid"); pipe.add(rotate); output_tee = Gst.ElementFactory.make("tee", @"video_tee_$rtpid"); output_tee.@set("allow-not-linked", true); pipe.add(output_tee); rotate.link(output_tee); add_output(rotate); base.create(); foreach (Gst.Element output in outputs) { output_tee.link(output); } plugin.unpause(); } private void on_video_orientation_changed(uint16 degree) { if (rotate != null) { switch (degree) { case 0: rotate.@set("method", 0); break; case 90: rotate.@set("method", 1); break; case 180: rotate.@set("method", 2); break; case 270: rotate.@set("method", 3); break; } } } public override void destroy() { foreach (Gst.Element output in outputs) { output_tee.unlink(output); } base.destroy(); rotate.set_locked_state(true); rotate.set_state(Gst.State.NULL); rotate.unlink(output_tee); pipe.remove(rotate); rotate = null; output_tee.set_locked_state(true); output_tee.set_state(Gst.State.NULL); pipe.remove(output_tee); output_tee = null; disconnect(incoming_video_orientation_changed_handler); } public override void add_output(Gst.Element element, Xmpp.Jid? participant) { if (element == output_tee || element == rotate) { base.add_output(element); return; } outputs.add(element); if (output_tee != null) { output_tee.link(element); } } public override void remove_output(Gst.Element element) { if (element == output_tee || element == rotate) { base.remove_output(element); return; } outputs.remove(element); if (output_tee != null) { output_tee.unlink(element); } } } dino-0.5.0/plugins/rtp/src/video_widget.vala0000664000000000000000000002565514776241610017565 0ustar rootrootprivate static extern unowned Gst.Video.Info gst_video_frame_get_video_info(Gst.Video.Frame frame); [CCode (array_length_type = "size_t", type = "void*")] private static extern unowned uint8[] gst_video_frame_get_data(Gst.Video.Frame frame); public class Dino.Plugins.Rtp.Paintable : Gdk.Paintable, Object { private Gdk.Paintable image; private double pixel_aspect_ratio; public override Gdk.PaintableFlags get_flags() { return 0; } public void snapshot(Gdk.Snapshot snapshot, double width, double height) { if (image != null) image.snapshot(snapshot, width, height); } public override Gdk.Paintable get_current_image() { if (image != null) return image; return Gdk.Paintable.empty(0, 0); } public override int get_intrinsic_width() { if (image != null) return (int) (pixel_aspect_ratio * image.get_intrinsic_width()); return 0; } public override int get_intrinsic_height() { if (image != null) return (int) (pixel_aspect_ratio * image.get_intrinsic_height()); return 0; } public override double get_intrinsic_aspect_ratio() { if (image != null) return pixel_aspect_ratio * image.get_intrinsic_aspect_ratio(); return 0.0; } public override void dispose() { image = null; base.dispose(); } private void set_paintable(Gdk.Paintable paintable, double pixel_aspect_ratio) { if (paintable == image) return; bool size_changed = image == null || this.pixel_aspect_ratio * image.get_intrinsic_width() != pixel_aspect_ratio * paintable.get_intrinsic_width() || image.get_intrinsic_height() != paintable.get_intrinsic_height() || image.get_intrinsic_aspect_ratio() != paintable.get_intrinsic_aspect_ratio(); if (image != null) this.image.dispose(); this.image = paintable; this.pixel_aspect_ratio = pixel_aspect_ratio; if (size_changed) invalidate_size(); invalidate_contents(); } public void queue_set_texture(Gdk.Texture texture, double pixel_aspect_ratio) { Idle.add(() => { set_paintable(texture, pixel_aspect_ratio); return Source.REMOVE; }, Priority.DEFAULT); } } public class Dino.Plugins.Rtp.Sink : Gst.Video.Sink { internal Paintable paintable = new Paintable(); private Gst.Video.Info info = new Gst.Video.Info(); class construct { set_metadata("Dino Gtk Video Sink", "Sink/Video", "The video sink used by Dino", "Dino Team "); add_pad_template(new Gst.PadTemplate("sink", Gst.PadDirection.SINK, Gst.PadPresence.ALWAYS, Gst.Caps.from_string(@"video/x-raw, format={ BGRA, ARGB, RGBA, ABGR, RGB, BGR }"))); } construct { set_drop_out_of_segment(false); } #if GST_1_20 public override bool set_info(Gst.Caps caps, Gst.Video.Info info) { this.info = info; return true; } #else public override bool set_caps(Gst.Caps caps) { base.set_caps(caps); return info.from_caps(caps); } #endif public override void get_times(Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end) { if (buffer.pts != -1) { start = buffer.pts; if (buffer.duration != -1) { end = start + buffer.duration; } else if (info.fps_n > 0) { end = start + Gst.Util.uint64_scale_int(Gst.SECOND, info.fps_d, info.fps_n); } } } public override Gst.Caps get_caps(Gst.Caps? filter) { Gst.Caps caps = Gst.Caps.from_string("video/x-raw, format={ BGRA, ARGB, RGBA, ABGR, RGB, BGR }"); if (filter != null) { return filter.intersect(caps, Gst.CapsIntersectMode.FIRST); } else { return caps; } } private Gdk.MemoryFormat memory_format_from_video(Gst.Video.Format format) { switch (format) { case Gst.Video.Format.BGRA: return Gdk.MemoryFormat.B8G8R8A8; case Gst.Video.Format.ARGB: return Gdk.MemoryFormat.A8R8G8B8; case Gst.Video.Format.RGBA: return Gdk.MemoryFormat.R8G8B8A8; case Gst.Video.Format.ABGR: return Gdk.MemoryFormat.A8B8G8R8; case Gst.Video.Format.RGB: return Gdk.MemoryFormat.R8G8B8; case Gst.Video.Format.BGR: return Gdk.MemoryFormat.B8G8R8; default: warning("Unsupported video format: %s", format.to_string()); return Gdk.MemoryFormat.A8R8G8B8; } } private Gdk.Texture texture_from_buffer(Gst.Buffer buffer, out double pixel_aspect_ratio) { Gst.Video.Frame frame = Gst.Video.Frame(); Gdk.Texture texture; if (frame.map(info, buffer, Gst.MapFlags.READ)) { unowned Gst.Video.Info info = gst_video_frame_get_video_info(frame); Bytes bytes = new Bytes.take(gst_video_frame_get_data(frame)); texture = new Gdk.MemoryTexture(info.width, info.height, memory_format_from_video(info.finfo.format), bytes, info.stride[0]); pixel_aspect_ratio = ((double) info.par_n) / ((double) info.par_d); frame.unmap(); } else { texture = null; } return texture; } private void queue_buffer(Gst.Buffer buf) { double pixel_aspect_ratio; Gdk.Texture texture = texture_from_buffer(buf, out pixel_aspect_ratio); if (texture != null) { paintable.queue_set_texture(texture, pixel_aspect_ratio); } } public override Gst.FlowReturn show_frame(Gst.Buffer buf) { @lock.lock(); queue_buffer(buf); @lock.unlock(); return Gst.FlowReturn.OK; } } public class Dino.Plugins.Rtp.VideoWidget : Gtk.Widget, Dino.Plugins.VideoCallWidget { private const int RECAPS_AFTER_CHANGE = 5; private static uint last_id = 0; public uint id { get; private set; } public Plugin plugin { get; private set; } public Gst.Pipeline pipe { get { return plugin.pipe; }} private bool attached; private Device? connected_device; private Gst.Element? connected_device_element; private Stream? connected_stream; private Gst.Element prepare; private Gst.Caps last_input_caps; private Gst.Caps last_caps; private int recaps_since_change; private Sink sink; private Gtk.Picture widget; private static uint active_widgets = 0; public static bool has_instances() { return active_widgets > 0; } private static void notify_weak(Object widget_object) { if (active_widgets > 0) { debug("Video widget %p destroyed. left=%u", widget_object, active_widgets); active_widgets--; } if (active_widgets == 0 && widget_object is VideoWidget) { ((VideoWidget)widget_object).plugin.destroy_call_pipe_if_unused(); } } public VideoWidget(Plugin plugin) { this.plugin = plugin; this.layout_manager = new Gtk.BinLayout(); id = last_id++; sink = new Sink() { async = false, sync = true }; widget = new Gtk.Picture.for_paintable(sink.paintable); widget.insert_after(this, null); active_widgets++; debug("Video widget %p created. total=%u", this, active_widgets); this.weak_ref(notify_weak); } public void input_caps_changed(GLib.Object pad, ParamSpec spec) { Gst.Caps? caps = ((Gst.Pad)pad).caps; if (caps == null) { debug("Input: No caps"); return; } int width, height; caps.get_structure(0).get_int("width", out width); caps.get_structure(0).get_int("height", out height); debug("Input resolution changed: %ix%i", width, height); // Invoke signal on GTK main loop as recipients are likely to use it for doing GTK operations Idle.add(() => { resolution_changed(width, height); return Source.REMOVE; }); last_input_caps = caps; } public void display_stream(Xmpp.Xep.JingleRtp.Stream? stream, Xmpp.Jid jid) { if (sink == null) return; detach(); if (stream.media != "video") return; connected_stream = stream as Stream?; if (connected_stream == null) return; plugin.pause(); pipe.add(sink); prepare = Gst.parse_bin_from_description(@"videoconvert name=video_widget_$(id)_convert", true); prepare.name = @"video_widget_$(id)_prepare"; prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); pipe.add(prepare); connected_stream.add_output(prepare); prepare.link(sink); sink.set_locked_state(false); plugin.unpause(); attached = true; } public void display_device(MediaDevice media_device) { if (sink == null) return; detach(); connected_device = media_device as Device; if (connected_device == null) return; plugin.pause(); pipe.add(sink); #if GST_1_20 prepare = Gst.parse_bin_from_description(@"videoflip video-direction=auto name=video_widget_$(id)_orientation ! videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); #else prepare = Gst.parse_bin_from_description(@"videoflip method=horizontal-flip name=video_widget_$(id)_flip ! videoconvert name=video_widget_$(id)_convert", true); #endif prepare.name = @"video_widget_$(id)_prepare"; #if GST_1_20 if (prepare is Gst.Bin) { ((Gst.Bin) prepare).get_by_name(@"video_widget_$(id)_flip").get_static_pad("sink").notify["caps"].connect(input_caps_changed); } else { #endif prepare.get_static_pad("sink").notify["caps"].connect(input_caps_changed); #if GST_1_20 } #endif pipe.add(prepare); connected_device_element = connected_device.link_source(); connected_device_element.link(prepare); prepare.link(sink); sink.set_locked_state(false); plugin.unpause(); attached = true; } public void detach() { if (sink == null) return; if (attached) { debug("Detaching"); if (connected_stream != null) { connected_stream.remove_output(prepare); connected_stream = null; } if (connected_device != null) { connected_device_element.unlink(sink); connected_device_element = null; connected_device.unlink(); connected_device = null; } prepare.set_locked_state(true); prepare.set_state(Gst.State.NULL); pipe.remove(prepare); prepare = null; sink.set_locked_state(true); sink.set_state(Gst.State.NULL); pipe.remove(sink); attached = false; } } public override void dispose() { detach(); if (widget != null) widget.unparent(); widget = null; sink = null; } } dino-0.5.0/plugins/rtp/src/voice_processor.vala0000664000000000000000000002043714776241610020311 0ustar rootrootusing Gst; namespace Dino.Plugins.Rtp { public static extern Buffer adjust_to_running_time(Base.Transform transform, Buffer buf); } public class Dino.Plugins.Rtp.EchoProbe : Audio.Filter { private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; public Audio.Info audio_info { get; private set; } public signal void on_new_buffer(owned Buffer buffer); public signal void on_new_delay(int delay); private uint period_samples; private uint period_size; public int delay { get; private set; } private Base.Adapter adapter = new Base.Adapter(); static construct { add_static_pad_template(sink_template); add_static_pad_template(src_template); set_static_metadata("Acoustic Echo Canceller probe", "Generic/Audio", "Gathers playback buffers for echo cancellation", "Dino Team "); } construct { set_passthrough(true); } public override bool setup(Audio.Info info) { audio_info = info; period_samples = info.rate / 100; // 10ms buffers period_size = period_samples * info.bpf; return true; } public override bool src_event(owned Event event) { Query query = new Query.latency(); if (event.type == EventType.LATENCY && srcpad != null && srcpad.query(query)) { ClockTime upstream_latency; query.parse_latency(null, out upstream_latency, null); int delay = this.delay; if (upstream_latency != CLOCK_TIME_NONE) { delay = (int) (upstream_latency / MSECOND); } else { delay = 0; } if (delay != this.delay) { debug("Delay adjusted from %ms to %dms", this.delay, delay); this.delay = delay; on_new_delay(delay); } } return base.src_event((owned) event); } public override FlowReturn transform_ip(Buffer buf) { lock (adapter) { adapter.push(adjust_to_running_time(this, buf)); while (adapter.available() > period_size) { on_new_buffer(adapter.take_buffer(period_size)); } } return FlowReturn.OK; } public override bool stop() { adapter.clear(); return true; } } public class Dino.Plugins.Rtp.VoiceProcessor : Audio.Filter { private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}}; public Audio.Info audio_info { get; private set; } private ulong process_outgoing_buffer_handler_id; private ulong process_stream_delay_handler_id; private uint adjust_delay_timeout_id; private uint period_samples; private uint period_size; private Base.Adapter adapter = new Base.Adapter(); private EchoProbe? echo_probe; private Audio.StreamVolume? stream_volume; private ClockTime last_reverse; private void* native; static construct { add_static_pad_template(sink_template); add_static_pad_template(src_template); set_static_metadata("Voice Processor (AGC, AEC, filters, etc.)", "Generic/Audio", "Pre-processes voice with WebRTC Audio Processing Library", "Dino Team "); } construct { set_passthrough(false); } public VoiceProcessor(EchoProbe? echo_probe = null, Audio.StreamVolume? stream_volume = null) { this.echo_probe = echo_probe; this.stream_volume = stream_volume; } private static extern void* init_native(int stream_delay); private static extern void destroy_native(void* native); private static extern void analyze_reverse_stream(void* native, Audio.Info info, Buffer buffer); private static extern void process_stream(void* native, Audio.Info info, Buffer buffer); private static extern void adjust_stream_delay(void* native); private static extern void set_stream_delay(void* native, int delay); private static extern void notify_gain_level(void* native, int gain_level); private static extern int get_suggested_gain_level(void* native); private static extern bool get_stream_has_voice(void* native); public override bool setup(Audio.Info info) { debug("VoiceProcessor.setup(%s)", info.to_caps().to_string()); audio_info = info; period_samples = info.rate / 100; // 10ms buffers period_size = period_samples * info.bpf; adapter.clear(); return true; } public override bool start() { native = init_native(echo_probe.delay); if (process_outgoing_buffer_handler_id == 0 && echo_probe != null) { process_outgoing_buffer_handler_id = echo_probe.on_new_buffer.connect(process_outgoing_buffer); } if (process_stream_delay_handler_id == 0 && echo_probe != null) { process_stream_delay_handler_id = echo_probe.on_new_delay.connect(process_stream_delay); } if (stream_volume == null && sinkpad.get_peer() != null && sinkpad.get_peer().get_parent_element() is Audio.StreamVolume) { stream_volume = sinkpad.get_peer().get_parent_element() as Audio.StreamVolume; } return true; } private bool adjust_delay() { if (native != null) { adjust_stream_delay(native); return Source.CONTINUE; } else { adjust_delay_timeout_id = 0; return Source.REMOVE; } } private void process_outgoing_buffer(owned Buffer buffer) { if (buffer.pts != uint64.MAX) { last_reverse = buffer.pts; } if (native != null) { buffer = (Buffer) buffer.make_writable(); analyze_reverse_stream(native, echo_probe.audio_info, buffer); } if (adjust_delay_timeout_id == 0 && echo_probe != null) { adjust_delay_timeout_id = Timeout.add(1000, adjust_delay); } } private void process_stream_delay(int stream_delay) { if (native != null) { set_stream_delay(native, stream_delay); } } public override FlowReturn submit_input_buffer(bool is_discont, Buffer input) { lock (adapter) { if (is_discont) { adapter.clear(); } adapter.push(adjust_to_running_time(this, input)); } return FlowReturn.OK; } public override FlowReturn generate_output(out Buffer output_buffer) { lock (adapter) { if (adapter.available() >= period_size) { output_buffer = (Gst.Buffer) adapter.take_buffer(period_size).make_writable(); int old_gain_level = 0; if (stream_volume != null) { old_gain_level = (int) (stream_volume.get_volume(Audio.StreamVolumeFormat.LINEAR) * 255.0); notify_gain_level(native, old_gain_level); } process_stream(native, audio_info, output_buffer); if (stream_volume != null) { int new_gain_level = get_suggested_gain_level(native); if (old_gain_level != new_gain_level) { debug("Gain: %i -> %i", old_gain_level, new_gain_level); stream_volume.set_volume(Audio.StreamVolumeFormat.LINEAR, ((double)new_gain_level) / 255.0); } } } } return FlowReturn.OK; } public override bool stop() { if (process_outgoing_buffer_handler_id != 0) { echo_probe.disconnect(process_outgoing_buffer_handler_id); process_outgoing_buffer_handler_id = 0; } if (adjust_delay_timeout_id != 0) { Source.remove(adjust_delay_timeout_id); adjust_delay_timeout_id = 0; } adapter.clear(); destroy_native(native); native = null; return true; } }dino-0.5.0/plugins/rtp/src/voice_processor_native.cpp0000664000000000000000000002455514776241610021523 0ustar rootroot#include #include #include #if defined(WEBRTC0) #include #include #include #elif defined(WEBRTC1) || defined(WEBRTC2) #include #else #error "Need to define WEBRTC0, WEBRTC1 or WEBRTC2" #endif #define SAMPLE_RATE 48000 #define SAMPLE_CHANNELS 1 struct _DinoPluginsRtpVoiceProcessorNative { #if defined(WEBRTC0) webrtc::AudioProcessing *apm; #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm; #endif gint stream_delay = 0; gint last_median = 0; gint last_poor_delays = 0; }; extern "C" void *dino_plugins_rtp_adjust_to_running_time(GstBaseTransform *transform, GstBuffer *buffer) { GstBuffer *copy = gst_buffer_copy(buffer); GST_BUFFER_PTS(copy) = gst_segment_to_running_time(&transform->segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer)); return copy; } extern "C" void *dino_plugins_rtp_voice_processor_init_native(gint stream_delay) { auto *native = new _DinoPluginsRtpVoiceProcessorNative(); native->stream_delay = stream_delay; #if defined(WEBRTC0) webrtc::Config config; config.Set(new webrtc::ExtendedFilter(true)); config.Set(new webrtc::ExperimentalAgc(true, 85)); native->apm = webrtc::AudioProcessing::Create(config); webrtc::AudioProcessing *apm = native->apm; webrtc::ProcessingConfig pconfig; pconfig.streams[webrtc::ProcessingConfig::kInputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kOutputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kReverseInputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); pconfig.streams[webrtc::ProcessingConfig::kReverseOutputStream] = webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false); apm->Initialize(pconfig); apm->high_pass_filter()->Enable(true); apm->echo_cancellation()->enable_drift_compensation(false); apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kModerateSuppression); apm->echo_cancellation()->enable_delay_logging(true); apm->echo_cancellation()->Enable(true); apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kModerate); apm->noise_suppression()->Enable(true); apm->gain_control()->set_analog_level_limits(0, 255); apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog); apm->gain_control()->set_target_level_dbfs(3); apm->gain_control()->set_compression_gain_db(9); apm->gain_control()->enable_limiter(true); apm->gain_control()->Enable(true); apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kLowLikelihood); apm->voice_detection()->Enable(true); #elif defined(WEBRTC1) || defined(WEBRTC2) webrtc::AudioProcessing::Config config; rtc::scoped_refptr apm = webrtc::AudioProcessingBuilder().Create(); native->apm = apm; config.high_pass_filter.enabled = true; config.echo_canceller.enabled = true; config.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::Level::kModerate; config.noise_suppression.enabled = true; config.gain_controller1.target_level_dbfs = 3; config.gain_controller1.compression_gain_db = 9; config.gain_controller1.enable_limiter = true; config.gain_controller1.enabled = true; #ifdef WEBRTC1 config.level_estimation.enabled = true; config.voice_detection.enabled = true; #endif apm->ApplyConfig(config); #endif return native; } extern "C" void dino_plugins_rtp_voice_processor_analyze_reverse_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; int err; #if defined(WEBRTC0) webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); webrtc::AudioProcessing *apm = native->apm; GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READ); webrtc::AudioFrame frame; frame.num_channels_ = info->channels; frame.sample_rate_hz_ = info->rate; frame.samples_per_channel_ = gst_buffer_get_size(buffer) / info->bpf; memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf); apm->set_stream_delay_ms (native->stream_delay); err = apm->AnalyzeReverseStream(&frame); gst_buffer_unmap(buffer, &map); #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm = native->apm; #ifdef WEBRTC1 webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); #else webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS); #endif GstAudioBuffer abuf; if (!gst_audio_buffer_map (&abuf, info, buffer, GST_MAP_READWRITE)) { g_warning("voice_processor_native.cpp: analyze_reverse_stream: gst_audio_buffer_map failed"); return; } apm->set_stream_delay_ms (native->stream_delay); auto * const data = (int16_t * const) abuf.planes[0]; err = apm->ProcessReverseStream (data, config, config, data); gst_audio_buffer_unmap (&abuf); #endif if (err < 0) g_warning("voice_processor_native.cpp: ProcessReverseStream %i", err); } extern "C" void dino_plugins_rtp_voice_processor_notify_gain_level(void *native_ptr, gint gain_level) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; #if defined(WEBRTC0) webrtc::AudioProcessing *apm = native->apm; apm->gain_control()->set_stream_analog_level(gain_level); #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm = native->apm; apm->set_stream_analog_level(gain_level); #endif } extern "C" gint dino_plugins_rtp_voice_processor_get_suggested_gain_level(void *native_ptr) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; int level = 0; #if defined(WEBRTC0) webrtc::AudioProcessing *apm = native->apm; level = apm->gain_control()->stream_analog_level(); #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm = native->apm; level = apm->recommended_stream_analog_level(); #endif return level; } extern "C" bool dino_plugins_rtp_voice_processor_get_stream_has_voice(void *native_ptr) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; bool has_voice = false; #if defined(WEBRTC0) webrtc::AudioProcessing *apm = native->apm; has_voice = apm->voice_detection()->stream_has_voice(); #elif defined(WEBRTC1) rtc::scoped_refptr apm = native->apm; webrtc::AudioProcessingStats stats = apm->GetStatistics (); has_voice = stats.voice_detected.value_or(false); #endif return has_voice; } extern "C" void dino_plugins_rtp_voice_processor_set_stream_delay(void *native_ptr, gint stream_delay) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; native->stream_delay = stream_delay; } extern "C" void dino_plugins_rtp_voice_processor_adjust_stream_delay(void *native_ptr) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; int median, std, poor_delays; float fraction_poor_delays; #if defined(WEBRTC0) webrtc::AudioProcessing *apm = native->apm; apm->echo_cancellation()->GetDelayMetrics(&median, &std, &fraction_poor_delays); poor_delays = (int)(fraction_poor_delays * 100.0); #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm = native->apm; webrtc::AudioProcessingStats stats = apm->GetStatistics(); median = stats.delay_median_ms.value_or(-1); std = stats.delay_standard_deviation_ms.value_or(-1); fraction_poor_delays = (float) stats.divergent_filter_fraction.value_or(-1.0); poor_delays = (int) (fraction_poor_delays * 100.0); #endif if (fraction_poor_delays < 0 || (native->last_median == median && native->last_poor_delays == poor_delays)) return; g_debug("voice_processor_native.cpp: Stream delay metrics: median=%i std=%i poor_delays=%i%%", median, std, poor_delays); native->last_median = median; native->last_poor_delays = poor_delays; #if defined(WEBRTC0) if (poor_delays > 90) { native->stream_delay = std::min(std::max(0, native->stream_delay + std::min(48, std::max(median, -48))), 384); g_debug("voice_processor_native.cpp: set stream_delay=%i", native->stream_delay); } #endif } extern "C" void dino_plugins_rtp_voice_processor_process_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; int err; #if defined(WEBRTC0) webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); webrtc::AudioProcessing *apm = native->apm; GstMapInfo map; gst_buffer_map(buffer, &map, GST_MAP_READWRITE); webrtc::AudioFrame frame; frame.num_channels_ = info->channels; frame.sample_rate_hz_ = info->rate; frame.samples_per_channel_ = info->rate / 100; memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf); err = apm->ProcessStream(&frame); if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf); gst_buffer_unmap(buffer, &map); #elif defined(WEBRTC1) || defined(WEBRTC2) rtc::scoped_refptr apm = native->apm; #ifdef WEBRTC1 webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false); #else webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS); #endif GstAudioBuffer abuf; if (!gst_audio_buffer_map (&abuf, info, buffer, GST_MAP_READWRITE)) { g_warning("voice_processor_native.cpp: process_stream: gst_audio_buffer_map failed"); return; } auto * const data = (int16_t * const) abuf.planes[0]; err = apm->ProcessStream (data, config, config, data); gst_audio_buffer_unmap (&abuf); #endif if (err < 0) g_warning("voice_processor_native.cpp: ProcessStream %i", err); } extern "C" void dino_plugins_rtp_voice_processor_destroy_native(void *native_ptr) { auto *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr; #if defined(WEBRTC0) delete native->apm; native->apm = NULL; #elif defined(WEBRTC1) || defined(WEBRTC2) native->apm = nullptr; #endif delete native; }dino-0.5.0/plugins/rtp/vapi/0000775000000000000000000000000014776241610014402 5ustar rootrootdino-0.5.0/plugins/rtp/vapi/gstreamer-base-1.0.vapi0000664000000000000000000013742414776241610020473 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-base-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstBase", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace Base { [CCode (cheader_filename = "gst/base/base.h", cname = "GstAdapter", lower_case_cprefix = "gst_adapter_", type_id = "gst_adapter_get_type ()")] [GIR (name = "Adapter")] public class Adapter : GLib.Object { [CCode (has_construct_function = false)] public Adapter (); public size_t available (); public size_t available_fast (); public void clear (); public void copy ([CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "gsize")] uint8[] dest, size_t offset); [Version (since = "1.4")] public GLib.Bytes copy_bytes (size_t offset, size_t size); [Version (since = "1.10")] public uint64 distance_from_discont (); [Version (since = "1.10")] public Gst.ClockTime dts_at_discont (); public void flush (size_t flush); [Version (since = "1.6")] public Gst.Buffer? get_buffer (size_t nbytes); [Version (since = "1.6")] public Gst.Buffer? get_buffer_fast (size_t nbytes); [Version (since = "1.6")] public Gst.BufferList? get_buffer_list (size_t nbytes); [Version (since = "1.6")] public GLib.List? get_list (size_t nbytes); [CCode (array_length = false)] public unowned uint8[]? map (size_t size); public ssize_t masked_scan_uint32 (uint32 mask, uint32 pattern, size_t offset, size_t size); public ssize_t masked_scan_uint32_peek (uint32 mask, uint32 pattern, size_t offset, size_t size, out uint32 value); [Version (since = "1.10")] public uint64 offset_at_discont (); public Gst.ClockTime prev_dts (out uint64 distance); [Version (since = "1.2")] public Gst.ClockTime prev_dts_at_offset (size_t offset, out uint64 distance); [Version (since = "1.10")] public uint64 prev_offset (out uint64 distance); public Gst.ClockTime prev_pts (out uint64 distance); [Version (since = "1.2")] public Gst.ClockTime prev_pts_at_offset (size_t offset, out uint64 distance); [Version (since = "1.10")] public Gst.ClockTime pts_at_discont (); public void push (owned Gst.Buffer buf); [CCode (array_length = false)] public uint8[]? take (size_t nbytes); public Gst.Buffer? take_buffer (size_t nbytes); [Version (since = "1.2")] public Gst.Buffer? take_buffer_fast (size_t nbytes); [Version (since = "1.6")] public Gst.BufferList? take_buffer_list (size_t nbytes); public GLib.List? take_list (size_t nbytes); public void unmap (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregator", lower_case_cprefix = "gst_aggregator_", type_id = "gst_aggregator_get_type ()")] [GIR (name = "Aggregator")] [Version (since = "1.14")] public abstract class Aggregator : Gst.Element { public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Aggregator (); [NoWrapper] public virtual Gst.FlowReturn aggregate (bool timeout); [NoWrapper] public virtual Gst.Buffer clip (Gst.Base.AggregatorPad aggregator_pad, Gst.Buffer buf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); public virtual Gst.FlowReturn finish_buffer (owned Gst.Buffer buffer); [Version (since = "1.18")] public virtual Gst.FlowReturn finish_buffer_list (owned Gst.BufferList bufferlist); [NoWrapper] public virtual Gst.Caps fixate_src_caps (Gst.Caps caps); [NoWrapper] public virtual Gst.FlowReturn flush (); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public Gst.BufferPool? get_buffer_pool (); [Version (since = "1.20")] public bool get_ignore_inactive_pads (); public Gst.ClockTime get_latency (); [NoWrapper] public virtual Gst.ClockTime get_next_time (); [Version (since = "1.18")] public virtual bool negotiate (); [NoWrapper] public virtual bool negotiated_src_caps (Gst.Caps caps); [Version (since = "1.18")] public virtual Gst.Sample? peek_next_sample (Gst.Base.AggregatorPad aggregator_pad); [NoWrapper] public virtual bool propose_allocation (Gst.Base.AggregatorPad pad, Gst.Query decide_query, Gst.Query query); [Version (since = "1.18")] public void selected_samples (Gst.ClockTime pts, Gst.ClockTime dts, Gst.ClockTime duration, Gst.Structure? info); [Version (since = "1.20")] public void set_ignore_inactive_pads (bool ignore); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_src_caps (Gst.Caps caps); [Version (since = "1.16")] public Gst.ClockTime simple_get_next_time (); [NoWrapper] public virtual bool sink_event (Gst.Base.AggregatorPad aggregator_pad, Gst.Event event); [NoWrapper] public virtual Gst.FlowReturn sink_event_pre_queue (Gst.Base.AggregatorPad aggregator_pad, Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Base.AggregatorPad aggregator_pad, Gst.Query query); [NoWrapper] public virtual bool sink_query_pre_queue (Gst.Base.AggregatorPad aggregator_pad, Gst.Query query); [NoWrapper] public virtual bool src_activate (Gst.PadMode mode, bool active); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [Version (since = "1.18")] public void update_segment (Gst.Segment segment); [NoWrapper] public virtual Gst.FlowReturn update_src_caps (Gst.Caps caps, out Gst.Caps ret); [NoAccessorMethod] [Version (since = "1.18")] public bool emit_signals { get; set; } [NoAccessorMethod] public uint64 latency { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public uint64 min_upstream_latency { get; set; } [NoAccessorMethod] public uint64 start_time { get; set; } [NoAccessorMethod] public Gst.Base.AggregatorStartTimeSelection start_time_selection { get; set; } [Version (since = "1.18")] public signal void samples_selected (Gst.Segment segment, uint64 pts, uint64 dts, uint64 duration, Gst.Structure? info); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregatorPad", lower_case_cprefix = "gst_aggregator_pad_", type_id = "gst_aggregator_pad_get_type ()")] [GIR (name = "AggregatorPad")] [Version (since = "1.14")] public class AggregatorPad : Gst.Pad { public weak Gst.Segment segment; [CCode (has_construct_function = false)] protected AggregatorPad (); public bool drop_buffer (); [NoWrapper] public virtual Gst.FlowReturn flush (Gst.Base.Aggregator aggregator); [Version (since = "1.14.1")] public bool has_buffer (); public bool is_eos (); [Version (since = "1.20")] public bool is_inactive (); public Gst.Buffer? peek_buffer (); public Gst.Buffer? pop_buffer (); [NoWrapper] public virtual bool skip_buffer (Gst.Base.Aggregator aggregator, Gst.Buffer buffer); [NoAccessorMethod] [Version (since = "1.16")] public bool emit_signals { get; set; } public signal void buffer_consumed (Gst.Buffer object); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstBitReader", free_function = "gst_bit_reader_free", has_type_id = false)] [Compact] [GIR (name = "BitReader")] public class BitReader { public uint bit; public uint byte; [CCode (array_length_cname = "size", array_length_type = "guint")] public weak uint8[] data; public uint size; [CCode (cname = "gst_bit_reader_new", has_construct_function = false)] public BitReader ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_reader_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_bit_reader_get_bits_uint16")] public bool get_bits_uint16 (out uint16 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint32")] public bool get_bits_uint32 (out uint32 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint64")] public bool get_bits_uint64 (out uint64 val, uint nbits); [CCode (cname = "gst_bit_reader_get_bits_uint8")] public bool get_bits_uint8 (out uint8 val, uint nbits); [CCode (cname = "gst_bit_reader_get_pos")] public uint get_pos (); [CCode (cname = "gst_bit_reader_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_bit_reader_get_size")] public uint get_size (); [CCode (cname = "gst_bit_reader_init")] public void init ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_reader_peek_bits_uint16")] public bool peek_bits_uint16 (out uint16 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint32")] public bool peek_bits_uint32 (out uint32 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint64")] public bool peek_bits_uint64 (out uint64 val, uint nbits); [CCode (cname = "gst_bit_reader_peek_bits_uint8")] public bool peek_bits_uint8 (out uint8 val, uint nbits); [CCode (cname = "gst_bit_reader_set_pos")] public bool set_pos (uint pos); [CCode (cname = "gst_bit_reader_skip")] public bool skip (uint nbits); [CCode (cname = "gst_bit_reader_skip_to_byte")] public bool skip_to_byte (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstByteReader", free_function = "gst_byte_reader_free", has_type_id = false)] [Compact] [GIR (name = "ByteReader")] public class ByteReader { public uint byte; [CCode (array_length_cname = "size", array_length_type = "guint")] public weak uint8[] data; public uint size; [CCode (cname = "gst_byte_reader_new", has_construct_function = false)] public ByteReader ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_reader_dup_data")] public bool dup_data (uint size, out uint8[] val); [CCode (cname = "gst_byte_reader_dup_string_utf16")] public bool dup_string_utf16 ([CCode (array_length = false, array_null_terminated = true)] out uint16[] str); [CCode (cname = "gst_byte_reader_dup_string_utf32")] public bool dup_string_utf32 ([CCode (array_length = false, array_null_terminated = true)] out uint32[] str); [CCode (cname = "gst_byte_reader_dup_string_utf8")] public bool dup_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out string[] str); [CCode (cname = "gst_byte_reader_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_byte_reader_get_data")] public bool get_data (uint size, out unowned uint8[] val); [CCode (cname = "gst_byte_reader_get_float32_be")] public bool get_float32_be (out float val); [CCode (cname = "gst_byte_reader_get_float32_le")] public bool get_float32_le (out float val); [CCode (cname = "gst_byte_reader_get_float64_be")] public bool get_float64_be (out double val); [CCode (cname = "gst_byte_reader_get_float64_le")] public bool get_float64_le (out double val); [CCode (cname = "gst_byte_reader_get_int16_be")] public bool get_int16_be (out int16 val); [CCode (cname = "gst_byte_reader_get_int16_le")] public bool get_int16_le (out int16 val); [CCode (cname = "gst_byte_reader_get_int24_be")] public bool get_int24_be (out int32 val); [CCode (cname = "gst_byte_reader_get_int24_le")] public bool get_int24_le (out int32 val); [CCode (cname = "gst_byte_reader_get_int32_be")] public bool get_int32_be (out int32 val); [CCode (cname = "gst_byte_reader_get_int32_le")] public bool get_int32_le (out int32 val); [CCode (cname = "gst_byte_reader_get_int64_be")] public bool get_int64_be (out int64 val); [CCode (cname = "gst_byte_reader_get_int64_le")] public bool get_int64_le (out int64 val); [CCode (cname = "gst_byte_reader_get_int8")] public bool get_int8 (out int8 val); [CCode (cname = "gst_byte_reader_get_pos")] public uint get_pos (); [CCode (cname = "gst_byte_reader_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_byte_reader_get_size")] public uint get_size (); [CCode (cname = "gst_byte_reader_get_string_utf8")] public bool get_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out unowned string[] str); [CCode (cname = "gst_byte_reader_get_uint16_be")] public bool get_uint16_be (out uint16 val); [CCode (cname = "gst_byte_reader_get_uint16_le")] public bool get_uint16_le (out uint16 val); [CCode (cname = "gst_byte_reader_get_uint24_be")] public bool get_uint24_be (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint24_le")] public bool get_uint24_le (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint32_be")] public bool get_uint32_be (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint32_le")] public bool get_uint32_le (out uint32 val); [CCode (cname = "gst_byte_reader_get_uint64_be")] public bool get_uint64_be (out uint64 val); [CCode (cname = "gst_byte_reader_get_uint64_le")] public bool get_uint64_le (out uint64 val); [CCode (cname = "gst_byte_reader_get_uint8")] public bool get_uint8 (out uint8 val); [CCode (cname = "gst_byte_reader_init")] public void init ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_reader_masked_scan_uint32")] public uint masked_scan_uint32 (uint32 mask, uint32 pattern, uint offset, uint size); [CCode (cname = "gst_byte_reader_masked_scan_uint32_peek")] [Version (since = "1.6")] public uint masked_scan_uint32_peek (uint32 mask, uint32 pattern, uint offset, uint size, out uint32 value); [CCode (cname = "gst_byte_reader_peek_data")] public bool peek_data (uint size, out unowned uint8[] val); [CCode (cname = "gst_byte_reader_peek_float32_be")] public bool peek_float32_be (out float val); [CCode (cname = "gst_byte_reader_peek_float32_le")] public bool peek_float32_le (out float val); [CCode (cname = "gst_byte_reader_peek_float64_be")] public bool peek_float64_be (out double val); [CCode (cname = "gst_byte_reader_peek_float64_le")] public bool peek_float64_le (out double val); [CCode (cname = "gst_byte_reader_peek_int16_be")] public bool peek_int16_be (out int16 val); [CCode (cname = "gst_byte_reader_peek_int16_le")] public bool peek_int16_le (out int16 val); [CCode (cname = "gst_byte_reader_peek_int24_be")] public bool peek_int24_be (out int32 val); [CCode (cname = "gst_byte_reader_peek_int24_le")] public bool peek_int24_le (out int32 val); [CCode (cname = "gst_byte_reader_peek_int32_be")] public bool peek_int32_be (out int32 val); [CCode (cname = "gst_byte_reader_peek_int32_le")] public bool peek_int32_le (out int32 val); [CCode (cname = "gst_byte_reader_peek_int64_be")] public bool peek_int64_be (out int64 val); [CCode (cname = "gst_byte_reader_peek_int64_le")] public bool peek_int64_le (out int64 val); [CCode (cname = "gst_byte_reader_peek_int8")] public bool peek_int8 (out int8 val); [CCode (cname = "gst_byte_reader_peek_string_utf8")] public bool peek_string_utf8 ([CCode (array_length = false, array_null_terminated = true)] out unowned string[] str); [CCode (cname = "gst_byte_reader_peek_uint16_be")] public bool peek_uint16_be (out uint16 val); [CCode (cname = "gst_byte_reader_peek_uint16_le")] public bool peek_uint16_le (out uint16 val); [CCode (cname = "gst_byte_reader_peek_uint24_be")] public bool peek_uint24_be (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint24_le")] public bool peek_uint24_le (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint32_be")] public bool peek_uint32_be (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint32_le")] public bool peek_uint32_le (out uint32 val); [CCode (cname = "gst_byte_reader_peek_uint64_be")] public bool peek_uint64_be (out uint64 val); [CCode (cname = "gst_byte_reader_peek_uint64_le")] public bool peek_uint64_le (out uint64 val); [CCode (cname = "gst_byte_reader_peek_uint8")] public bool peek_uint8 (out uint8 val); [CCode (cname = "gst_byte_reader_set_pos")] public bool set_pos (uint pos); [CCode (cname = "gst_byte_reader_skip")] public bool skip (uint nbytes); [CCode (cname = "gst_byte_reader_skip_string_utf16")] public bool skip_string_utf16 (); [CCode (cname = "gst_byte_reader_skip_string_utf32")] public bool skip_string_utf32 (); [CCode (cname = "gst_byte_reader_skip_string_utf8")] public bool skip_string_utf8 (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstByteWriter", free_function = "gst_byte_writer_free", has_type_id = false)] [Compact] [GIR (name = "ByteWriter")] public class ByteWriter { public uint alloc_size; public bool fixed; public bool @owned; public weak Gst.Base.ByteReader parent; [CCode (cname = "gst_byte_writer_new", has_construct_function = false)] public ByteWriter (); [CCode (cname = "gst_byte_writer_ensure_free_space")] public bool ensure_free_space (uint size); [CCode (cname = "gst_byte_writer_fill")] public bool fill (uint8 value, uint size); [CCode (cname = "gst_byte_writer_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_byte_writer_free_and_get_buffer")] [DestroysInstance] public Gst.Buffer free_and_get_buffer (); [CCode (cname = "gst_byte_writer_free_and_get_data")] [DestroysInstance] public uint8 free_and_get_data (); [CCode (cname = "gst_byte_writer_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_byte_writer_init")] public void init (); [CCode (cname = "gst_byte_writer_init_with_data")] public void init_with_data ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, bool initialized); [CCode (cname = "gst_byte_writer_init_with_size")] public void init_with_size (uint size, bool fixed); [CCode (cname = "gst_byte_writer_put_buffer")] public bool put_buffer (Gst.Buffer buffer, size_t offset, ssize_t size); [CCode (cname = "gst_byte_writer_put_data")] public bool put_data ([CCode (array_length_cname = "size", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_byte_writer_put_float32_be")] public bool put_float32_be (float val); [CCode (cname = "gst_byte_writer_put_float32_le")] public bool put_float32_le (float val); [CCode (cname = "gst_byte_writer_put_float64_be")] public bool put_float64_be (double val); [CCode (cname = "gst_byte_writer_put_float64_le")] public bool put_float64_le (double val); [CCode (cname = "gst_byte_writer_put_int16_be")] public bool put_int16_be (int16 val); [CCode (cname = "gst_byte_writer_put_int16_le")] public bool put_int16_le (int16 val); [CCode (cname = "gst_byte_writer_put_int24_be")] public bool put_int24_be (int32 val); [CCode (cname = "gst_byte_writer_put_int24_le")] public bool put_int24_le (int32 val); [CCode (cname = "gst_byte_writer_put_int32_be")] public bool put_int32_be (int32 val); [CCode (cname = "gst_byte_writer_put_int32_le")] public bool put_int32_le (int32 val); [CCode (cname = "gst_byte_writer_put_int64_be")] public bool put_int64_be (int64 val); [CCode (cname = "gst_byte_writer_put_int64_le")] public bool put_int64_le (int64 val); [CCode (cname = "gst_byte_writer_put_int8")] public bool put_int8 (int8 val); [CCode (cname = "gst_byte_writer_put_string_utf16")] public bool put_string_utf16 ([CCode (array_length = false, array_null_terminated = true)] uint16[] data); [CCode (cname = "gst_byte_writer_put_string_utf32")] public bool put_string_utf32 ([CCode (array_length = false, array_null_terminated = true)] uint32[] data); [CCode (cname = "gst_byte_writer_put_string_utf8")] public bool put_string_utf8 (string data); [CCode (cname = "gst_byte_writer_put_uint16_be")] public bool put_uint16_be (uint16 val); [CCode (cname = "gst_byte_writer_put_uint16_le")] public bool put_uint16_le (uint16 val); [CCode (cname = "gst_byte_writer_put_uint24_be")] public bool put_uint24_be (uint32 val); [CCode (cname = "gst_byte_writer_put_uint24_le")] public bool put_uint24_le (uint32 val); [CCode (cname = "gst_byte_writer_put_uint32_be")] public bool put_uint32_be (uint32 val); [CCode (cname = "gst_byte_writer_put_uint32_le")] public bool put_uint32_le (uint32 val); [CCode (cname = "gst_byte_writer_put_uint64_be")] public bool put_uint64_be (uint64 val); [CCode (cname = "gst_byte_writer_put_uint64_le")] public bool put_uint64_le (uint64 val); [CCode (cname = "gst_byte_writer_put_uint8")] public bool put_uint8 (uint8 val); [CCode (cname = "gst_byte_writer_reset")] public void reset (); [CCode (cname = "gst_byte_writer_reset_and_get_buffer")] public Gst.Buffer reset_and_get_buffer (); [CCode (array_length = false, cname = "gst_byte_writer_reset_and_get_data")] public uint8[] reset_and_get_data (); [CCode (cname = "gst_byte_writer_new_with_data", has_construct_function = false)] public ByteWriter.with_data ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint", type = "guint8*")] uint8[] data, bool initialized); [CCode (cname = "gst_byte_writer_new_with_size", has_construct_function = false)] public ByteWriter.with_size (uint size, bool fixed); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPads", lower_case_cprefix = "gst_collect_pads_", type_id = "gst_collect_pads_get_type ()")] [GIR (name = "CollectPads")] public class CollectPads : Gst.Object { public weak GLib.SList data; [CCode (has_construct_function = false)] public CollectPads (); public unowned Gst.Base.CollectData? add_pad (Gst.Pad pad, uint size, [CCode (scope = "async")] Gst.Base.CollectDataDestroyNotify destroy_notify, bool @lock); public uint available (); public Gst.FlowReturn clip_running_time (Gst.Base.CollectData cdata, Gst.Buffer buf, out Gst.Buffer outbuf, void* user_data); public bool event_default (Gst.Base.CollectData data, Gst.Event event, bool discard); public uint flush (Gst.Base.CollectData data, uint size); public Gst.Buffer? peek (Gst.Base.CollectData data); public Gst.Buffer? pop (Gst.Base.CollectData data); public bool query_default (Gst.Base.CollectData data, Gst.Query query, bool discard); public Gst.Buffer? read_buffer (Gst.Base.CollectData data, uint size); public bool remove_pad (Gst.Pad pad); public void set_buffer_function (Gst.Base.CollectPadsBufferFunction func); public void set_clip_function (Gst.Base.CollectPadsClipFunction clipfunc); public void set_compare_function (Gst.Base.CollectPadsCompareFunction func); public void set_event_function (Gst.Base.CollectPadsEventFunction func); [Version (since = "1.4")] public void set_flush_function (Gst.Base.CollectPadsFlushFunction func); public void set_flushing (bool flushing); public void set_function (Gst.Base.CollectPadsFunction func); public void set_query_function (Gst.Base.CollectPadsQueryFunction func); public void set_waiting (Gst.Base.CollectData data, bool waiting); [Version (since = "1.4")] public bool src_event_default (Gst.Pad pad, Gst.Event event); public void start (); public void stop (); public Gst.Buffer? take_buffer (Gst.Base.CollectData data, uint size); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueue", lower_case_cprefix = "gst_data_queue_", type_id = "gst_data_queue_get_type ()")] [GIR (name = "DataQueue")] public class DataQueue : GLib.Object { [CCode (has_construct_function = false)] protected DataQueue (); [NoWrapper] public virtual void empty (); [NoWrapper] public virtual void full (); [NoAccessorMethod] public uint current_level_bytes { get; } [NoAccessorMethod] public uint64 current_level_time { get; } [NoAccessorMethod] public uint current_level_visible { get; } } [CCode (cheader_filename = "gst/base/base.h", cname = "GstFlowCombiner", copy_function = "g_boxed_copy", free_function = "g_boxed_free", lower_case_cprefix = "gst_flow_combiner_", type_id = "gst_flow_combiner_get_type ()")] [Compact] [GIR (name = "FlowCombiner")] [Version (since = "1.4")] public class FlowCombiner { [CCode (has_construct_function = false)] public FlowCombiner (); public void add_pad (Gst.Pad pad); [Version (since = "1.6")] public void clear (); public void free (); [Version (since = "1.12.1")] public unowned Gst.Base.FlowCombiner @ref (); public void remove_pad (Gst.Pad pad); [Version (since = "1.6")] public void reset (); [Version (since = "1.12.1")] public void unref (); public Gst.FlowReturn update_flow (Gst.FlowReturn fret); [Version (since = "1.6")] public Gst.FlowReturn update_pad_flow (Gst.Pad pad, Gst.FlowReturn fret); } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_parse_get_type ()")] [GIR (name = "BaseParse")] public abstract class Parse : Gst.Element { public uint flags; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Parse (); public bool add_index_entry (uint64 offset, Gst.ClockTime ts, bool key, bool force); [NoWrapper] public virtual bool convert (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); public bool convert_default (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); [NoWrapper] public virtual Gst.FlowReturn detect (Gst.Buffer buffer); [Version (since = "1.12")] public void drain (); public Gst.FlowReturn finish_frame (Gst.Base.ParseFrame frame, int size); [NoWrapper] public virtual Gst.Caps get_sink_caps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (Gst.Base.ParseFrame frame, out int skipsize); [Version (since = "1.6")] public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); [NoWrapper] public virtual Gst.FlowReturn pre_push_frame (Gst.Base.ParseFrame frame); public Gst.FlowReturn push_frame (Gst.Base.ParseFrame frame); public void set_average_bitrate (uint bitrate); public void set_duration (Gst.Format fmt, int64 duration, int interval); public void set_frame_rate (uint fps_num, uint fps_den, uint lead_in, uint lead_out); public void set_has_timing_info (bool has_timing); public void set_infer_ts (bool infer_ts); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_min_frame_size (uint min_size); public void set_passthrough (bool passthrough); public void set_pts_interpolation (bool pts_interpolate); [NoWrapper] public virtual bool set_sink_caps (Gst.Caps caps); public void set_syncable (bool syncable); [Version (since = "1.2")] public void set_ts_at_offset (size_t offset); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoAccessorMethod] public bool disable_passthrough { get; set; } } [CCode (cheader_filename = "gst/base/base.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_base_parse_frame_get_type ()")] [Compact] [GIR (name = "BaseParseFrame")] public class ParseFrame { public weak Gst.Buffer buffer; public uint flags; public uint64 offset; public weak Gst.Buffer out_buffer; public int overhead; [CCode (has_construct_function = false)] public ParseFrame (Gst.Buffer buffer, Gst.Base.ParseFrameFlags flags, int overhead); [Version (since = "1.12.1")] public Gst.Base.ParseFrame copy (); public void free (); public void init (); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstPushSrc", lower_case_cprefix = "gst_push_src_", type_id = "gst_push_src_get_type ()")] [GIR (name = "PushSrc")] public class PushSrc : Gst.Base.Src { [CCode (has_construct_function = false)] protected PushSrc (); [NoWrapper] public virtual Gst.FlowReturn alloc (out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn create (out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn fill (Gst.Buffer buf); } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_sink_get_type ()")] [GIR (name = "BaseSink")] public abstract class Sink : Gst.Element { public bool can_activate_pull; public bool can_activate_push; public bool eos; public bool have_newsegment; public bool have_preroll; public bool need_preroll; public uint64 offset; public Gst.PadMode pad_mode; public bool playing_async; public GLib.Cond preroll_cond; public GLib.Mutex preroll_lock; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; [CCode (has_construct_function = false)] protected Sink (); [NoWrapper] public virtual bool activate_pull (bool active); public Gst.FlowReturn do_preroll (Gst.MiniObject obj); [NoWrapper] public virtual bool event (Gst.Event event); [NoWrapper] public virtual Gst.Caps fixate (Gst.Caps caps); public uint get_blocksize (); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Caps? filter); [Version (since = "1.12")] public bool get_drop_out_of_segment (); public Gst.Sample? get_last_sample (); public Gst.ClockTime get_latency (); [Version (since = "1.2")] public uint64 get_max_bitrate (); public int64 get_max_lateness (); [Version (since = "1.16")] public Gst.ClockTime get_processing_deadline (); public Gst.ClockTime get_render_delay (); [Version (since = "1.18")] public Gst.Structure get_stats (); public bool get_sync (); public uint64 get_throttle_time (); [NoWrapper] public virtual void get_times (Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end); public Gst.ClockTimeDiff get_ts_offset (); public bool is_async_enabled (); public bool is_last_sample_enabled (); public bool is_qos_enabled (); [NoWrapper] public virtual Gst.FlowReturn prepare (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn prepare_list (Gst.BufferList buffer_list); [NoWrapper] public virtual Gst.FlowReturn preroll (Gst.Buffer buffer); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); [NoWrapper] public virtual bool query (Gst.Query query); public bool query_latency (out bool live, out bool upstream_live, out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); [NoWrapper] public virtual Gst.FlowReturn render (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn render_list (Gst.BufferList buffer_list); public void set_async_enabled (bool enabled); public void set_blocksize (uint blocksize); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); [Version (since = "1.12")] public void set_drop_out_of_segment (bool drop_out_of_segment); public void set_last_sample_enabled (bool enabled); [Version (since = "1.2")] public void set_max_bitrate (uint64 max_bitrate); public void set_max_lateness (int64 max_lateness); [Version (since = "1.16")] public void set_processing_deadline (Gst.ClockTime processing_deadline); public void set_qos_enabled (bool enabled); public void set_render_delay (Gst.ClockTime delay); public void set_sync (bool sync); public void set_throttle_time (uint64 throttle); public void set_ts_offset (Gst.ClockTimeDiff offset); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool @unlock (); [NoWrapper] public virtual bool unlock_stop (); public Gst.FlowReturn wait (Gst.ClockTime time, out Gst.ClockTimeDiff jitter); public Gst.ClockReturn wait_clock (Gst.ClockTime time, out Gst.ClockTimeDiff jitter); [NoWrapper] public virtual Gst.FlowReturn wait_event (Gst.Event event); public Gst.FlowReturn wait_preroll (); [NoAccessorMethod] public bool @async { get; set; } public uint blocksize { get; set; } [NoAccessorMethod] public bool enable_last_sample { get; set; } public Gst.Sample last_sample { owned get; } [Version (since = "1.2")] public uint64 max_bitrate { get; set; } public int64 max_lateness { get; set; } [Version (since = "1.16")] public uint64 processing_deadline { get; set; } [NoAccessorMethod] public bool qos { get; set; } public uint64 render_delay { get; set; } [Version (since = "1.18")] public Gst.Structure stats { owned get; } public bool sync { get; set; } public uint64 throttle_time { get; set; } public int64 ts_offset { get; set; } } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_src_get_type ()")] [GIR (name = "BaseSrc")] public abstract class Src : Gst.Element { public bool can_activate_push; public Gst.ClockID clock_id; public GLib.Cond live_cond; public GLib.Mutex live_lock; public bool live_running; public bool need_newsegment; public int num_buffers_left; public weak Gst.Event pending_seek; public bool random_access; public bool running; public weak Gst.Segment segment; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Src (); [NoWrapper] public virtual Gst.FlowReturn alloc (uint64 offset, uint size, out Gst.Buffer buf); [NoWrapper] public virtual Gst.FlowReturn create (uint64 offset, uint size, ref Gst.Buffer buf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual bool do_seek (Gst.Segment segment); [NoWrapper] public virtual bool event (Gst.Event event); [NoWrapper] public virtual Gst.FlowReturn fill (uint64 offset, uint size, Gst.Buffer buf); [NoWrapper] public virtual Gst.Caps fixate (Gst.Caps caps); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public uint get_blocksize (); public Gst.BufferPool? get_buffer_pool (); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Caps? filter); public bool get_do_timestamp (); [NoWrapper] public virtual bool get_size (out uint64 size); [NoWrapper] public virtual void get_times (Gst.Buffer buffer, out Gst.ClockTime start, out Gst.ClockTime end); public bool is_async (); [NoWrapper] public virtual bool is_seekable (); [Version (since = "1.18")] public virtual bool negotiate (); [Version (deprecated = true, deprecated_since = "1.18")] public bool new_seamless_segment (int64 start, int64 stop, int64 time); [Version (since = "1.18")] public bool new_segment (Gst.Segment segment); [NoWrapper] public virtual bool prepare_seek_segment (Gst.Event seek, Gst.Segment segment); [NoWrapper] public virtual bool query (Gst.Query query); public bool query_latency (out bool live, out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); public void set_async (bool @async); [Version (since = "1.4")] public void set_automatic_eos (bool automatic_eos); public void set_blocksize (uint blocksize); public virtual bool set_caps (Gst.Caps caps); public void set_do_timestamp (bool timestamp); public void set_dynamic_size (bool @dynamic); public void set_format (Gst.Format format); public void set_live (bool live); [NoWrapper] public virtual bool start (); public void start_complete (Gst.FlowReturn ret); public Gst.FlowReturn start_wait (); [NoWrapper] public virtual bool stop (); [Version (since = "1.14")] public void submit_buffer_list (owned Gst.BufferList buffer_list); [NoWrapper] public virtual bool @unlock (); [NoWrapper] public virtual bool unlock_stop (); public Gst.FlowReturn wait_playing (); public uint blocksize { get; set; } public bool do_timestamp { get; set; } [NoAccessorMethod] public int num_buffers { get; set; } [NoAccessorMethod] public bool typefind { get; set; } } [CCode (cheader_filename = "gst/base/base.h", type_id = "gst_base_transform_get_type ()")] [GIR (name = "BaseTransform")] public abstract class Transform : Gst.Element { public bool have_segment; public weak Gst.Buffer queued_buf; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected Transform (); [NoWrapper] public virtual bool accept_caps (Gst.PadDirection direction, Gst.Caps caps); [NoWrapper] public virtual void before_transform (Gst.Buffer buffer); [NoWrapper] public virtual bool copy_metadata (Gst.Buffer input, Gst.Buffer outbuf); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual bool filter_meta (Gst.Query query, GLib.Type api, Gst.Structure @params); [NoWrapper] public virtual Gst.Caps fixate_caps (Gst.PadDirection direction, Gst.Caps caps, owned Gst.Caps othercaps); [NoWrapper] public virtual Gst.FlowReturn generate_output (out Gst.Buffer outbuf); public void get_allocator (out Gst.Allocator? allocator, out unowned Gst.AllocationParams @params); public Gst.BufferPool? get_buffer_pool (); [NoWrapper] public virtual bool get_unit_size (Gst.Caps caps, out size_t size); public bool is_in_place (); public bool is_passthrough (); public bool is_qos_enabled (); [NoWrapper] public virtual Gst.FlowReturn prepare_output_buffer (Gst.Buffer input, out Gst.Buffer outbuf); [NoWrapper] public virtual bool propose_allocation (Gst.Query decide_query, Gst.Query query); [NoWrapper] public virtual bool query (Gst.PadDirection direction, Gst.Query query); [Version (since = "1.18")] public bool reconfigure (); public void reconfigure_sink (); public void reconfigure_src (); [NoWrapper] public virtual bool set_caps (Gst.Caps incaps, Gst.Caps outcaps); public void set_gap_aware (bool gap_aware); public void set_in_place (bool in_place); public void set_passthrough (bool passthrough); [Version (since = "1.0.1")] public void set_prefer_passthrough (bool prefer_passthrough); public void set_qos_enabled (bool enabled); [NoWrapper] public virtual bool sink_event (owned Gst.Event event); [NoWrapper] public virtual bool src_event (owned Gst.Event event); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual Gst.FlowReturn submit_input_buffer (bool is_discont, Gst.Buffer input); [NoWrapper] public virtual Gst.FlowReturn transform (Gst.Buffer inbuf, Gst.Buffer outbuf); [NoWrapper] public virtual Gst.Caps transform_caps (Gst.PadDirection direction, Gst.Caps caps, Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn transform_ip (Gst.Buffer buf); [NoWrapper] public virtual bool transform_meta (Gst.Buffer outbuf, Gst.Meta meta, Gst.Buffer inbuf); [NoWrapper] public virtual bool transform_size (Gst.PadDirection direction, Gst.Caps caps, size_t size, Gst.Caps othercaps, out size_t othersize); public void update_qos (double proportion, Gst.ClockTimeDiff diff, Gst.ClockTime timestamp); [Version (since = "1.6")] public bool update_src_caps (Gst.Caps updated_caps); [NoAccessorMethod] public bool qos { get; set; } } [CCode (cheader_filename = "gst/base/base.h", cname = "GstBitWriter", has_type_id = false)] [GIR (name = "BitWriter")] [Version (since = "1.16")] public struct BitWriter { public uint8 data; public uint bit_size; [CCode (cname = "gst_bit_writer_align_bytes")] public bool align_bytes (uint8 trailing_bit); [CCode (cname = "gst_bit_writer_free")] [DestroysInstance] public void free (); [CCode (cname = "gst_bit_writer_free_and_get_buffer")] [DestroysInstance] public Gst.Buffer free_and_get_buffer (); [CCode (array_length = false, cname = "gst_bit_writer_free_and_get_data")] [DestroysInstance] public uint8[] free_and_get_data (); [CCode (array_length = false, cname = "gst_bit_writer_get_data")] public unowned uint8[] get_data (); [CCode (cname = "gst_bit_writer_get_remaining")] public uint get_remaining (); [CCode (cname = "gst_bit_writer_get_size")] public uint get_size (); [CCode (cname = "gst_bit_writer_put_bits_uint16")] public bool put_bits_uint16 (uint16 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint32")] public bool put_bits_uint32 (uint32 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint64")] public bool put_bits_uint64 (uint64 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bits_uint8")] public bool put_bits_uint8 (uint8 value, uint nbits); [CCode (cname = "gst_bit_writer_put_bytes")] public bool put_bytes ([CCode (array_length_cname = "nbytes", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cname = "gst_bit_writer_reset")] public void reset (); [CCode (cname = "gst_bit_writer_reset_and_get_buffer")] public Gst.Buffer reset_and_get_buffer (); [CCode (array_length = false, cname = "gst_bit_writer_reset_and_get_data")] public uint8[] reset_and_get_data (); [CCode (cname = "gst_bit_writer_set_pos")] public bool set_pos (uint pos); } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectData", has_type_id = false)] [GIR (name = "CollectData")] public struct CollectData { public weak Gst.Base.CollectPads collect; public weak Gst.Pad pad; public weak Gst.Buffer buffer; public uint pos; public weak Gst.Segment segment; [CCode (cname = "ABI.abi.dts")] public int64 ABI_abi_dts; } [CCode (cheader_filename = "gst/base/base.h", cname = "GstAggregatorStartTimeSelection", cprefix = "GST_AGGREGATOR_START_TIME_SELECTION_", type_id = "gst_aggregator_start_time_selection_get_type ()")] [GIR (name = "AggregatorStartTimeSelection")] [Version (since = "1.18")] public enum AggregatorStartTimeSelection { ZERO, FIRST, SET } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsStateFlags", cprefix = "GST_COLLECT_PADS_STATE_", has_type_id = false)] [Flags] [GIR (name = "CollectPadsStateFlags")] public enum CollectPadsStateFlags { EOS, FLUSHING, NEW_SEGMENT, WAITING, LOCKED } [CCode (cheader_filename = "gst/base/base.h", cprefix = "GST_BASE_PARSE_FRAME_FLAG_", has_type_id = false)] [Flags] [GIR (name = "BaseParseFrameFlags")] public enum ParseFrameFlags { NONE, NEW_FRAME, NO_FRAME, CLIP, DROP, QUEUE } [CCode (cheader_filename = "gst/base/base.h", cprefix = "GST_BASE_SRC_FLAG_", has_type_id = false)] [Flags] [GIR (name = "BaseSrcFlags")] public enum SrcFlags { STARTING, STARTED, LAST } [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectDataDestroyNotify", has_target = false)] public delegate void CollectDataDestroyNotify (Gst.Base.CollectData data); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsBufferFunction", instance_pos = 3.9)] public delegate Gst.FlowReturn CollectPadsBufferFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data, owned Gst.Buffer buffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsClipFunction", instance_pos = 4.9)] public delegate Gst.FlowReturn CollectPadsClipFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data, owned Gst.Buffer inbuffer, out Gst.Buffer outbuffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsCompareFunction", instance_pos = 5.9)] public delegate int CollectPadsCompareFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData data1, Gst.ClockTime timestamp1, Gst.Base.CollectData data2, Gst.ClockTime timestamp2); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsEventFunction", instance_pos = 3.9)] public delegate bool CollectPadsEventFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData pad, Gst.Event event); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsFlushFunction", instance_pos = 1.9)] [Version (since = "1.4")] public delegate void CollectPadsFlushFunction (Gst.Base.CollectPads pads); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsFunction", instance_pos = 1.9)] public delegate Gst.FlowReturn CollectPadsFunction (Gst.Base.CollectPads pads); [CCode (cheader_filename = "gst/base/base.h", cname = "GstCollectPadsQueryFunction", instance_pos = 3.9)] public delegate bool CollectPadsQueryFunction (Gst.Base.CollectPads pads, Gst.Base.CollectData pad, Gst.Query query); [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueueEmptyCallback", has_target = false)] public delegate void DataQueueEmptyCallback (Gst.Base.DataQueue queue, void* checkdata); [CCode (cheader_filename = "gst/base/base.h", cname = "GstDataQueueFullCallback", has_target = false)] public delegate void DataQueueFullCallback (Gst.Base.DataQueue queue, void* checkdata); [CCode (cheader_filename = "gst/base/base.h", cname = "GstTypeFindHelperGetRangeFunction", has_target = false)] public delegate Gst.FlowReturn TypeFindHelperGetRangeFunction (Gst.Object obj, Gst.Object? parent, uint64 offset, uint length, out Gst.Buffer buffer); [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_PARSE_FLAG_DRAINING")] public const int PARSE_FLAG_DRAINING; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_PARSE_FLAG_LOST_SYNC")] public const int PARSE_FLAG_LOST_SYNC; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_TRANSFORM_SINK_NAME")] public const string TRANSFORM_SINK_NAME; [CCode (cheader_filename = "gst/base/base.h", cname = "GST_BASE_TRANSFORM_SRC_NAME")] public const string TRANSFORM_SRC_NAME; [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper")] public static Gst.Caps? type_find_helper (Gst.Pad src, uint64 size); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_buffer")] public static Gst.Caps? type_find_helper_for_buffer (Gst.Object? obj, Gst.Buffer buf, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_buffer_with_extension")] [Version (since = "1.16")] public static Gst.Caps? type_find_helper_for_buffer_with_extension (Gst.Object? obj, Gst.Buffer buf, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_data")] public static Gst.Caps? type_find_helper_for_data (Gst.Object? obj, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_data_with_extension")] [Version (since = "1.16")] public static Gst.Caps? type_find_helper_for_data_with_extension (Gst.Object? obj, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_for_extension")] public static Gst.Caps? type_find_helper_for_extension (Gst.Object? obj, string extension); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_get_range")] public static Gst.Caps? type_find_helper_get_range (Gst.Object obj, Gst.Object? parent, Gst.Base.TypeFindHelperGetRangeFunction func, uint64 size, string? extension, out Gst.TypeFindProbability prob); [CCode (cheader_filename = "gst/base/base.h", cname = "gst_type_find_helper_get_range_full")] [Version (since = "1.14.3")] public static Gst.FlowReturn type_find_helper_get_range_full (Gst.Object obj, Gst.Object? parent, Gst.Base.TypeFindHelperGetRangeFunction func, uint64 size, string? extension, out Gst.Caps caps, out Gst.TypeFindProbability prob); } } dino-0.5.0/plugins/rtp/vapi/gstreamer-rtp-1.0.vapi0000664000000000000000000010335414776241610020361 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-rtp-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstRtp", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace RTCP { [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTCPBuffer")] public struct Buffer { public weak Gst.Buffer buffer; public bool add_packet (Gst.RTCP.Type type, Gst.RTCP.Packet packet); public bool get_first_packet (Gst.RTCP.Packet packet); public uint get_packet_count (); public static bool map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTCP.Buffer rtcp); public static Gst.Buffer @new (uint mtu); public static Gst.Buffer new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); public static Gst.Buffer new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] owned uint8[] data); public bool unmap (); public static bool validate (Gst.Buffer buffer); public static bool validate_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [Version (since = "1.6")] public static bool validate_data_reduced ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [Version (since = "1.6")] public static bool validate_reduced (Gst.Buffer buffer); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTCPPacket")] public struct Packet { public weak Gst.RTCP.Buffer? rtcp; public uint offset; [Version (since = "1.10")] public bool add_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); public bool add_rb (uint32 ssrc, uint8 fractionlost, int32 packetslost, uint32 exthighestseq, uint32 jitter, uint32 lsr, uint32 dlsr); [Version (since = "1.10")] public uint8 app_get_data (); [Version (since = "1.10")] public uint16 app_get_data_length (); [Version (since = "1.10")] public unowned string app_get_name (); [Version (since = "1.10")] public uint32 app_get_ssrc (); [Version (since = "1.10")] public uint8 app_get_subtype (); [Version (since = "1.10")] public bool app_set_data_length (uint16 wordlen); [Version (since = "1.10")] public void app_set_name (string name); [Version (since = "1.10")] public void app_set_ssrc (uint32 ssrc); [Version (since = "1.10")] public void app_set_subtype (uint8 subtype); public bool bye_add_ssrc (uint32 ssrc); public bool bye_add_ssrcs ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint32[] ssrc); public uint32 bye_get_nth_ssrc (uint nth); public string bye_get_reason (); public uint8 bye_get_reason_len (); public uint bye_get_ssrc_count (); public bool bye_set_reason (string reason); [Version (since = "1.10")] public bool copy_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] out uint8[] data); public uint8 fb_get_fci (); public uint16 fb_get_fci_length (); public uint32 fb_get_media_ssrc (); public uint32 fb_get_sender_ssrc (); public Gst.RTCP.FBType fb_get_type (); public bool fb_set_fci_length (uint16 wordlen); public void fb_set_media_ssrc (uint32 ssrc); public void fb_set_sender_ssrc (uint32 ssrc); public void fb_set_type (Gst.RTCP.FBType type); public uint8 get_count (); public uint16 get_length (); public bool get_padding (); [Version (since = "1.10")] public bool get_profile_specific_ext ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] out unowned uint8[] data); [Version (since = "1.10")] public uint16 get_profile_specific_ext_length (); public void get_rb (uint nth, out uint32 ssrc, out uint8 fractionlost, out int32 packetslost, out uint32 exthighestseq, out uint32 jitter, out uint32 lsr, out uint32 dlsr); public uint get_rb_count (); public Gst.RTCP.Type get_type (); public bool move_to_next (); public bool remove (); public uint32 rr_get_ssrc (); public void rr_set_ssrc (uint32 ssrc); public bool sdes_add_entry (Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] uint8[] data); public bool sdes_add_item (uint32 ssrc); public bool sdes_copy_entry (out Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] out uint8[] data); public bool sdes_first_entry (); public bool sdes_first_item (); public bool sdes_get_entry (out Gst.RTCP.SDESType type, [CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint8")] out unowned uint8[] data); public uint sdes_get_item_count (); public uint32 sdes_get_ssrc (); public bool sdes_next_entry (); public bool sdes_next_item (); public void set_rb (uint nth, uint32 ssrc, uint8 fractionlost, int32 packetslost, uint32 exthighestseq, uint32 jitter, uint32 lsr, uint32 dlsr); public void sr_get_sender_info (out uint32 ssrc, out uint64 ntptime, out uint32 rtptime, out uint32 packet_count, out uint32 octet_count); public void sr_set_sender_info (uint32 ssrc, uint64 ntptime, uint32 rtptime, uint32 packet_count, uint32 octet_count); [Version (since = "1.16")] public bool xr_first_rb (); [Version (since = "1.16")] public uint16 xr_get_block_length (); [Version (since = "1.16")] public Gst.RTCP.XRType xr_get_block_type (); [Version (since = "1.16")] public bool xr_get_dlrr_block (uint nth, out uint32 ssrc, out uint32 last_rr, out uint32 delay); [Version (since = "1.16")] public bool xr_get_prt_by_seq (uint16 seq, out uint32 receipt_time); [Version (since = "1.16")] public bool xr_get_prt_info (out uint32 ssrc, out uint8 thinning, out uint16 begin_seq, out uint16 end_seq); [Version (since = "1.16")] public bool xr_get_rle_info (out uint32 ssrc, out uint8 thinning, out uint16 begin_seq, out uint16 end_seq, out uint32 chunk_count); [Version (since = "1.16")] public bool xr_get_rle_nth_chunk (uint nth, out uint16 chunk); [Version (since = "1.16")] public bool xr_get_rrt (out uint64 timestamp); [Version (since = "1.16")] public uint32 xr_get_ssrc (); [Version (since = "1.16")] public bool xr_get_summary_info (out uint32 ssrc, out uint16 begin_seq, out uint16 end_seq); [Version (since = "1.16")] public bool xr_get_summary_jitter (out uint32 min_jitter, out uint32 max_jitter, out uint32 mean_jitter, out uint32 dev_jitter); [Version (since = "1.16")] public bool xr_get_summary_pkt (out uint32 lost_packets, out uint32 dup_packets); [Version (since = "1.16")] public bool xr_get_summary_ttl (out bool is_ipv4, out uint8 min_ttl, out uint8 max_ttl, out uint8 mean_ttl, out uint8 dev_ttl); [Version (since = "1.16")] public bool xr_get_voip_burst_metrics (out uint8 burst_density, out uint8 gap_density, out uint16 burst_duration, out uint16 gap_duration); [Version (since = "1.16")] public bool xr_get_voip_configuration_params (out uint8 gmin, out uint8 rx_config); [Version (since = "1.16")] public bool xr_get_voip_delay_metrics (out uint16 roundtrip_delay, out uint16 end_system_delay); [Version (since = "1.16")] public bool xr_get_voip_jitter_buffer_params (out uint16 jb_nominal, out uint16 jb_maximum, out uint16 jb_abs_max); [Version (since = "1.16")] public bool xr_get_voip_metrics_ssrc (out uint32 ssrc); [Version (since = "1.16")] public bool xr_get_voip_packet_metrics (out uint8 loss_rate, out uint8 discard_rate); [Version (since = "1.16")] public bool xr_get_voip_quality_metrics (out uint8 r_factor, out uint8 ext_r_factor, out uint8 mos_lq, out uint8 mos_cq); [Version (since = "1.16")] public bool xr_get_voip_signal_metrics (out uint8 signal_level, out uint8 noise_level, out uint8 rerl, out uint8 gmin); [Version (since = "1.16")] public bool xr_next_rb (); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_", type_id = "gst_rtcpfb_type_get_type ()")] [GIR (name = "RTCPFBType")] public enum FBType { FB_TYPE_INVALID, RTPFB_TYPE_NACK, RTPFB_TYPE_TMMBR, RTPFB_TYPE_TMMBN, RTPFB_TYPE_RTCP_SR_REQ, RTPFB_TYPE_TWCC, PSFB_TYPE_PLI, PSFB_TYPE_SLI, PSFB_TYPE_RPSI, PSFB_TYPE_AFB, PSFB_TYPE_FIR, PSFB_TYPE_TSTR, PSFB_TYPE_TSTN, PSFB_TYPE_VBCN } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_SDES_", type_id = "gst_rtcpsdes_type_get_type ()")] [GIR (name = "RTCPSDESType")] public enum SDESType { INVALID, END, CNAME, NAME, EMAIL, PHONE, LOC, TOOL, NOTE, PRIV, [Version (since = "1.20")] H323_CADDR, [Version (since = "1.20")] APSI, [Version (since = "1.20")] RGRP, [Version (since = "1.20")] RTP_STREAM_ID, [Version (since = "1.20")] REPAIRED_RTP_STREAM_ID, [Version (since = "1.20")] CCID, [Version (since = "1.20")] MID; [CCode (cname = "gst_rtcp_sdes_name_to_type")] public static Gst.RTCP.SDESType from_string (string name); [CCode (cname = "gst_rtcp_sdes_type_to_name")] public unowned string to_string (); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_TYPE_", type_id = "gst_rtcp_type_get_type ()")] [GIR (name = "RTCPType")] public enum Type { INVALID, SR, RR, SDES, BYE, APP, RTPFB, PSFB, XR } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTCP_XR_TYPE_", type_id = "gst_rtcpxr_type_get_type ()")] [GIR (name = "RTCPXRType")] [Version (since = "1.16")] public enum XRType { INVALID, LRLE, DRLE, PRT, RRT, DLRR, SSUMM, VOIP_METRICS } [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_BYE_SSRC_COUNT")] public const int MAX_BYE_SSRC_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_RB_COUNT")] public const int MAX_RB_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_SDES")] public const int MAX_SDES; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_MAX_SDES_ITEM_COUNT")] public const int MAX_SDES_ITEM_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_REDUCED_SIZE_VALID_MASK")] public const int REDUCED_SIZE_VALID_MASK; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VALID_MASK")] public const int VALID_MASK; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VALID_VALUE")] public const int VALID_VALUE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTCP_VERSION")] public const int VERSION; [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.map")] public static bool buffer_map (Gst.Buffer buffer, Gst.MapFlags flags, Gst.RTCP.Buffer rtcp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new")] public static Gst.Buffer buffer_new (uint mtu); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new_copy_data")] public static Gst.Buffer buffer_new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.new_take_data")] public static Gst.Buffer buffer_new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate")] public static bool buffer_validate (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_data")] public static bool buffer_validate_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_data_reduced", since = "1.6")] public static bool buffer_validate_data_reduced ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTCPBuffer.validate_reduced", since = "1.6")] public static bool buffer_validate_reduced (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static uint64 ntp_to_unix (uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static uint64 unix_to_ntp (uint64 unixtime); } namespace RTP { [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_audio_payload_get_type ()")] [GIR (name = "RTPBaseAudioPayload")] public class BaseAudioPayload : Gst.RTP.BasePayload { public Gst.ClockTime base_ts; public int frame_duration; public int frame_size; public int sample_size; [CCode (has_construct_function = false)] protected BaseAudioPayload (); public Gst.FlowReturn flush (uint payload_len, Gst.ClockTime timestamp); public Gst.Base.Adapter get_adapter (); public Gst.FlowReturn push ([CCode (array_length_cname = "payload_len", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, Gst.ClockTime timestamp); public void set_frame_based (); public void set_frame_options (int frame_duration, int frame_size); public void set_sample_based (); public void set_sample_options (int sample_size); public void set_samplebits_options (int sample_size); [NoAccessorMethod] public bool buffer_list { get; set; } } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_depayload_get_type ()")] [GIR (name = "RTPBaseDepayload")] public abstract class BaseDepayload : Gst.Element { public uint clock_rate; public bool need_newsegment; public weak Gst.Segment segment; public weak Gst.Pad sinkpad; public weak Gst.Pad srcpad; [CCode (has_construct_function = false)] protected BaseDepayload (); [NoWrapper] public virtual bool handle_event (Gst.Event event); [Version (since = "1.16")] public bool is_source_info_enabled (); [NoWrapper] public virtual bool packet_lost (Gst.Event event); [NoWrapper] public virtual Gst.Buffer process (Gst.Buffer @in); [NoWrapper] public virtual Gst.Buffer process_rtp_packet (Gst.RTP.Buffer rtp_buffer); public Gst.FlowReturn push (Gst.Buffer out_buf); public Gst.FlowReturn push_list (Gst.BufferList out_list); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); [Version (since = "1.16")] public void set_source_info_enabled (bool enable); [NoAccessorMethod] [Version (since = "1.20")] public bool auto_header_extension { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public int max_reorder { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool source_info { get; set; } [NoAccessorMethod] public Gst.Structure stats { owned get; } [Version (since = "1.20")] public signal void add_extension (owned Gst.RTP.HeaderExtension ext); [Version (since = "1.20")] public signal void clear_extensions (); [Version (since = "1.20")] public signal Gst.RTP.HeaderExtension request_extension (uint ext_id, string? ext_uri); } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_base_payload_get_type ()")] [GIR (name = "RTPBasePayload")] public abstract class BasePayload : Gst.Element { [CCode (has_construct_function = false)] protected BasePayload (); [Version (since = "1.16")] public Gst.Buffer allocate_output_buffer (uint payload_len, uint8 pad_len, uint8 csrc_count); [NoWrapper] public virtual Gst.Caps get_caps (Gst.Pad pad, Gst.Caps filter); [Version (since = "1.16")] public uint get_source_count (Gst.Buffer buffer); [NoWrapper] public virtual Gst.FlowReturn handle_buffer (Gst.Buffer buffer); public bool is_filled (uint size, Gst.ClockTime duration); [Version (since = "1.16")] public bool is_source_info_enabled (); public Gst.FlowReturn push (owned Gst.Buffer buffer); public Gst.FlowReturn push_list (owned Gst.BufferList list); [NoWrapper] public virtual bool query (Gst.Pad pad, Gst.Query query); [NoWrapper] public virtual bool set_caps (Gst.Caps caps); public void set_options (string media, bool @dynamic, string encoding_name, uint32 clock_rate); [Version (since = "1.20")] public bool set_outcaps_structure (Gst.Structure? s); [Version (since = "1.16")] public void set_source_info_enabled (bool enable); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoAccessorMethod] [Version (since = "1.20")] public bool auto_header_extension { get; set; } [NoAccessorMethod] public int64 max_ptime { get; set; } [NoAccessorMethod] public int64 min_ptime { get; set; } [NoAccessorMethod] public uint mtu { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool onvif_no_rate_control { get; set; } [NoAccessorMethod] public bool perfect_rtptime { get; set; } [NoAccessorMethod] public uint pt { get; set; } [NoAccessorMethod] public int64 ptime_multiple { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public bool scale_rtptime { get; set; } [NoAccessorMethod] public uint seqnum { get; } [NoAccessorMethod] public int seqnum_offset { get; set; } [NoAccessorMethod] [Version (since = "1.16")] public bool source_info { get; set; } [NoAccessorMethod] public uint ssrc { get; set; } [NoAccessorMethod] public Gst.Structure stats { owned get; } [NoAccessorMethod] public uint timestamp { get; } [NoAccessorMethod] public uint timestamp_offset { get; set; } [Version (since = "1.20")] public signal void add_extension (owned Gst.RTP.HeaderExtension ext); [Version (since = "1.20")] public signal void clear_extensions (); [Version (since = "1.20")] public signal Gst.RTP.HeaderExtension request_extension (uint ext_id, string ext_uri); } [CCode (cheader_filename = "gst/rtp/rtp.h", type_id = "gst_rtp_header_extension_get_type ()")] [GIR (name = "RTPHeaderExtension")] [Version (since = "1.20")] public abstract class HeaderExtension : Gst.Element { [CCode (has_construct_function = false)] protected HeaderExtension (); public static Gst.RTP.HeaderExtension? create_from_uri (string uri); public Gst.RTP.HeaderExtensionDirection get_direction (); public uint get_id (); public virtual size_t get_max_size (Gst.Buffer input_meta); public string get_sdp_caps_field_name (); public virtual Gst.RTP.HeaderExtensionFlags get_supported_flags (); public unowned string get_uri (); public virtual bool read (Gst.RTP.HeaderExtensionFlags read_flags, [CCode (array_length_cname = "size", array_length_pos = 2.5, array_length_type = "gsize")] uint8[] data, Gst.Buffer buffer); [NoWrapper] public virtual bool set_attributes (Gst.RTP.HeaderExtensionDirection direction, string attributes); public bool set_attributes_from_caps (Gst.Caps caps); public virtual bool set_caps_from_attributes (Gst.Caps caps); public bool set_caps_from_attributes_helper (Gst.Caps caps, string attributes); public void set_direction (Gst.RTP.HeaderExtensionDirection direction); public void set_id (uint ext_id); public virtual bool set_non_rtp_sink_caps (Gst.Caps caps); [CCode (cname = "gst_rtp_header_extension_class_set_uri")] public class void set_uri (string uri); public void set_wants_update_non_rtp_src_caps (bool state); public virtual bool update_non_rtp_src_caps (Gst.Caps caps); public bool wants_update_non_rtp_src_caps (); public virtual ssize_t write (Gst.Buffer input_meta, Gst.RTP.HeaderExtensionFlags write_flags, Gst.Buffer output, [CCode (array_length_cname = "size", array_length_pos = 4.1, array_length_type = "gsize")] uint8[] data); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPBuffer")] public struct Buffer { public weak Gst.Buffer buffer; public uint state; [CCode (array_length = false)] public weak void* data[4]; [CCode (array_length = false)] public weak size_t size[4]; public bool add_extension_onebyte_header (uint8 id, [CCode (array_length_cname = "size", array_length_pos = 2.1, array_length_type = "guint")] uint8[] data); public bool add_extension_twobytes_header (uint8 appbits, uint8 id, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "guint")] uint8[] data); public static void allocate_data (Gst.Buffer buffer, uint payload_len, uint8 pad_len, uint8 csrc_count); public static uint calc_header_len (uint8 csrc_count); public static uint calc_packet_len (uint payload_len, uint8 pad_len, uint8 csrc_count); public static uint calc_payload_len (uint packet_len, uint8 pad_len, uint8 csrc_count); public static int compare_seqnum (uint16 seqnum1, uint16 seqnum2); public static uint32 default_clock_rate (uint8 payload_type); public static uint64 ext_timestamp (ref uint64 exttimestamp, uint32 timestamp); public uint32 get_csrc (uint8 idx); public uint8 get_csrc_count (); public bool get_extension (); [Version (since = "1.2")] public GLib.Bytes get_extension_bytes (out uint16 bits); public bool get_extension_data (out uint16 bits, [CCode (array_length = false)] out unowned uint8[] data, out uint wordlen); public bool get_extension_onebyte_header (uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "guint")] out unowned uint8[] data); [Version (since = "1.18")] public static bool get_extension_onebyte_header_from_bytes (GLib.Bytes bytes, uint16 bit_pattern, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 5.1, array_length_type = "guint")] out unowned uint8[] data); public bool get_extension_twobytes_header (out uint8 appbits, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 4.1, array_length_type = "guint")] out unowned uint8[] data); public uint get_header_len (); public bool get_marker (); public uint get_packet_len (); public bool get_padding (); [CCode (array_length = false)] public unowned uint8[] get_payload (); public Gst.Buffer get_payload_buffer (); [Version (since = "1.2")] public GLib.Bytes get_payload_bytes (); public uint get_payload_len (); public Gst.Buffer get_payload_subbuffer (uint offset, uint len); public uint8 get_payload_type (); public uint16 get_seq (); public uint32 get_ssrc (); public uint32 get_timestamp (); public uint8 get_version (); public static bool map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTP.Buffer rtp); public static Gst.Buffer new_allocate (uint payload_len, uint8 pad_len, uint8 csrc_count); public static Gst.Buffer new_allocate_len (uint packet_len, uint8 pad_len, uint8 csrc_count); public static Gst.Buffer new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] uint8[] data); public static Gst.Buffer new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] owned uint8[] data); public void pad_to (uint len); [Version (since = "1.20")] public void remove_extension_data (); public void set_csrc (uint8 idx, uint32 csrc); public void set_extension (bool extension); public bool set_extension_data (uint16 bits, uint16 length); public void set_marker (bool marker); public void set_packet_len (uint len); public void set_padding (bool padding); public void set_payload_type (uint8 payload_type); public void set_seq (uint16 seq); public void set_ssrc (uint32 ssrc); public void set_timestamp (uint32 timestamp); public void set_version (uint8 version); public void unmap (); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPPayloadInfo")] public struct PayloadInfo { public uint8 payload_type; public weak string media; public weak string encoding_name; public uint clock_rate; public weak string encoding_parameters; public uint bitrate; public static unowned Gst.RTP.PayloadInfo? for_name (string media, string encoding_name); public static unowned Gst.RTP.PayloadInfo? for_pt (uint8 payload_type); } [CCode (cheader_filename = "gst/rtp/rtp.h", has_type_id = false)] [GIR (name = "RTPSourceMeta")] [Version (since = "1.16")] public struct SourceMeta { public Gst.Meta meta; public uint32 ssrc; public bool ssrc_valid; [CCode (array_length = false)] public weak uint32 csrc[15]; public uint csrc_count; public bool append_csrc ([CCode (array_length_cname = "csrc_count", array_length_pos = 1.1, array_length_type = "guint", type = "const guint32*")] uint32[] csrc); public static unowned Gst.MetaInfo? get_info (); public uint get_source_count (); public bool set_ssrc (uint32? ssrc); } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_BUFFER_FLAG_", type_id = "gst_rtp_buffer_flags_get_type ()")] [Flags] [GIR (name = "RTPBufferFlags")] [Version (since = "1.10")] public enum BufferFlags { RETRANSMISSION, REDUNDANT, LAST } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_BUFFER_MAP_FLAG_", type_id = "gst_rtp_buffer_map_flags_get_type ()")] [Flags] [GIR (name = "RTPBufferMapFlags")] [Version (since = "1.6.1")] public enum BufferMapFlags { SKIP_PADDING, LAST } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_HEADER_EXTENSION_DIRECTION_", type_id = "gst_rtp_header_extension_direction_get_type ()")] [Flags] [GIR (name = "RTPHeaderExtensionDirection")] [Version (since = "1.20")] public enum HeaderExtensionDirection { INACTIVE, SENDONLY, RECVONLY, SENDRECV, INHERITED } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_HEADER_EXTENSION_", type_id = "gst_rtp_header_extension_flags_get_type ()")] [Flags] [GIR (name = "RTPHeaderExtensionFlags")] [Version (since = "1.20")] public enum HeaderExtensionFlags { ONE_BYTE, TWO_BYTE } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_PAYLOAD_", type_id = "gst_rtp_payload_get_type ()")] [GIR (name = "RTPPayload")] public enum Payload { PCMU, @1016, G721, GSM, G723, DVI4_8000, DVI4_16000, LPC, PCMA, G722, L16_STEREO, L16_MONO, QCELP, CN, MPA, G728, DVI4_11025, DVI4_22050, G729, CELLB, JPEG, NV, H261, MPV, MP2T, H263; public const string @1016_STRING; public const string CELLB_STRING; public const string CN_STRING; public const string DVI4_11025_STRING; public const string DVI4_16000_STRING; public const string DVI4_22050_STRING; public const string DVI4_8000_STRING; public const string DYNAMIC_STRING; public const string G721_STRING; public const string G722_STRING; public const int G723_53; public const string G723_53_STRING; public const int G723_63; public const string G723_63_STRING; public const string G723_STRING; public const string G728_STRING; public const string G729_STRING; public const string GSM_STRING; public const string H261_STRING; public const string H263_STRING; public const string JPEG_STRING; public const string L16_MONO_STRING; public const string L16_STEREO_STRING; public const string LPC_STRING; public const string MP2T_STRING; public const string MPA_STRING; public const string MPV_STRING; public const string NV_STRING; public const string PCMA_STRING; public const string PCMU_STRING; public const string QCELP_STRING; public const int TS41; public const string TS41_STRING; public const int TS48; public const string TS48_STRING; } [CCode (cheader_filename = "gst/rtp/rtp.h", cprefix = "GST_RTP_PROFILE_", type_id = "gst_rtp_profile_get_type ()")] [GIR (name = "RTPProfile")] [Version (since = "1.6")] public enum Profile { UNKNOWN, AVP, SAVP, AVPF, SAVPF } [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_BASE")] public const string HDREXT_BASE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_ELEMENT_CLASS")] [Version (since = "1.20")] public const string HDREXT_ELEMENT_CLASS; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_56")] public const string HDREXT_NTP_56; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_56_SIZE")] public const int HDREXT_NTP_56_SIZE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_64")] public const string HDREXT_NTP_64; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HDREXT_NTP_64_SIZE")] public const int HDREXT_NTP_64_SIZE; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_HEADER_EXTENSION_URI_METADATA_KEY")] [Version (since = "1.20")] public const string HEADER_EXTENSION_URI_METADATA_KEY; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_SOURCE_META_MAX_CSRC_COUNT")] public const int SOURCE_META_MAX_CSRC_COUNT; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "GST_RTP_VERSION")] public const int VERSION; [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "gst_buffer_add_rtp_source_meta")] [Version (since = "1.16")] public static unowned Gst.RTP.SourceMeta? buffer_add_rtp_source_meta (Gst.Buffer buffer, uint32? ssrc, uint32? csrc, uint csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.allocate_data")] public static void buffer_allocate_data (Gst.Buffer buffer, uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_header_len")] public static uint buffer_calc_header_len (uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_packet_len")] public static uint buffer_calc_packet_len (uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.calc_payload_len")] public static uint buffer_calc_payload_len (uint packet_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.compare_seqnum")] public static int buffer_compare_seqnum (uint16 seqnum1, uint16 seqnum2); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.default_clock_rate")] public static uint32 buffer_default_clock_rate (uint8 payload_type); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.ext_timestamp")] public static uint64 buffer_ext_timestamp (ref uint64 exttimestamp, uint32 timestamp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.get_extension_onebyte_header_from_bytes", since = "1.18")] public static bool buffer_get_extension_onebyte_header_from_bytes (GLib.Bytes bytes, uint16 bit_pattern, uint8 id, uint nth, [CCode (array_length_cname = "size", array_length_pos = 5.1, array_length_type = "guint")] out unowned uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h", cname = "gst_buffer_get_rtp_source_meta")] [Version (since = "1.16")] public static unowned Gst.RTP.SourceMeta? buffer_get_rtp_source_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.map")] public static bool buffer_map (Gst.Buffer buffer, Gst.MapFlags flags, out Gst.RTP.Buffer rtp); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_allocate")] public static Gst.Buffer buffer_new_allocate (uint payload_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_allocate_len")] public static Gst.Buffer buffer_new_allocate_len (uint packet_len, uint8 pad_len, uint8 csrc_count); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_copy_data")] public static Gst.Buffer buffer_new_copy_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPBuffer.new_take_data")] public static Gst.Buffer buffer_new_take_data ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "gsize")] owned uint8[] data); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (since = "1.20")] public static GLib.List get_header_extension_list (); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_get_ntp_56 ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, out uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_get_ntp_64 ([CCode (array_length_cname = "size", array_length_pos = 1.5, array_length_type = "guint")] uint8[] data, out uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_set_ntp_56 (void* data, uint size, uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static bool hdrext_set_ntp_64 (void* data, uint size, uint64 ntptime); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPPayloadInfo.for_name")] public static unowned Gst.RTP.PayloadInfo? payload_info_for_name (string media, string encoding_name); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPPayloadInfo.for_pt")] public static unowned Gst.RTP.PayloadInfo? payload_info_for_pt (uint8 payload_type); [CCode (cheader_filename = "gst/rtp/rtp.h")] public static GLib.Type source_meta_api_get_type (); [CCode (cheader_filename = "gst/rtp/rtp.h")] [Version (replacement = "RTPSourceMeta.get_info")] public static unowned Gst.MetaInfo? source_meta_get_info (); } } dino-0.5.0/plugins/rtp/vapi/gstreamer-video-1.0.vapi0000664000000000000000000033673714776241610020677 0ustar rootroot// Fixme: This is fetched from development code of Vala upstream which fixed a few bugs. /* gstreamer-video-1.0.vapi generated by vapigen, do not modify. */ [CCode (cprefix = "Gst", gir_namespace = "GstVideo", gir_version = "1.0", lower_case_cprefix = "gst_")] namespace Gst { namespace Video { [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_get_type ()")] [GIR (name = "VideoAggregator")] [Version (since = "1.16")] public abstract class Aggregator : Gst.Base.Aggregator { public weak Gst.Video.Info info; [CCode (has_construct_function = false)] protected Aggregator (); [NoWrapper] public virtual Gst.FlowReturn aggregate_frames (Gst.Buffer outbuffer); [NoWrapper] public virtual Gst.FlowReturn create_output_buffer (Gst.Buffer outbuffer); [NoWrapper] public virtual void find_best_format (Gst.Caps downstream_caps, Gst.Video.Info best_info, out bool at_least_one_alpha); [Version (since = "1.20")] public Gst.TaskPool get_execution_task_pool (); [NoWrapper] public virtual Gst.Caps update_caps (Gst.Caps caps); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_convert_pad_get_type ()")] [GIR (name = "VideoAggregatorConvertPad")] [Version (since = "1.16")] public class AggregatorConvertPad : Gst.Video.AggregatorPad { [CCode (has_construct_function = false)] protected AggregatorConvertPad (); [NoWrapper] public virtual void create_conversion_info (Gst.Video.Aggregator agg, Gst.Video.Info conversion_info); public void update_conversion_info (); [NoAccessorMethod] public Gst.Structure converter_config { owned get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_pad_get_type ()")] [GIR (name = "VideoAggregatorPad")] [Version (since = "1.16")] public class AggregatorPad : Gst.Base.AggregatorPad { public weak Gst.Video.Info info; [CCode (has_construct_function = false)] protected AggregatorPad (); [NoWrapper] public virtual void clean_frame (Gst.Video.Aggregator videoaggregator, Gst.Video.Frame prepared_frame); public unowned Gst.Buffer get_current_buffer (); public unowned Gst.Video.Frame? get_prepared_frame (); public bool has_current_buffer (); [NoWrapper] public virtual bool prepare_frame (Gst.Video.Aggregator videoaggregator, Gst.Buffer buffer, Gst.Video.Frame prepared_frame); [NoWrapper] [Version (since = "1.20")] public virtual void prepare_frame_finish (Gst.Video.Aggregator videoaggregator, Gst.Video.Frame prepared_frame); [NoWrapper] [Version (since = "1.20")] public virtual void prepare_frame_start (Gst.Video.Aggregator videoaggregator, Gst.Buffer buffer, Gst.Video.Frame prepared_frame); public void set_needs_alpha (bool needs_alpha); [NoWrapper] public virtual void update_conversion_info (); [NoAccessorMethod] public uint64 max_last_buffer_repeat { get; set; } [NoAccessorMethod] public bool repeat_after_eos { get; set; } [NoAccessorMethod] public uint zorder { get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_aggregator_parallel_convert_pad_get_type ()")] [GIR (name = "VideoAggregatorParallelConvertPad")] [Version (since = "1.20")] public class AggregatorParallelConvertPad : Gst.Video.AggregatorConvertPad { [CCode (has_construct_function = false)] protected AggregatorParallelConvertPad (); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_buffer_pool_get_type ()")] [GIR (name = "VideoBufferPool")] public class BufferPool : Gst.BufferPool { [CCode (has_construct_function = false, type = "GstBufferPool*")] public BufferPool (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoChromaResample")] public class ChromaResample { [CCode (cname = "gst_video_chroma_resample_new", has_construct_function = false)] public ChromaResample (Gst.Video.ChromaMethod method, Gst.Video.ChromaSite site, Gst.Video.ChromaFlags flags, Gst.Video.Format format, int h_factor, int v_factor); public void free (); public void get_info (out uint n_lines, out int offset); [CCode (cname = "gst_video_chroma_resample")] public void resample (void* lines, int width); } [CCode (cheader_filename = "gst/video/gstvideoutils.h", ref_function = "gst_video_codec_frame_ref", type_id = "gst_video_codec_frame_get_type ()", unref_function = "gst_video_codec_frame_unref")] [Compact] [GIR (name = "VideoCodecFrame")] public class CodecFrame { public Gst.ClockTime deadline; public int distance_from_sync; public Gst.ClockTime dts; public Gst.ClockTime duration; public weak Gst.Buffer input_buffer; public weak Gst.Buffer output_buffer; public Gst.ClockTime pts; public uint32 system_frame_number; [CCode (simple_generics = true)] public T get_user_data (); public unowned Gst.Video.CodecFrame @ref (); [CCode (simple_generics = true)] public void set_user_data (owned T user_data); public void unref (); } [CCode (cheader_filename = "gst/video/video.h", ref_function = "gst_video_codec_state_ref", type_id = "gst_video_codec_state_get_type ()", unref_function = "gst_video_codec_state_unref")] [Compact] [GIR (name = "VideoCodecState")] public class CodecState { public weak Gst.Caps allocation_caps; public weak Gst.Caps caps; public weak Gst.Buffer codec_data; [Version (since = "1.20")] public Gst.Video.ContentLightLevel content_light_level; public weak Gst.Video.Info info; [Version (since = "1.20")] public Gst.Video.MasteringDisplayInfo mastering_display_info; public unowned Gst.Video.CodecState @ref (); public void unref (); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalanceChannel", lower_case_cprefix = "gst_color_balance_channel_", type_id = "gst_color_balance_channel_get_type ()")] [GIR (name = "ColorBalanceChannel")] public class ColorBalanceChannel : GLib.Object { public weak string label; public int max_value; public int min_value; [CCode (has_construct_function = false)] protected ColorBalanceChannel (); public virtual signal void value_changed (int value); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoConverter")] public class Converter { [Version (since = "1.6")] public void frame (Gst.Video.Frame src, Gst.Video.Frame dest); [Version (since = "1.20")] public void frame_finish (); [Version (since = "1.6")] public void free (); public unowned Gst.Structure get_config (); [Version (since = "1.6")] public bool set_config (owned Gst.Structure config); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_decoder_get_type ()")] [GIR (name = "VideoDecoder")] public abstract class Decoder : Gst.Element { [CCode (has_construct_function = false)] protected Decoder (); public void add_to_frame (int n_bytes); public Gst.Buffer allocate_output_buffer (); public Gst.FlowReturn allocate_output_frame (Gst.Video.CodecFrame frame); [Version (since = "1.12")] public Gst.FlowReturn allocate_output_frame_with_params (Gst.Video.CodecFrame frame, Gst.BufferPoolAcquireParams @params); [NoWrapper] public virtual bool close (); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual Gst.FlowReturn drain (); public Gst.FlowReturn drop_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public Gst.FlowReturn drop_subframe (owned Gst.Video.CodecFrame frame); [NoWrapper] public virtual Gst.FlowReturn finish (); public Gst.FlowReturn finish_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public Gst.FlowReturn finish_subframe (owned Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool flush (); public void get_allocator (out Gst.Allocator allocator, out Gst.AllocationParams @params); public Gst.BufferPool get_buffer_pool (); public int get_estimate_rate (); public Gst.Video.CodecFrame get_frame (int frame_number); public GLib.List get_frames (); [Version (since = "1.20")] public uint get_input_subframe_index (Gst.Video.CodecFrame frame); public void get_latency (out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); public Gst.ClockTimeDiff get_max_decode_time (Gst.Video.CodecFrame frame); public int get_max_errors (); [Version (since = "1.4")] public bool get_needs_format (); [Version (since = "1.20")] public bool get_needs_sync_point (); public Gst.Video.CodecFrame get_oldest_frame (); public Gst.Video.CodecState get_output_state (); public bool get_packetized (); [Version (since = "1.4")] public size_t get_pending_frame_size (); [Version (since = "1.20")] public uint get_processed_subframe_index (Gst.Video.CodecFrame frame); [Version (since = "1.0.3")] public double get_qos_proportion (); [Version (since = "1.20")] public bool get_subframe_mode (); [NoWrapper] public virtual Gst.Caps getcaps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (owned Gst.Video.CodecFrame frame); [NoWrapper] [Version (since = "1.20")] public virtual bool handle_missing_data (Gst.ClockTime timestamp, Gst.ClockTime duration); public Gst.FlowReturn have_frame (); [Version (since = "1.20")] public Gst.FlowReturn have_last_subframe (Gst.Video.CodecFrame frame); public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); public virtual bool negotiate (); [NoWrapper] public virtual bool open (); [NoWrapper] public virtual Gst.FlowReturn parse (Gst.Video.CodecFrame frame, Gst.Base.Adapter adapter, bool at_eos); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); [Version (since = "1.6")] public Gst.Caps proxy_getcaps (Gst.Caps? caps, Gst.Caps? filter); [Version (since = "1.2.2")] public void release_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.20")] public void request_sync_point (Gst.Video.CodecFrame frame, Gst.Video.DecoderRequestSyncPointFlags flags); [NoWrapper] public virtual bool reset (bool hard); public void set_estimate_rate (bool enabled); [NoWrapper] public virtual bool set_format (Gst.Video.CodecState state); [Version (since = "1.16.")] public Gst.Video.CodecState set_interlaced_output_state (Gst.Video.Format fmt, Gst.Video.InterlaceMode interlace_mode, uint width, uint height, Gst.Video.CodecState? reference); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); public void set_max_errors (int num); [Version (since = "1.4")] public void set_needs_format (bool enabled); [Version (since = "1.20")] public void set_needs_sync_point (bool enabled); public Gst.Video.CodecState set_output_state (Gst.Video.Format fmt, uint width, uint height, Gst.Video.CodecState? reference); public void set_packetized (bool packetized); [Version (since = "1.20")] public void set_subframe_mode (bool subframe_mode); [Version (since = "1.6")] public void set_use_default_pad_acceptcaps (bool use); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool transform_meta (Gst.Video.CodecFrame frame, Gst.Meta meta); [NoAccessorMethod] [Version (since = "1.20")] public Gst.Video.DecoderRequestSyncPointFlags automatic_request_sync_point_flags { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public bool automatic_request_sync_points { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public bool discard_corrupted_frames { get; set; } [Version (since = "1.18")] public int max_errors { get; set; } [NoAccessorMethod] [Version (since = "1.20")] public uint64 min_force_key_unit_interval { get; set; } [NoAccessorMethod] [Version (since = "1.18")] public bool qos { get; set; } } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoDither")] public class Dither { public void free (); public void line (void* line, uint x, uint y, uint width); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_encoder_get_type ()")] [GIR (name = "VideoEncoder")] public abstract class Encoder : Gst.Element, Gst.Preset { [CCode (has_construct_function = false)] protected Encoder (); public Gst.Buffer allocate_output_buffer (size_t size); public Gst.FlowReturn allocate_output_frame (Gst.Video.CodecFrame frame, size_t size); [NoWrapper] public virtual bool close (); [NoWrapper] public virtual bool decide_allocation (Gst.Query query); [NoWrapper] public virtual Gst.FlowReturn finish (); public Gst.FlowReturn finish_frame (owned Gst.Video.CodecFrame frame); [Version (since = "1.18")] public Gst.FlowReturn finish_subframe (Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool flush (); public void get_allocator (out Gst.Allocator allocator, out Gst.AllocationParams @params); public Gst.Video.CodecFrame get_frame (int frame_number); public GLib.List get_frames (); public void get_latency (out Gst.ClockTime min_latency, out Gst.ClockTime max_latency); [Version (since = "1.14")] public Gst.ClockTimeDiff get_max_encode_time (Gst.Video.CodecFrame frame); [Version (since = "1.18")] public Gst.ClockTime get_min_force_key_unit_interval (); public Gst.Video.CodecFrame get_oldest_frame (); public Gst.Video.CodecState get_output_state (); [NoWrapper] public virtual Gst.Caps getcaps (Gst.Caps filter); [NoWrapper] public virtual Gst.FlowReturn handle_frame (Gst.Video.CodecFrame frame); [Version (since = "1.14")] public bool is_qos_enabled (); public void merge_tags (Gst.TagList? tags, Gst.TagMergeMode mode); public virtual bool negotiate (); [NoWrapper] public virtual bool open (); [NoWrapper] public virtual Gst.FlowReturn pre_push (Gst.Video.CodecFrame frame); [NoWrapper] public virtual bool propose_allocation (Gst.Query query); public Gst.Caps proxy_getcaps (Gst.Caps? caps, Gst.Caps? filter); [NoWrapper] public virtual bool reset (bool hard); [NoWrapper] public virtual bool set_format (Gst.Video.CodecState state); public void set_headers (owned GLib.List headers); public void set_latency (Gst.ClockTime min_latency, Gst.ClockTime max_latency); [Version (since = "1.18")] public void set_min_force_key_unit_interval (Gst.ClockTime interval); [Version (since = "1.6")] public void set_min_pts (Gst.ClockTime min_pts); public Gst.Video.CodecState set_output_state (owned Gst.Caps caps, Gst.Video.CodecState? reference); [Version (since = "1.14")] public void set_qos_enabled (bool enabled); [NoWrapper] public virtual bool sink_event (Gst.Event event); [NoWrapper] public virtual bool sink_query (Gst.Query query); [NoWrapper] public virtual bool src_event (Gst.Event event); [NoWrapper] public virtual bool src_query (Gst.Query query); [NoWrapper] public virtual bool start (); [NoWrapper] public virtual bool stop (); [NoWrapper] public virtual bool transform_meta (Gst.Video.CodecFrame frame, Gst.Meta meta); [Version (since = "1.18")] public uint64 min_force_key_unit_interval { get; set; } [NoAccessorMethod] public bool qos { get; set; } } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_filter_get_type ()")] [GIR (name = "VideoFilter")] public abstract class Filter : Gst.Base.Transform { public weak Gst.Video.Info in_info; public bool negotiated; public weak Gst.Video.Info out_info; [CCode (has_construct_function = false)] protected Filter (); [NoWrapper] public virtual bool set_info (Gst.Caps incaps, Gst.Video.Info in_info, Gst.Caps outcaps, Gst.Video.Info out_info); [NoWrapper] public virtual Gst.FlowReturn transform_frame (Gst.Video.Frame inframe, Gst.Video.Frame outframe); [NoWrapper] public virtual Gst.FlowReturn transform_frame_ip (Gst.Video.Frame frame); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_info_get_type ()")] [Compact] [GIR (name = "VideoInfo")] public class Info { [CCode (cname = "ABI.abi.field_order")] public Gst.Video.FieldOrder ABI_abi_field_order; [CCode (cname = "ABI.abi.multiview_flags")] public Gst.Video.MultiviewFlags ABI_abi_multiview_flags; [CCode (cname = "ABI.abi.multiview_mode")] public Gst.Video.MultiviewMode ABI_abi_multiview_mode; public Gst.Video.ChromaSite chroma_site; public Gst.Video.Colorimetry colorimetry; public weak Gst.Video.FormatInfo? finfo; public Gst.Video.Flags flags; public int fps_d; public int fps_n; public int height; public Gst.Video.InterlaceMode interlace_mode; [CCode (array_length = false)] public weak size_t offset[4]; public int par_d; public int par_n; public size_t size; [CCode (array_length = false)] public weak int stride[4]; public int views; public int width; [CCode (has_construct_function = false)] [Version (since = "1.6")] public Info (); public bool align (Gst.Video.Alignment align); [Version (since = "1.18")] public bool align_full (Gst.Video.Alignment align, out size_t plane_size); public bool convert (Gst.Format src_format, int64 src_value, Gst.Format dest_format, out int64 dest_value); [Version (since = "1.6")] public Gst.Video.Info copy (); [Version (since = "1.6")] public void free (); public bool from_caps (Gst.Caps caps); public void init (); public bool is_equal (Gst.Video.Info other); public bool set_format (Gst.Video.Format format, uint width, uint height); [Version (since = "1.16")] public bool set_interlaced_format (Gst.Video.Format format, Gst.Video.InterlaceMode mode, uint width, uint height); public Gst.Caps to_caps (); [CCode (cname = "gst_video_info_new_from_caps", has_construct_function = false)] [Version (since = "1.20")] public Info.with_caps (Gst.Caps caps); } [CCode (cheader_filename = "gst/video/video.h", lower_case_cprefix = "gst_video_multiview_flagset_", type_id = "gst_video_multiview_flagset_get_type ()")] [GIR (name = "VideoMultiviewFlagsSet")] public class MultiviewFlagsSet : Gst.FlagSet { [CCode (has_construct_function = false)] protected MultiviewFlagsSet (); } [CCode (cheader_filename = "gst/video/video-overlay-composition.h", ref_function = "gst_video_overlay_composition_ref", type_id = "gst_video_overlay_composition_get_type ()", unref_function = "gst_video_overlay_composition_unref")] [Compact] [GIR (name = "VideoOverlayComposition")] public class OverlayComposition : Gst.MiniObject { [CCode (has_construct_function = false)] public OverlayComposition (Gst.Video.OverlayRectangle? rectangle); public void add_rectangle (Gst.Video.OverlayRectangle rectangle); public bool blend (Gst.Video.Frame video_buf); public Gst.Video.OverlayComposition copy (); public unowned Gst.Video.OverlayRectangle get_rectangle (uint n); public uint get_seqnum (); [DestroysInstance] [ReturnsModifiedPointer] public Gst.Video.OverlayComposition make_writable (); public uint n_rectangles (); } [CCode (cheader_filename = "gst/video/video-overlay-composition.h", ref_function = "gst_video_overlay_rectangle_ref", type_id = "gst_video_overlay_rectangle_get_type ()", unref_function = "gst_video_overlay_rectangle_unref")] [Compact] [GIR (name = "VideoOverlayRectangle")] public class OverlayRectangle : Gst.MiniObject { public Gst.Video.OverlayRectangle copy (); public Gst.Video.OverlayFormatFlags get_flags (); public float get_global_alpha (); public unowned Gst.Buffer get_pixels_argb (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_ayuv (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_raw (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_argb (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_ayuv (Gst.Video.OverlayFormatFlags flags); public unowned Gst.Buffer get_pixels_unscaled_raw (Gst.Video.OverlayFormatFlags flags); public bool get_render_rectangle (out int render_x, out int render_y, out uint render_width, out uint render_height); public uint get_seqnum (); [CCode (has_construct_function = false)] public OverlayRectangle.raw (Gst.Buffer pixels, int render_x, int render_y, uint render_width, uint render_height, Gst.Video.OverlayFormatFlags flags); public void set_global_alpha (float global_alpha); public void set_render_rectangle (int render_x, int render_y, uint render_width, uint render_height); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [Compact] [GIR (name = "VideoScaler")] public class Scaler { public void @2d (Gst.Video.Scaler vscale, Gst.Video.Format format, void* src, int src_stride, void* dest, int dest_stride, uint x, uint y, uint width, uint height); public void free (); public double get_coeff (uint out_offset, out uint in_offset, out uint n_taps); public uint get_max_taps (); public void horizontal (Gst.Video.Format format, void* src, void* dest, uint dest_offset, uint width); public void vertical (Gst.Video.Format format, void* src_lines, void* dest, uint dest_offset, uint width); } [CCode (cheader_filename = "gst/video/video.h", type_id = "gst_video_sink_get_type ()")] [GIR (name = "VideoSink")] public class Sink : Gst.Base.Sink { public int height; public int width; [CCode (has_construct_function = false)] protected Sink (); [Version (deprecated = true, deprecated_since = "1.20")] public static void center_rect (Gst.Video.Rectangle src, Gst.Video.Rectangle dst, out Gst.Video.Rectangle result, bool scaling); [NoWrapper] [Version (since = "1.20")] public virtual bool set_info (Gst.Caps caps, Gst.Video.Info info); [NoWrapper] public virtual Gst.FlowReturn show_frame (Gst.Buffer buf); [NoAccessorMethod] public bool show_preroll_frame { get; set construct; } } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_time_code_get_type ()")] [Compact] [GIR (name = "VideoTimeCode")] [Version (since = "1.10")] public class TimeCode { public Gst.Video.TimeCodeConfig config; public uint field_count; public uint frames; public uint hours; public uint minutes; public uint seconds; [CCode (has_construct_function = false)] public TimeCode (uint fps_n, uint fps_d, GLib.DateTime latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); public void add_frames (int64 frames); [Version (since = "1.12")] public Gst.Video.TimeCode? add_interval (Gst.Video.TimeCodeInterval tc_inter); public void clear (); public int compare (Gst.Video.TimeCode tc2); public Gst.Video.TimeCode copy (); [CCode (has_construct_function = false)] public TimeCode.empty (); public uint64 frames_since_daily_jam (); public void free (); [CCode (has_construct_function = false)] [Version (since = "1.12")] public TimeCode.from_date_time (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [CCode (has_construct_function = false)] [Version (since = "1.16")] public TimeCode.from_date_time_full (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [CCode (has_construct_function = false)] [Version (since = "1.12")] public TimeCode.from_string (string tc_str); public void increment_frame (); public void init (uint fps_n, uint fps_d, GLib.DateTime? latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); [Version (since = "1.12")] public void init_from_date_time (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); [Version (since = "1.16")] public bool init_from_date_time_full (uint fps_n, uint fps_d, GLib.DateTime dt, Gst.Video.TimeCodeFlags flags, uint field_count); public bool is_valid (); public uint64 nsec_since_daily_jam (); public GLib.DateTime? to_date_time (); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_time_code_interval_get_type ()")] [Compact] [GIR (name = "VideoTimeCodeInterval")] [Version (since = "1.12")] public class TimeCodeInterval { public uint frames; public uint hours; public uint minutes; public uint seconds; [CCode (has_construct_function = false)] public TimeCodeInterval (uint hours, uint minutes, uint seconds, uint frames); public void clear (); public Gst.Video.TimeCodeInterval copy (); public void free (); [CCode (has_construct_function = false)] public TimeCodeInterval.from_string (string tc_inter_str); public void init (uint hours, uint minutes, uint seconds, uint frames); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_vbi_encoder_get_type ()")] [Compact] [GIR (name = "VideoVBIEncoder")] [Version (since = "1.16")] public class VBIEncoder { [CCode (has_construct_function = false)] public VBIEncoder (Gst.Video.Format format, uint32 pixel_width); public bool add_ancillary (bool composite, uint8 DID, uint8 SDID_block_number, [CCode (array_length_cname = "data_count", array_length_pos = 4.1, array_length_type = "guint")] uint8[] data); public Gst.Video.VBIEncoder copy (); public void free (); public void write_line ([CCode (array_length = false, type = "guint8*")] uint8[] data); } [CCode (cheader_filename = "gst/video/video.h", copy_function = "g_boxed_copy", free_function = "g_boxed_free", type_id = "gst_video_vbi_parser_get_type ()")] [Compact] [GIR (name = "VideoVBIParser")] [Version (since = "1.16")] public class VBIParser { [CCode (has_construct_function = false)] public VBIParser (Gst.Video.Format format, uint32 pixel_width); public void add_line ([CCode (array_length = false)] uint8[] data); public Gst.Video.VBIParser copy (); public void free (); public Gst.Video.VBIParserResult get_ancillary (out Gst.Video.Ancillary anc); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalance", lower_case_cprefix = "gst_color_balance_", type_cname = "GstColorBalanceInterface", type_id = "gst_color_balance_get_type ()")] [GIR (name = "ColorBalance")] public interface ColorBalance : GLib.Object { public abstract Gst.Video.ColorBalanceType get_balance_type (); public abstract int get_value (Gst.Video.ColorBalanceChannel channel); public abstract unowned GLib.List list_channels (); public abstract void set_value (Gst.Video.ColorBalanceChannel channel, int value); [HasEmitter] public virtual signal void value_changed (Gst.Video.ColorBalanceChannel channel, int value); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoDirectionInterface", type_id = "gst_video_direction_get_type ()")] [GIR (name = "VideoDirection")] [Version (since = "1.10")] public interface Direction : GLib.Object { [NoAccessorMethod] public abstract Gst.Video.OrientationMethod video_direction { get; set construct; } } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigation", lower_case_cprefix = "gst_navigation_", type_cname = "GstNavigationInterface", type_id = "gst_navigation_get_type ()")] [GIR (name = "Navigation")] public interface Navigation : GLib.Object { [Version (since = "1.22")] public static bool event_get_coordinates (Gst.Event event, out double x, out double y); public static Gst.Video.NavigationEventType event_get_type (Gst.Event event); [Version (since = "1.22")] public static Gst.Event event_new_command (Gst.Video.NavigationCommand command); [Version (since = "1.22")] public static Gst.Event event_new_key_press (string key, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_key_release (string key, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_button_press (int button, double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_button_release (int button, double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_move (double x, double y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_mouse_scroll (double x, double y, double delta_x, double delta_y, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_cancel (Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_down (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_frame (Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_motion (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [Version (since = "1.22")] public static Gst.Event event_new_touch_up (uint identifier, double x, double y, Gst.Video.NavigationModifierType state); public static bool event_parse_command (Gst.Event event, out Gst.Video.NavigationCommand command); public static bool event_parse_key_event (Gst.Event event, out unowned string key); [Version (since = "1.22")] public static bool event_parse_modifier_state (Gst.Event event, Gst.Video.NavigationModifierType state); public static bool event_parse_mouse_button_event (Gst.Event event, out int button, out double x, out double y); public static bool event_parse_mouse_move_event (Gst.Event event, out double x, out double y); [Version (since = "1.18")] public static bool event_parse_mouse_scroll_event (Gst.Event event, out double x, out double y, out double delta_x, out double delta_y); [Version (since = "1.22")] public static bool event_parse_touch_event (Gst.Event event, out uint identifier, out double x, out double y, out double pressure); [Version (since = "1.22")] public static bool event_parse_touch_up_event (Gst.Event event, out uint identifier, out double x, out double y); [Version (since = "1.22")] public static bool event_set_coordinates (Gst.Event event, double x, double y); public static Gst.Video.NavigationMessageType message_get_type (Gst.Message message); public static Gst.Message message_new_angles_changed (Gst.Object src, uint cur_angle, uint n_angles); public static Gst.Message message_new_commands_changed (Gst.Object src); [Version (since = "1.6")] public static Gst.Message message_new_event (Gst.Object src, Gst.Event event); public static Gst.Message message_new_mouse_over (Gst.Object src, bool active); public static bool message_parse_angles_changed (Gst.Message message, out uint cur_angle, out uint n_angles); [Version (since = "1.6")] public static bool message_parse_event (Gst.Message message, out Gst.Event event); public static bool message_parse_mouse_over (Gst.Message message, out bool active); public static Gst.Video.NavigationQueryType query_get_type (Gst.Query query); public static Gst.Query query_new_angles (); public static Gst.Query query_new_commands (); public static bool query_parse_angles (Gst.Query query, out uint cur_angle, out uint n_angles); public static bool query_parse_commands_length (Gst.Query query, out uint n_cmds); public static bool query_parse_commands_nth (Gst.Query query, uint nth, out Gst.Video.NavigationCommand cmd); public static void query_set_angles (Gst.Query query, uint cur_angle, uint n_angles); public static void query_set_commandsv (Gst.Query query, [CCode (array_length_cname = "n_cmds", array_length_pos = 1.5)] Gst.Video.NavigationCommand[] cmds); public void send_command (Gst.Video.NavigationCommand command); [Version (deprecated = true, deprecated_since = "1.22")] public abstract void send_event (Gst.Structure structure); [Version (since = "1.22")] public abstract void send_event_simple (owned Gst.Event event); public void send_key_event (string event, string key); public void send_mouse_event (string event, int button, double x, double y); [Version (since = "1.18")] public void send_mouse_scroll_event (double x, double y, double delta_x, double delta_y); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoOrientationInterface", type_id = "gst_video_orientation_get_type ()")] [GIR (name = "VideoOrientation")] public interface Orientation : GLib.Object { [Version (since = "1.20")] public static bool from_tag (Gst.TagList taglist, out Gst.Video.OrientationMethod method); public abstract bool get_hcenter (out int center); public abstract bool get_hflip (out bool flip); public abstract bool get_vcenter (out int center); public abstract bool get_vflip (out bool flip); public abstract bool set_hcenter (int center); public abstract bool set_hflip (bool flip); public abstract bool set_vcenter (int center); public abstract bool set_vflip (bool flip); } [CCode (cheader_filename = "gst/video/video.h", type_cname = "GstVideoOverlayInterface", type_id = "gst_video_overlay_get_type ()")] [GIR (name = "VideoOverlay")] public interface Overlay : GLib.Object { public abstract void expose (); public void got_window_handle ([CCode (type = "guintptr")] uint* handle); public abstract void handle_events (bool handle_events); [Version (since = "1.14")] public static void install_properties (GLib.ObjectClass oclass, int last_prop_id); public void prepare_window_handle (); [Version (since = "1.14")] public static bool set_property (GLib.Object object, int last_prop_id, uint property_id, GLib.Value value); [NoWrapper] public virtual void set_render_rectangle (int x, int y, int width, int height); public abstract void set_window_handle ([CCode (type = "guintptr")] uint* handle); [CCode (cname = "gst_video_overlay_set_render_rectangle")] public bool try_set_render_rectangle (int x, int y, int width, int height); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAFDMeta")] [Version (since = "1.18")] public struct AFDMeta { public Gst.Meta meta; public uint8 field; public Gst.Video.AFDSpec spec; public Gst.Video.AFDValue afd; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAffineTransformationMeta")] [Version (since = "1.8")] public struct AffineTransformationMeta { public Gst.Meta meta; [CCode (array_length = false)] public weak float matrix[16]; public void apply_matrix ([CCode (array_length = false)] float matrix[16]); public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAlignment")] public struct Alignment { public uint padding_top; public uint padding_bottom; public uint padding_left; public uint padding_right; [CCode (array_length = false)] public weak uint stride_align[4]; public void reset (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoAncillary")] [Version (since = "1.16")] public struct Ancillary { public uint8 DID; public uint8 SDID_block_number; public uint8 data_count; [CCode (array_length_cname = "data_count", array_length_type = "guint8")] public weak uint8[] data; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoBarMeta")] [Version (since = "1.18")] public struct BarMeta { public Gst.Meta meta; public uint8 field; public bool is_letterbox; public uint bar_data1; public uint bar_data2; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCaptionMeta")] [Version (since = "1.16")] public struct CaptionMeta { public Gst.Meta meta; public Gst.Video.CaptionType caption_type; [CCode (array_length_cname = "size", array_length_type = "gsize")] public weak uint8[] data; public size_t size; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCodecAlphaMeta")] [Version (since = "1.20")] public struct CodecAlphaMeta { public Gst.Meta meta; public weak Gst.Buffer buffer; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoColorPrimariesInfo")] [Version (since = "1.6")] public struct ColorPrimariesInfo { public Gst.Video.ColorPrimaries primaries; public double Wx; public double Wy; public double Rx; public double Ry; public double Gx; public double Gy; public double Bx; public double By; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoColorimetry")] public struct Colorimetry { public Gst.Video.ColorRange range; public Gst.Video.ColorMatrix matrix; public Gst.Video.TransferFunction transfer; public Gst.Video.ColorPrimaries primaries; public bool from_string (string color); [Version (since = "1.6")] public bool is_equal (Gst.Video.Colorimetry other); [Version (since = "1.22")] public bool is_equivalent (uint bitdepth, Gst.Video.Colorimetry other, uint other_bitdepth); public bool matches (string color); public string? to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoContentLightLevel")] [Version (since = "1.18")] public struct ContentLightLevel { public uint16 max_content_light_level; public uint16 max_frame_average_light_level; public bool add_to_caps (Gst.Caps caps); public bool from_caps (Gst.Caps caps); public bool from_string (string level); public void init (); [Version (since = "1.20")] public bool is_equal (Gst.Video.ContentLightLevel other); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoCropMeta")] public struct CropMeta { public Gst.Meta meta; public uint x; public uint y; public uint width; public uint height; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoFormatInfo")] public struct FormatInfo { public Gst.Video.Format format; public weak string name; public weak string description; public Gst.Video.FormatFlags flags; public uint bits; public uint n_components; [CCode (array_length = false)] public weak uint shift[4]; [CCode (array_length = false)] public weak uint depth[4]; [CCode (array_length = false)] public weak int pixel_stride[4]; public uint n_planes; [CCode (array_length = false)] public weak uint plane[4]; [CCode (array_length = false)] public weak uint poffset[4]; [CCode (array_length = false)] public weak uint w_sub[4]; [CCode (array_length = false)] public weak uint h_sub[4]; public Gst.Video.Format unpack_format; public weak Gst.Video.FormatUnpack unpack_func; public int pack_lines; public weak Gst.Video.FormatPack pack_func; public Gst.Video.TileMode tile_mode; public uint tile_ws; public uint tile_hs; [Version (since = "1.18")] public void component (uint plane, out int components); [Version (since = "1.22")] public int extrapolate_stride (int plane, int stride); [Version (since = "1.22")] public uint get_tile_sizes (uint plane, uint? out_ws, uint? out_hs); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoFrame")] public struct Frame { public weak Gst.Video.Info info; public Gst.Video.FrameFlags flags; public weak Gst.Buffer buffer; public void* meta; public int id; [CCode (array_length = false)] public void* data[4]; [CCode (array_length = false, cname = "map")] public Gst.MapInfo map_info[4]; public bool copy (Gst.Video.Frame src); public bool copy_plane (Gst.Video.Frame src, uint plane); public bool map (Gst.Video.Info info, Gst.Buffer buffer, Gst.MapFlags flags); public bool map_id (Gst.Video.Info info, Gst.Buffer buffer, int id, Gst.MapFlags flags); public void unmap (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoGLTextureUploadMeta")] public struct GLTextureUploadMeta { public Gst.Meta meta; public Gst.Video.GLTextureOrientation texture_orientation; public uint n_textures; [CCode (array_length = false)] public weak Gst.Video.GLTextureType texture_type[4]; public static unowned Gst.MetaInfo? get_info (); public bool upload (uint texture_id); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMasteringDisplayInfo")] [Version (since = "1.18")] public struct MasteringDisplayInfo { [CCode (array_length = false)] public weak Gst.Video.MasteringDisplayInfoCoordinates display_primaries[3]; public Gst.Video.MasteringDisplayInfoCoordinates white_point; public uint32 max_display_mastering_luminance; public uint32 min_display_mastering_luminance; public bool add_to_caps (Gst.Caps caps); public bool from_caps (Gst.Caps caps); public bool from_string (string mastering); public void init (); public bool is_equal (Gst.Video.MasteringDisplayInfo other); public string to_string (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMasteringDisplayInfoCoordinates")] [Version (since = "1.18")] public struct MasteringDisplayInfoCoordinates { public uint16 x; public uint16 y; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMeta")] public struct Meta { public Gst.Meta meta; public weak Gst.Buffer buffer; public Gst.Video.FrameFlags flags; public Gst.Video.Format format; public int id; public uint width; public uint height; public uint n_planes; [CCode (array_length = false)] public weak size_t offset[4]; [CCode (array_length = false)] public weak int stride[4]; [CCode (cname = "map")] public weak Gst.Video.MetaMapVFunc map_v; [CCode (cname = "unmap")] public weak Gst.Video.MetaUnmapVFunc unmap_v; public Gst.Video.Alignment alignment; public static unowned Gst.MetaInfo? get_info (); [Version (since = "1.18")] public bool get_plane_height ([CCode (array_length = false)] out unowned uint plane_height[4]); [Version (since = "1.18")] public bool get_plane_size ([CCode (array_length = false)] out unowned size_t plane_size[4]); public bool map (uint plane, Gst.MapInfo info, out void* data, out int stride, Gst.MapFlags flags); [Version (since = "1.18")] public bool set_alignment (Gst.Video.Alignment alignment); public bool unmap (uint plane, Gst.MapInfo info); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoMetaTransform")] public struct MetaTransform { public weak Gst.Video.Info in_info; public weak Gst.Video.Info out_info; public static GLib.Quark scale_get_quark (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoOverlayCompositionMeta")] public struct OverlayCompositionMeta { public Gst.Meta meta; public weak Gst.Video.OverlayComposition overlay; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoRectangle")] public struct Rectangle { public int x; public int y; public int w; public int h; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoRegionOfInterestMeta")] public struct RegionOfInterestMeta { public Gst.Meta meta; public GLib.Quark roi_type; public int id; public int parent_id; public uint x; public uint y; public uint w; public uint h; public weak GLib.List @params; [Version (since = "1.14")] public void add_param (owned Gst.Structure s); public static unowned Gst.MetaInfo? get_info (); [Version (since = "1.14")] public unowned Gst.Structure? get_param (string name); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoResampler")] [Version (since = "1.6")] public struct Resampler { public int in_size; public int out_size; public uint max_taps; public uint n_phases; public uint32 offset; public uint32 phase; public uint32 n_taps; public double taps; public void clear (); public bool init (Gst.Video.ResamplerMethod method, Gst.Video.ResamplerFlags flags, uint n_phases, uint n_taps, double shift, uint in_size, uint out_size, Gst.Structure options); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoSEIUserDataUnregisteredMeta")] [Version (since = "1.22")] public struct SEIUserDataUnregisteredMeta { public Gst.Meta meta; [CCode (array_length = false)] public weak uint8 uuid[16]; public uint8 data; public size_t size; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoTimeCodeConfig")] [Version (since = "1.10")] public struct TimeCodeConfig { public uint fps_n; public uint fps_d; public Gst.Video.TimeCodeFlags flags; public weak GLib.DateTime latest_daily_jam; } [CCode (cheader_filename = "gst/video/video.h", has_type_id = false)] [GIR (name = "VideoTimeCodeMeta")] [Version (since = "1.10")] public struct TimeCodeMeta { public Gst.Meta meta; public weak Gst.Video.TimeCode tc; public static unowned Gst.MetaInfo? get_info (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_AFD_SPEC_", type_id = "gst_video_afd_spec_get_type ()")] [GIR (name = "VideoAFDSpec")] [Version (since = "1.18")] public enum AFDSpec { DVB_ETSI, ATSC_A53, SMPTE_ST2016_1 } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_AFD_", type_id = "gst_video_afd_value_get_type ()")] [GIR (name = "VideoAFDValue")] [Version (since = "1.18")] public enum AFDValue { UNAVAILABLE, @16_9_TOP_ALIGNED, @14_9_TOP_ALIGNED, GREATER_THAN_16_9, @4_3_FULL_16_9_FULL, @4_3_FULL_4_3_PILLAR, @16_9_LETTER_16_9_FULL, @14_9_LETTER_14_9_PILLAR, @4_3_FULL_14_9_CENTER, @16_9_LETTER_14_9_CENTER, @16_9_LETTER_4_3_CENTER } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ALPHA_MODE_", type_id = "gst_video_alpha_mode_get_type ()")] [GIR (name = "VideoAlphaMode")] [Version (since = "1.6")] public enum AlphaMode { COPY, SET, MULT } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ANCILLARY_DID_", type_id = "gst_video_ancillary_did_get_type ()")] [GIR (name = "VideoAncillaryDID")] [Version (since = "1.16")] public enum AncillaryDID { UNDEFINED, DELETION, HANC_3G_AUDIO_DATA_FIRST, HANC_3G_AUDIO_DATA_LAST, HANC_HDTV_AUDIO_DATA_FIRST, HANC_HDTV_AUDIO_DATA_LAST, HANC_SDTV_AUDIO_DATA_1_FIRST, HANC_SDTV_AUDIO_DATA_1_LAST, CAMERA_POSITION, HANC_ERROR_DETECTION, HANC_SDTV_AUDIO_DATA_2_FIRST, HANC_SDTV_AUDIO_DATA_2_LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ANCILLARY_DID16_", type_id = "gst_video_ancillary_di_d16_get_type ()")] [GIR (name = "VideoAncillaryDID16")] [Version (since = "1.16")] public enum AncillaryDID16 { S334_EIA_708, S334_EIA_608, S2016_3_AFD_BAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_BUFFER_FLAG_", type_id = "gst_video_buffer_flags_get_type ()")] [Flags] [GIR (name = "VideoBufferFlags")] public enum BufferFlags { INTERLACED, TFF, RFF, ONEFIELD, MULTIPLE_VIEW, FIRST_IN_BUNDLE, TOP_FIELD, BOTTOM_FIELD, MARKER, LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CAPTION_TYPE_", type_id = "gst_video_caption_type_get_type ()")] [GIR (name = "VideoCaptionType")] [Version (since = "1.16")] public enum CaptionType { UNKNOWN, CEA608_RAW, CEA608_S334_1A, CEA708_RAW, CEA708_CDP; public static Gst.Video.CaptionType from_caps (Gst.Caps caps); public Gst.Caps to_caps (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_FLAG_", type_id = "gst_video_chroma_flags_get_type ()")] [Flags] [GIR (name = "VideoChromaFlags")] public enum ChromaFlags { NONE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_METHOD_", type_id = "gst_video_chroma_method_get_type ()")] [GIR (name = "VideoChromaMethod")] public enum ChromaMethod { NEAREST, LINEAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_MODE_", type_id = "gst_video_chroma_mode_get_type ()")] [GIR (name = "VideoChromaMode")] [Version (since = "1.6")] public enum ChromaMode { FULL, UPSAMPLE_ONLY, DOWNSAMPLE_ONLY, NONE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CHROMA_SITE_", type_id = "gst_video_chroma_site_get_type ()")] [Flags] [GIR (name = "VideoChromaSite")] public enum ChromaSite { UNKNOWN, NONE, H_COSITED, V_COSITED, ALT_LINE, COSITED, JPEG, MPEG2, DV; [Version (since = "1.20")] public static Gst.Video.ChromaSite from_string (string s); [Version (since = "1.20")] public string? to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_CODEC_FRAME_FLAG_", type_id = "gst_video_codec_frame_flags_get_type ()")] [Flags] [GIR (name = "VideoCodecFrameFlags")] public enum CodecFrameFlags { DECODE_ONLY, SYNC_POINT, FORCE_KEYFRAME, FORCE_KEYFRAME_HEADERS, [Version (since = "1.20")] CORRUPTED } [CCode (cheader_filename = "gst/video/video.h", cname = "GstColorBalanceType", cprefix = "GST_COLOR_BALANCE_", type_id = "gst_color_balance_type_get_type ()")] [GIR (name = "ColorBalanceType")] public enum ColorBalanceType { HARDWARE, SOFTWARE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_MATRIX_", type_id = "gst_video_color_matrix_get_type ()")] [GIR (name = "VideoColorMatrix")] public enum ColorMatrix { UNKNOWN, RGB, FCC, BT709, BT601, SMPTE240M, BT2020; [Version (since = "1.18")] public static Gst.Video.ColorMatrix from_iso (uint value); [Version (since = "1.6")] public bool get_Kr_Kb (out double Kr, out double Kb); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_PRIMARIES_", type_id = "gst_video_color_primaries_get_type ()")] [GIR (name = "VideoColorPrimaries")] public enum ColorPrimaries { UNKNOWN, BT709, BT470M, BT470BG, SMPTE170M, SMPTE240M, FILM, BT2020, ADOBERGB, SMPTEST428, SMPTERP431, SMPTEEG432, EBU3213; [Version (since = "1.18")] public static Gst.Video.ColorPrimaries from_iso (uint value); [Version (since = "1.6")] public unowned Gst.Video.ColorPrimariesInfo? get_info (); [Version (since = "1.22")] public bool is_equivalent (Gst.Video.ColorPrimaries other); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_COLOR_RANGE_", type_id = "gst_video_color_range_get_type ()")] [GIR (name = "VideoColorRange")] public enum ColorRange { UNKNOWN, @0_255, @16_235; public void offsets (Gst.Video.FormatInfo info, [CCode (array_length = false)] out unowned int offset[4], [CCode (array_length = false)] out unowned int scale[4]); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DECODER_REQUEST_SYNC_POINT_", type_id = "gst_video_decoder_request_sync_point_flags_get_type ()")] [Flags] [GIR (name = "VideoDecoderRequestSyncPointFlags")] [Version (since = "1.20")] public enum DecoderRequestSyncPointFlags { DISCARD_INPUT, CORRUPT_OUTPUT } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DITHER_FLAG_", type_id = "gst_video_dither_flags_get_type ()")] [Flags] [GIR (name = "VideoDitherFlags")] public enum DitherFlags { NONE, INTERLACED, QUANTIZE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_DITHER_", type_id = "gst_video_dither_method_get_type ()")] [GIR (name = "VideoDitherMethod")] public enum DitherMethod { NONE, VERTERR, FLOYD_STEINBERG, SIERRA_LITE, BAYER } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FIELD_ORDER_", type_id = "gst_video_field_order_get_type ()")] [GIR (name = "VideoFieldOrder")] [Version (since = "1.12")] public enum FieldOrder { UNKNOWN, TOP_FIELD_FIRST, BOTTOM_FIELD_FIRST; public static Gst.Video.FieldOrder from_string (string order); public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FLAG_", type_id = "gst_video_flags_get_type ()")] [Flags] [GIR (name = "VideoFlags")] public enum Flags { NONE, VARIABLE_FPS, PREMULTIPLIED_ALPHA } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FORMAT_", type_id = "gst_video_format_get_type ()")] [GIR (name = "VideoFormat")] public enum Format { UNKNOWN, ENCODED, I420, YV12, YUY2, UYVY, AYUV, [CCode (cname = "GST_VIDEO_FORMAT_RGBx")] RGBX, [CCode (cname = "GST_VIDEO_FORMAT_BGRx")] BGRX, [CCode (cname = "GST_VIDEO_FORMAT_xRGB")] XRGB, [CCode (cname = "GST_VIDEO_FORMAT_xBGR")] XBGR, RGBA, BGRA, ARGB, ABGR, RGB, BGR, Y41B, Y42B, YVYU, Y444, [CCode (cname = "GST_VIDEO_FORMAT_v210")] V210, [CCode (cname = "GST_VIDEO_FORMAT_v216")] V216, NV12, NV21, GRAY8, GRAY16_BE, GRAY16_LE, [CCode (cname = "GST_VIDEO_FORMAT_v308")] V308, RGB16, BGR16, RGB15, BGR15, UYVP, A420, RGB8P, YUV9, YVU9, IYU1, ARGB64, AYUV64, [CCode (cname = "GST_VIDEO_FORMAT_r210")] R210, I420_10BE, I420_10LE, I422_10BE, I422_10LE, Y444_10BE, Y444_10LE, GBR, GBR_10BE, GBR_10LE, NV16, NV24, NV12_64Z32, A420_10BE, A420_10LE, A422_10BE, A422_10LE, A444_10BE, A444_10LE, NV61, P010_10BE, P010_10LE, IYU2, VYUY, GBRA, GBRA_10BE, GBRA_10LE, GBR_12BE, GBR_12LE, GBRA_12BE, GBRA_12LE, I420_12BE, I420_12LE, I422_12BE, I422_12LE, Y444_12BE, Y444_12LE, GRAY10_LE32, NV12_10LE32, NV16_10LE32, NV12_10LE40, Y210, Y410, VUYA, BGR10A2_LE, RGB10A2_LE, Y444_16BE, Y444_16LE, P016_BE, P016_LE, P012_BE, P012_LE, Y212_BE, Y212_LE, Y412_BE, Y412_LE, [Version (since = "1.18")] NV12_4L4, [Version (since = "1.18")] NV12_32L32, [Version (since = "1.20")] RGBP, [Version (since = "1.20")] BGRP, [Version (since = "1.20")] AV12, [Version (since = "1.20")] ARGB64_LE, [Version (since = "1.20")] ARGB64_BE, [Version (since = "1.20")] RGBA64_LE, [Version (since = "1.20")] RGBA64_BE, [Version (since = "1.20")] BGRA64_LE, [Version (since = "1.20")] BGRA64_BE, [Version (since = "1.20")] ABGR64_LE, [Version (since = "1.20")] ABGR64_BE, [Version (since = "1.22")] NV12_16L32S, [Version (since = "1.22")] NV12_8L128, [Version (since = "1.22")] NV12_10BE_8L128; public static Gst.Video.Format from_fourcc (uint32 fourcc); public static Gst.Video.Format from_masks (int depth, int bpp, int endianness, uint red_mask, uint green_mask, uint blue_mask, uint alpha_mask); public static Gst.Video.Format from_string (string format); public unowned Gst.Video.FormatInfo? get_info (); [Version (since = "1.2")] public void* get_palette (out size_t size); public uint32 to_fourcc (); public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FORMAT_FLAG_", type_id = "gst_video_format_flags_get_type ()")] [Flags] [GIR (name = "VideoFormatFlags")] public enum FormatFlags { YUV, RGB, GRAY, ALPHA, LE, PALETTE, COMPLEX, UNPACK, TILED, [Version (since = "1.22")] SUBTILES } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FRAME_FLAG_", type_id = "gst_video_frame_flags_get_type ()")] [Flags] [GIR (name = "VideoFrameFlags")] public enum FrameFlags { NONE, INTERLACED, TFF, RFF, ONEFIELD, MULTIPLE_VIEW, FIRST_IN_BUNDLE, TOP_FIELD, BOTTOM_FIELD } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_FRAME_MAP_FLAG_", type_id = "gst_video_frame_map_flags_get_type ()")] [Flags] [GIR (name = "VideoFrameMapFlags")] [Version (since = "1.6")] public enum FrameMapFlags { NO_REF, LAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GL_TEXTURE_ORIENTATION_X_", type_id = "gst_video_gl_texture_orientation_get_type ()")] [GIR (name = "VideoGLTextureOrientation")] public enum GLTextureOrientation { NORMAL_Y_NORMAL, NORMAL_Y_FLIP, FLIP_Y_NORMAL, FLIP_Y_FLIP } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GL_TEXTURE_TYPE_", type_id = "gst_video_gl_texture_type_get_type ()")] [GIR (name = "VideoGLTextureType")] public enum GLTextureType { LUMINANCE, LUMINANCE_ALPHA, RGB16, RGB, RGBA, R, RG } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_GAMMA_MODE_", type_id = "gst_video_gamma_mode_get_type ()")] [GIR (name = "VideoGammaMode")] [Version (since = "1.6")] public enum GammaMode { NONE, REMAP } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_INTERLACE_MODE_", type_id = "gst_video_interlace_mode_get_type ()")] [GIR (name = "VideoInterlaceMode")] public enum InterlaceMode { PROGRESSIVE, INTERLEAVED, MIXED, FIELDS, ALTERNATE; [Version (since = "1.6")] public static Gst.Video.InterlaceMode from_string (string mode); [Version (since = "1.6")] public unowned string to_string (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MATRIX_MODE_", type_id = "gst_video_matrix_mode_get_type ()")] [GIR (name = "VideoMatrixMode")] [Version (since = "1.6")] public enum MatrixMode { FULL, INPUT_ONLY, OUTPUT_ONLY, NONE } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_FLAGS_", type_id = "gst_video_multiview_flags_get_type ()")] [Flags] [GIR (name = "VideoMultiviewFlags")] public enum MultiviewFlags { NONE, RIGHT_VIEW_FIRST, LEFT_FLIPPED, LEFT_FLOPPED, RIGHT_FLIPPED, RIGHT_FLOPPED, HALF_ASPECT, MIXED_MONO } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_FRAME_PACKING_", type_id = "gst_video_multiview_frame_packing_get_type ()")] [GIR (name = "VideoMultiviewFramePacking")] public enum MultiviewFramePacking { NONE, MONO, LEFT, RIGHT, SIDE_BY_SIDE, SIDE_BY_SIDE_QUINCUNX, COLUMN_INTERLEAVED, ROW_INTERLEAVED, TOP_BOTTOM, CHECKERBOARD } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_MULTIVIEW_MODE_", type_id = "gst_video_multiview_mode_get_type ()")] [GIR (name = "VideoMultiviewMode")] public enum MultiviewMode { NONE, MONO, LEFT, RIGHT, SIDE_BY_SIDE, SIDE_BY_SIDE_QUINCUNX, COLUMN_INTERLEAVED, ROW_INTERLEAVED, TOP_BOTTOM, CHECKERBOARD, FRAME_BY_FRAME, MULTIVIEW_FRAME_BY_FRAME, SEPARATED; [Version (since = "1.6")] public static Gst.Video.MultiviewMode from_caps_string (string caps_mview_mode); [Version (since = "1.6")] public unowned string to_caps_string (); } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationCommand", cprefix = "GST_NAVIGATION_COMMAND_", type_id = "gst_navigation_command_get_type ()")] [GIR (name = "NavigationCommand")] public enum NavigationCommand { INVALID, MENU1, MENU2, MENU3, MENU4, MENU5, MENU6, MENU7, LEFT, RIGHT, UP, DOWN, ACTIVATE, PREV_ANGLE, NEXT_ANGLE } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationEventType", cprefix = "GST_NAVIGATION_EVENT_", type_id = "gst_navigation_event_type_get_type ()")] [GIR (name = "NavigationEventType")] public enum NavigationEventType { INVALID, KEY_PRESS, KEY_RELEASE, MOUSE_BUTTON_PRESS, MOUSE_BUTTON_RELEASE, MOUSE_MOVE, COMMAND, [Version (since = "1.18")] MOUSE_SCROLL, [Version (since = "1.22")] TOUCH_DOWN, [Version (since = "1.22")] TOUCH_MOTION, [Version (since = "1.22")] TOUCH_UP, [Version (since = "1.22")] TOUCH_FRAME, [Version (since = "1.22")] TOUCH_CANCEL } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationMessageType", cprefix = "GST_NAVIGATION_MESSAGE_", type_id = "gst_navigation_message_type_get_type ()")] [GIR (name = "NavigationMessageType")] public enum NavigationMessageType { INVALID, MOUSE_OVER, COMMANDS_CHANGED, ANGLES_CHANGED, EVENT } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationModifierType", cprefix = "GST_NAVIGATION_MODIFIER_", type_id = "gst_navigation_modifier_type_get_type ()")] [Flags] [GIR (name = "NavigationModifierType")] [Version (since = "1.22")] public enum NavigationModifierType { NONE, SHIFT_MASK, LOCK_MASK, CONTROL_MASK, ALT_MASK, BUTTON1_MASK, BUTTON2_MASK, BUTTON3_MASK, BUTTON4_MASK, BUTTON5_MASK, SUPER_MASK, HYPER_MASK, META_MASK, MASK } [CCode (cheader_filename = "gst/video/video.h", cname = "GstNavigationQueryType", cprefix = "GST_NAVIGATION_QUERY_", type_id = "gst_navigation_query_type_get_type ()")] [GIR (name = "NavigationQueryType")] public enum NavigationQueryType { INVALID, COMMANDS, ANGLES } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_ORIENTATION_", type_id = "gst_video_orientation_method_get_type ()")] [GIR (name = "VideoOrientationMethod")] [Version (since = "1.10")] public enum OrientationMethod { IDENTITY, @90R, @180, @90L, HORIZ, VERT, UL_LR, UR_LL, AUTO, CUSTOM } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_OVERLAY_FORMAT_FLAG_", type_id = "gst_video_overlay_format_flags_get_type ()")] [Flags] [GIR (name = "VideoOverlayFormatFlags")] public enum OverlayFormatFlags { NONE, PREMULTIPLIED_ALPHA, GLOBAL_ALPHA } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_PACK_FLAG_", type_id = "gst_video_pack_flags_get_type ()")] [Flags] [GIR (name = "VideoPackFlags")] public enum PackFlags { NONE, TRUNCATE_RANGE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_PRIMARIES_MODE_", type_id = "gst_video_primaries_mode_get_type ()")] [GIR (name = "VideoPrimariesMode")] [Version (since = "1.6")] public enum PrimariesMode { NONE, MERGE_ONLY, FAST } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_RESAMPLER_FLAG_", type_id = "gst_video_resampler_flags_get_type ()")] [Flags] [GIR (name = "VideoResamplerFlags")] [Version (since = "1.6")] public enum ResamplerFlags { NONE, HALF_TAPS } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_RESAMPLER_METHOD_", type_id = "gst_video_resampler_method_get_type ()")] [GIR (name = "VideoResamplerMethod")] [Version (since = "1.6")] public enum ResamplerMethod { NEAREST, LINEAR, CUBIC, SINC, LANCZOS } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_SCALER_FLAG_", type_id = "gst_video_scaler_flags_get_type ()")] [Flags] [GIR (name = "VideoScalerFlags")] public enum ScalerFlags { NONE, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TILE_MODE_", type_id = "gst_video_tile_mode_get_type ()")] [GIR (name = "VideoTileMode")] public enum TileMode { UNKNOWN, ZFLIPZ_2X2, [Version (since = "1.18")] LINEAR } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TILE_TYPE_", type_id = "gst_video_tile_type_get_type ()")] [GIR (name = "VideoTileType")] public enum TileType { INDEXED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TIME_CODE_FLAGS_", type_id = "gst_video_time_code_flags_get_type ()")] [Flags] [GIR (name = "VideoTimeCodeFlags")] [Version (since = "1.10")] public enum TimeCodeFlags { NONE, DROP_FRAME, INTERLACED } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_TRANSFER_", type_id = "gst_video_transfer_function_get_type ()")] [GIR (name = "VideoTransferFunction")] public enum TransferFunction { UNKNOWN, GAMMA10, GAMMA18, GAMMA20, GAMMA22, BT709, SMPTE240M, SRGB, GAMMA28, LOG100, LOG316, BT2020_12, ADOBERGB, BT2020_10, SMPTE2084, ARIB_STD_B67, [Version (since = "1.18")] BT601; [Version (since = "1.20")] public double decode (double val); [Version (since = "1.20")] public double encode (double val); [Version (since = "1.18")] public static Gst.Video.TransferFunction from_iso (uint value); [Version (since = "1.18")] public bool is_equivalent (uint from_bpp, Gst.Video.TransferFunction to_func, uint to_bpp); [Version (since = "1.18")] public uint to_iso (); } [CCode (cheader_filename = "gst/video/video.h", cprefix = "GST_VIDEO_VBI_PARSER_RESULT_", type_id = "gst_video_vbi_parser_result_get_type ()")] [GIR (name = "VideoVBIParserResult")] [Version (since = "1.16")] public enum VBIParserResult { DONE, OK, ERROR } [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate bool AffineTransformationGetMatrix (Gst.Video.AffineTransformationMeta meta, float matrix); [CCode (cheader_filename = "gst/video/video.h", instance_pos = 2.9)] public delegate void ConvertSampleCallback (Gst.Sample sample, GLib.Error error); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate void FormatPack (Gst.Video.FormatInfo info, Gst.Video.PackFlags flags, void* src, int sstride, void* data, int stride, Gst.Video.ChromaSite chroma_site, int y, int width); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate void FormatUnpack (Gst.Video.FormatInfo info, Gst.Video.PackFlags flags, void* dest, void* data, int stride, int x, int y, int width); [CCode (cheader_filename = "gst/video/video.h", has_target = false)] public delegate bool GLTextureUpload (Gst.Video.GLTextureUploadMeta meta, uint texture_id); [CCode (cheader_filename = "gst/video/video.h", has_target = false, has_typedef = false)] public delegate bool MetaMapVFunc (Gst.Video.Meta meta, uint plane, Gst.MapInfo info, void* data, int stride, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h", has_target = false, has_typedef = false)] public delegate bool MetaUnmapVFunc (Gst.Video.Meta meta, uint plane, Gst.MapInfo info); [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_AFFINE_TRANSFORMATION_META")] public const string BUFFER_POOL_OPTION_VIDEO_AFFINE_TRANSFORMATION_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT")] public const string BUFFER_POOL_OPTION_VIDEO_ALIGNMENT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META")] [Version (since = "1.2.2")] public const string BUFFER_POOL_OPTION_VIDEO_GL_TEXTURE_UPLOAD_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_BUFFER_POOL_OPTION_VIDEO_META")] public const string BUFFER_POOL_OPTION_VIDEO_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_FORMAT_INTERLACED")] [Version (since = "1.16.")] public const string CAPS_FEATURE_FORMAT_INTERLACED; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META")] public const string CAPS_FEATURE_META_GST_VIDEO_AFFINE_TRANSFORMATION_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META")] public const string CAPS_FEATURE_META_GST_VIDEO_GL_TEXTURE_UPLOAD_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_META")] public const string CAPS_FEATURE_META_GST_VIDEO_META; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION")] public const string CAPS_FEATURE_META_GST_VIDEO_OVERLAY_COMPOSITION; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2020")] public const string COLORIMETRY_BT2020; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2020_10")] public const string COLORIMETRY_BT2020_10; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2100_HLG")] public const string COLORIMETRY_BT2100_HLG; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT2100_PQ")] public const string COLORIMETRY_BT2100_PQ; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT601")] public const string COLORIMETRY_BT601; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_BT709")] public const string COLORIMETRY_BT709; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_SMPTE240M")] public const string COLORIMETRY_SMPTE240M; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COLORIMETRY_SRGB")] public const string COLORIMETRY_SRGB; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_A")] public const int COMP_A; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_B")] public const int COMP_B; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_G")] public const int COMP_G; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_INDEX")] public const int COMP_INDEX; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_PALETTE")] public const int COMP_PALETTE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_R")] public const int COMP_R; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_U")] public const int COMP_U; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_V")] public const int COMP_V; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_COMP_Y")] public const int COMP_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ALPHA_MODE")] public const string CONVERTER_OPT_ALPHA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ALPHA_VALUE")] public const string CONVERTER_OPT_ALPHA_VALUE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_ASYNC_TASKS")] [Version (since = "1.20")] public const string CONVERTER_OPT_ASYNC_TASKS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_BORDER_ARGB")] public const string CONVERTER_OPT_BORDER_ARGB; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_CHROMA_MODE")] public const string CONVERTER_OPT_CHROMA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_CHROMA_RESAMPLER_METHOD")] public const string CONVERTER_OPT_CHROMA_RESAMPLER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_HEIGHT")] public const string CONVERTER_OPT_DEST_HEIGHT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_WIDTH")] public const string CONVERTER_OPT_DEST_WIDTH; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_X")] public const string CONVERTER_OPT_DEST_X; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DEST_Y")] public const string CONVERTER_OPT_DEST_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DITHER_METHOD")] public const string CONVERTER_OPT_DITHER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_DITHER_QUANTIZATION")] public const string CONVERTER_OPT_DITHER_QUANTIZATION; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_FILL_BORDER")] public const string CONVERTER_OPT_FILL_BORDER; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_GAMMA_MODE")] public const string CONVERTER_OPT_GAMMA_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_MATRIX_MODE")] public const string CONVERTER_OPT_MATRIX_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_PRIMARIES_MODE")] public const string CONVERTER_OPT_PRIMARIES_MODE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_RESAMPLER_METHOD")] public const string CONVERTER_OPT_RESAMPLER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_RESAMPLER_TAPS")] public const string CONVERTER_OPT_RESAMPLER_TAPS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_HEIGHT")] public const string CONVERTER_OPT_SRC_HEIGHT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_WIDTH")] public const string CONVERTER_OPT_SRC_WIDTH; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_X")] public const string CONVERTER_OPT_SRC_X; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_SRC_Y")] public const string CONVERTER_OPT_SRC_Y; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_CONVERTER_OPT_THREADS")] public const string CONVERTER_OPT_THREADS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_MAX_ERRORS")] public const int DECODER_MAX_ERRORS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_SINK_NAME")] public const string DECODER_SINK_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_DECODER_SRC_NAME")] public const string DECODER_SRC_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_ENCODER_SINK_NAME")] public const string ENCODER_SINK_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_ENCODER_SRC_NAME")] public const string ENCODER_SRC_NAME; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_FORMATS_ALL")] public const string FORMATS_ALL; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_FPS_RANGE")] public const string FPS_RANGE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_MAX_COMPONENTS")] public const int MAX_COMPONENTS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_MAX_PLANES")] public const int MAX_PLANES; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_COLORSPACE_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_COLORSPACE_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_ORIENTATION_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_ORIENTATION_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_SIZE_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_SIZE_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_META_TAG_VIDEO_STR")] [Version (since = "1.2")] public const string META_TAG_VIDEO_STR; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_CUBIC_B")] public const string RESAMPLER_OPT_CUBIC_B; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_CUBIC_C")] public const string RESAMPLER_OPT_CUBIC_C; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_ENVELOPE")] public const string RESAMPLER_OPT_ENVELOPE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_MAX_TAPS")] public const string RESAMPLER_OPT_MAX_TAPS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_SHARPEN")] public const string RESAMPLER_OPT_SHARPEN; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_RESAMPLER_OPT_SHARPNESS")] public const string RESAMPLER_OPT_SHARPNESS; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_SCALER_OPT_DITHER_METHOD")] public const string SCALER_OPT_DITHER_METHOD; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_SIZE_RANGE")] public const string SIZE_RANGE; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_TYPE_MASK")] public const int TILE_TYPE_MASK; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_TYPE_SHIFT")] public const int TILE_TYPE_SHIFT; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_X_TILES_MASK")] public const int TILE_X_TILES_MASK; [CCode (cheader_filename = "gst/video/video.h", cname = "GST_VIDEO_TILE_Y_TILES_SHIFT")] public const int TILE_Y_TILES_SHIFT; [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type afd_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoAFDMeta.get_info")] public static unowned Gst.MetaInfo? afd_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type affine_transformation_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoAffineTransformationMeta.get_info")] public static unowned Gst.MetaInfo? affine_transformation_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type bar_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoBarMeta.get_info")] public static unowned Gst.MetaInfo? bar_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static bool blend (Gst.Video.Frame dest, Gst.Video.Frame src, int x, int y, float global_alpha); [CCode (cheader_filename = "gst/video/video.h")] public static void blend_scale_linear_RGBA (Gst.Video.Info src, Gst.Buffer src_buffer, int dest_height, int dest_width, out unowned Gst.Video.Info dest, out Gst.Buffer dest_buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_afd_meta")] [Version (since = "1.18")] public static unowned Gst.Video.AFDMeta? buffer_add_video_afd_meta (Gst.Buffer buffer, uint8 field, Gst.Video.AFDSpec spec, Gst.Video.AFDValue afd); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_affine_transformation_meta")] [Version (since = "1.8")] public static unowned Gst.Video.AffineTransformationMeta? buffer_add_video_affine_transformation_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_bar_meta")] [Version (since = "1.18")] public static unowned Gst.Video.BarMeta? buffer_add_video_bar_meta (Gst.Buffer buffer, uint8 field, bool is_letterbox, uint bar_data1, uint bar_data2); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_caption_meta")] [Version (since = "1.16")] public static unowned Gst.Video.CaptionMeta? buffer_add_video_caption_meta (Gst.Buffer buffer, Gst.Video.CaptionType caption_type, [CCode (array_length_cname = "size", array_length_pos = 3.1, array_length_type = "gsize")] uint8[] data); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_codec_alpha_meta")] [Version (since = "1.20")] public static unowned Gst.Video.CodecAlphaMeta? buffer_add_video_codec_alpha_meta (Gst.Buffer buffer, owned Gst.Buffer alpha_buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_gl_texture_upload_meta")] public static unowned Gst.Video.GLTextureUploadMeta? buffer_add_video_gl_texture_upload_meta (Gst.Buffer buffer, Gst.Video.GLTextureOrientation texture_orientation, uint n_textures, Gst.Video.GLTextureType texture_type, [CCode (delegate_target_pos = 5.5)] Gst.Video.GLTextureUpload upload, GLib.BoxedCopyFunc user_data_copy, GLib.BoxedFreeFunc user_data_free); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_meta")] public static unowned Gst.Video.Meta? buffer_add_video_meta (Gst.Buffer buffer, Gst.Video.FrameFlags flags, Gst.Video.Format format, uint width, uint height); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_meta_full")] public static unowned Gst.Video.Meta? buffer_add_video_meta_full (Gst.Buffer buffer, Gst.Video.FrameFlags flags, Gst.Video.Format format, uint width, uint height, uint n_planes, [CCode (array_length = false)] size_t offset[4], [CCode (array_length = false)] int stride[4]); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_overlay_composition_meta")] public static unowned Gst.Video.OverlayCompositionMeta? buffer_add_video_overlay_composition_meta (Gst.Buffer buf, Gst.Video.OverlayComposition? comp); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_region_of_interest_meta")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_add_video_region_of_interest_meta (Gst.Buffer buffer, string roi_type, uint x, uint y, uint w, uint h); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_region_of_interest_meta_id")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_add_video_region_of_interest_meta_id (Gst.Buffer buffer, GLib.Quark roi_type, uint x, uint y, uint w, uint h); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_sei_user_data_unregistered_meta")] [Version (since = "1.22")] public static unowned Gst.Video.SEIUserDataUnregisteredMeta? buffer_add_video_sei_user_data_unregistered_meta (Gst.Buffer buffer, uint8 uuid, uint8 data, size_t size); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_time_code_meta")] [Version (since = "1.10")] public static unowned Gst.Video.TimeCodeMeta? buffer_add_video_time_code_meta (Gst.Buffer buffer, Gst.Video.TimeCode tc); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_add_video_time_code_meta_full")] [Version (since = "1.10")] public static unowned Gst.Video.TimeCodeMeta? buffer_add_video_time_code_meta_full (Gst.Buffer buffer, uint fps_n, uint fps_d, GLib.DateTime latest_daily_jam, Gst.Video.TimeCodeFlags flags, uint hours, uint minutes, uint seconds, uint frames, uint field_count); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_meta")] public static unowned Gst.Video.Meta? buffer_get_video_meta (Gst.Buffer buffer); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_meta_id")] public static unowned Gst.Video.Meta? buffer_get_video_meta_id (Gst.Buffer buffer, int id); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_get_video_region_of_interest_meta_id")] public static unowned Gst.Video.RegionOfInterestMeta? buffer_get_video_region_of_interest_meta_id (Gst.Buffer buffer, int id); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_pool_config_get_video_alignment")] public static bool buffer_pool_config_get_video_alignment (Gst.Structure config, Gst.Video.Alignment align); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_buffer_pool_config_set_video_alignment")] public static void buffer_pool_config_set_video_alignment (Gst.Structure config, Gst.Video.Alignment align); [CCode (cheader_filename = "gst/video/video.h")] public static bool calculate_display_ratio (out uint dar_n, out uint dar_d, uint video_width, uint video_height, uint video_par_n, uint video_par_d, uint display_par_n, uint display_par_d); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type caption_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionMeta.get_info")] public static unowned Gst.MetaInfo? caption_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionType.from_caps", since = "1.16")] public static Gst.Video.CaptionType caption_type_from_caps (Gst.Caps caps); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCaptionType.to_caps", since = "1.16")] public static Gst.Caps caption_type_to_caps (Gst.Video.CaptionType type); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.20")] public static void center_rect (Gst.Video.Rectangle src, Gst.Video.Rectangle dst, out Gst.Video.Rectangle result, bool scaling); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20")] public static Gst.Video.ChromaSite chroma_from_string (string s); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoChromaSite.from_string", since = "1.20")] public static Gst.Video.ChromaSite chroma_site_from_string (string s); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoChromaSite.to_string", since = "1.20")] public static string? chroma_site_to_string (Gst.Video.ChromaSite site); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20")] public static unowned string chroma_to_string (Gst.Video.ChromaSite site); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.20")] public static GLib.Type codec_alpha_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCodecAlphaMeta.get_info", since = "1.20")] public static unowned Gst.MetaInfo? codec_alpha_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.from_iso", since = "1.18")] public static Gst.Video.ColorMatrix color_matrix_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.get_Kr_Kb", since = "1.6")] public static bool color_matrix_get_Kr_Kb (Gst.Video.ColorMatrix matrix, out double Kr, out double Kb); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorMatrix.to_iso", since = "1.18")] public static uint color_matrix_to_iso (Gst.Video.ColorMatrix matrix); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.from_iso", since = "1.18")] public static Gst.Video.ColorPrimaries color_primaries_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.get_info", since = "1.6")] public static unowned Gst.Video.ColorPrimariesInfo? color_primaries_get_info (Gst.Video.ColorPrimaries primaries); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.is_equivalent", since = "1.22")] public static bool color_primaries_is_equivalent (Gst.Video.ColorPrimaries primaries, Gst.Video.ColorPrimaries other); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorPrimaries.to_iso", since = "1.18")] public static uint color_primaries_to_iso (Gst.Video.ColorPrimaries primaries); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoColorRange.offsets")] public static void color_range_offsets (Gst.Video.ColorRange range, Gst.Video.FormatInfo info, [CCode (array_length = false)] out unowned int offset[4], [CCode (array_length = false)] out unowned int scale[4]); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20", since = "1.6")] public static double color_transfer_decode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (deprecated = true, deprecated_since = "1.20", since = "1.6")] public static double color_transfer_encode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Sample convert_sample (Gst.Sample sample, Gst.Caps to_caps, Gst.ClockTime timeout) throws GLib.Error; [CCode (cheader_filename = "gst/video/video.h")] public static void convert_sample_async (Gst.Sample sample, Gst.Caps to_caps, Gst.ClockTime timeout, owned Gst.Video.ConvertSampleCallback callback); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type crop_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoCropMeta.get_info")] public static unowned Gst.MetaInfo? crop_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_is_force_key_unit (Gst.Event event); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_downstream_force_key_unit (Gst.ClockTime timestamp, Gst.ClockTime stream_time, Gst.ClockTime running_time, bool all_headers, uint count); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_still_frame (bool in_still); [CCode (cheader_filename = "gst/video/video.h")] public static Gst.Event event_new_upstream_force_key_unit (Gst.ClockTime running_time, bool all_headers, uint count); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_downstream_force_key_unit (Gst.Event event, out Gst.ClockTime timestamp, out Gst.ClockTime stream_time, out Gst.ClockTime running_time, out bool all_headers, out uint count); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_still_frame (Gst.Event event, out bool in_still); [CCode (cheader_filename = "gst/video/video.h")] public static bool event_parse_upstream_force_key_unit (Gst.Event event, out Gst.ClockTime running_time, out bool all_headers, out uint count); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFieldOrder.from_string", since = "1.12")] public static Gst.Video.FieldOrder field_order_from_string (string order); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFieldOrder.to_string", since = "1.12")] public static unowned string field_order_to_string (Gst.Video.FieldOrder order); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_fourcc")] public static Gst.Video.Format format_from_fourcc (uint32 fourcc); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_masks")] public static Gst.Video.Format format_from_masks (int depth, int bpp, int endianness, uint red_mask, uint green_mask, uint blue_mask, uint alpha_mask); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.from_string")] public static Gst.Video.Format format_from_string (string format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.get_info")] public static unowned Gst.Video.FormatInfo? format_get_info (Gst.Video.Format format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.get_palette", since = "1.2")] public static void* format_get_palette (Gst.Video.Format format, out size_t size); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.to_fourcc")] public static uint32 format_to_fourcc (Gst.Video.Format format); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFormat.to_string")] public static unowned string format_to_string (Gst.Video.Format format); [CCode (array_length_pos = 0.1, array_length_type = "guint", cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static unowned Gst.Video.Format[] formats_raw (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFrame.map")] public static bool frame_map (out Gst.Video.Frame frame, Gst.Video.Info info, Gst.Buffer buffer, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoFrame.map_id")] public static bool frame_map_id (out Gst.Video.Frame frame, Gst.Video.Info info, Gst.Buffer buffer, int id, Gst.MapFlags flags); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type gl_texture_upload_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoGLTextureUploadMeta.get_info")] public static unowned Gst.MetaInfo? gl_texture_upload_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static bool guess_framerate (Gst.ClockTime duration, out int dest_n, out int dest_d); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInfo.from_caps")] public static bool info_from_caps (out unowned Gst.Video.Info info, Gst.Caps caps); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInfo.init")] public static void info_init (out unowned Gst.Video.Info info); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInterlaceMode.from_string", since = "1.6")] public static Gst.Video.InterlaceMode interlace_mode_from_string (string mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoInterlaceMode.to_string", since = "1.6")] public static unowned string interlace_mode_to_string (Gst.Video.InterlaceMode mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static bool is_common_aspect_ratio (int width, int height, int par_n, int par_d); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_is_video_overlay_prepare_window_handle_message")] public static bool is_video_overlay_prepare_window_handle_message (Gst.Message msg); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static Gst.Caps make_raw_caps ([CCode (array_length_cname = "len", array_length_pos = 1.1, array_length_type = "guint")] Gst.Video.Format[]? formats); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.18")] public static Gst.Caps make_raw_caps_with_features ([CCode (array_length_cname = "len", array_length_pos = 1.5, array_length_type = "guint")] Gst.Video.Format[]? formats, owned Gst.CapsFeatures? features); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMasteringDisplayInfo.from_string", since = "1.18")] public static bool mastering_display_info_from_string (out Gst.Video.MasteringDisplayInfo minfo, string mastering); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMeta.get_info")] public static unowned Gst.MetaInfo? meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMetaTransform.scale_get_quark")] public static GLib.Quark meta_transform_scale_get_quark (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_height_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_size_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_doubled_width_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_mono_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static unowned GLib.Value? multiview_get_unpacked_modes (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static bool multiview_guess_half_aspect (Gst.Video.MultiviewMode mv_mode, uint width, uint height, uint par_n, uint par_d); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMultiviewMode.from_caps_string", since = "1.6")] public static Gst.Video.MultiviewMode multiview_mode_from_caps_string (string caps_mview_mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoMultiviewMode.to_caps_string", since = "1.6")] public static unowned string multiview_mode_to_caps_string (Gst.Video.MultiviewMode mview_mode); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.6")] public static void multiview_video_info_change_mode (Gst.Video.Info info, Gst.Video.MultiviewMode out_mview_mode, Gst.Video.MultiviewFlags out_mview_flags); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_get_coordinates")] [Version (replacement = "Navigation.event_get_coordinates", since = "1.22")] public static bool navigation_event_get_coordinates (Gst.Event event, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_get_type")] [Version (replacement = "Navigation.event_get_type")] public static Gst.Video.NavigationEventType navigation_event_get_type (Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_command")] [Version (replacement = "Navigation.event_new_command", since = "1.22")] public static Gst.Event navigation_event_new_command (Gst.Video.NavigationCommand command); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_key_press")] [Version (replacement = "Navigation.event_new_key_press", since = "1.22")] public static Gst.Event navigation_event_new_key_press (string key, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_key_release")] [Version (replacement = "Navigation.event_new_key_release", since = "1.22")] public static Gst.Event navigation_event_new_key_release (string key, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_button_press")] [Version (replacement = "Navigation.event_new_mouse_button_press", since = "1.22")] public static Gst.Event navigation_event_new_mouse_button_press (int button, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_button_release")] [Version (replacement = "Navigation.event_new_mouse_button_release", since = "1.22")] public static Gst.Event navigation_event_new_mouse_button_release (int button, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_move")] [Version (replacement = "Navigation.event_new_mouse_move", since = "1.22")] public static Gst.Event navigation_event_new_mouse_move (double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_mouse_scroll")] [Version (replacement = "Navigation.event_new_mouse_scroll", since = "1.22")] public static Gst.Event navigation_event_new_mouse_scroll (double x, double y, double delta_x, double delta_y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_cancel")] [Version (replacement = "Navigation.event_new_touch_cancel", since = "1.22")] public static Gst.Event navigation_event_new_touch_cancel (Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_down")] [Version (replacement = "Navigation.event_new_touch_down", since = "1.22")] public static Gst.Event navigation_event_new_touch_down (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_frame")] [Version (replacement = "Navigation.event_new_touch_frame", since = "1.22")] public static Gst.Event navigation_event_new_touch_frame (Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_motion")] [Version (replacement = "Navigation.event_new_touch_motion", since = "1.22")] public static Gst.Event navigation_event_new_touch_motion (uint identifier, double x, double y, double pressure, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_new_touch_up")] [Version (replacement = "Navigation.event_new_touch_up", since = "1.22")] public static Gst.Event navigation_event_new_touch_up (uint identifier, double x, double y, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_command")] [Version (replacement = "Navigation.event_parse_command")] public static bool navigation_event_parse_command (Gst.Event event, out Gst.Video.NavigationCommand command); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_key_event")] [Version (replacement = "Navigation.event_parse_key_event")] public static bool navigation_event_parse_key_event (Gst.Event event, out unowned string key); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_modifier_state")] [Version (replacement = "Navigation.event_parse_modifier_state", since = "1.22")] public static bool navigation_event_parse_modifier_state (Gst.Event event, Gst.Video.NavigationModifierType state); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_button_event")] [Version (replacement = "Navigation.event_parse_mouse_button_event")] public static bool navigation_event_parse_mouse_button_event (Gst.Event event, out int button, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_move_event")] [Version (replacement = "Navigation.event_parse_mouse_move_event")] public static bool navigation_event_parse_mouse_move_event (Gst.Event event, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_mouse_scroll_event")] [Version (replacement = "Navigation.event_parse_mouse_scroll_event", since = "1.18")] public static bool navigation_event_parse_mouse_scroll_event (Gst.Event event, out double x, out double y, out double delta_x, out double delta_y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_touch_event")] [Version (replacement = "Navigation.event_parse_touch_event", since = "1.22")] public static bool navigation_event_parse_touch_event (Gst.Event event, out uint identifier, out double x, out double y, out double pressure); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_parse_touch_up_event")] [Version (replacement = "Navigation.event_parse_touch_up_event", since = "1.22")] public static bool navigation_event_parse_touch_up_event (Gst.Event event, out uint identifier, out double x, out double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_event_set_coordinates")] [Version (replacement = "Navigation.event_set_coordinates", since = "1.22")] public static bool navigation_event_set_coordinates (Gst.Event event, double x, double y); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_get_type")] [Version (replacement = "Navigation.message_get_type")] public static Gst.Video.NavigationMessageType navigation_message_get_type (Gst.Message message); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_angles_changed")] [Version (replacement = "Navigation.message_new_angles_changed")] public static Gst.Message navigation_message_new_angles_changed (Gst.Object src, uint cur_angle, uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_commands_changed")] [Version (replacement = "Navigation.message_new_commands_changed")] public static Gst.Message navigation_message_new_commands_changed (Gst.Object src); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_event")] [Version (replacement = "Navigation.message_new_event", since = "1.6")] public static Gst.Message navigation_message_new_event (Gst.Object src, Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_new_mouse_over")] [Version (replacement = "Navigation.message_new_mouse_over")] public static Gst.Message navigation_message_new_mouse_over (Gst.Object src, bool active); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_angles_changed")] [Version (replacement = "Navigation.message_parse_angles_changed")] public static bool navigation_message_parse_angles_changed (Gst.Message message, out uint cur_angle, out uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_event")] [Version (replacement = "Navigation.message_parse_event", since = "1.6")] public static bool navigation_message_parse_event (Gst.Message message, out Gst.Event event); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_message_parse_mouse_over")] [Version (replacement = "Navigation.message_parse_mouse_over")] public static bool navigation_message_parse_mouse_over (Gst.Message message, out bool active); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_get_type")] [Version (replacement = "Navigation.query_get_type")] public static Gst.Video.NavigationQueryType navigation_query_get_type (Gst.Query query); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_new_angles")] [Version (replacement = "Navigation.query_new_angles")] public static Gst.Query navigation_query_new_angles (); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_new_commands")] [Version (replacement = "Navigation.query_new_commands")] public static Gst.Query navigation_query_new_commands (); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_angles")] [Version (replacement = "Navigation.query_parse_angles")] public static bool navigation_query_parse_angles (Gst.Query query, out uint cur_angle, out uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_commands_length")] [Version (replacement = "Navigation.query_parse_commands_length")] public static bool navigation_query_parse_commands_length (Gst.Query query, out uint n_cmds); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_parse_commands_nth")] [Version (replacement = "Navigation.query_parse_commands_nth")] public static bool navigation_query_parse_commands_nth (Gst.Query query, uint nth, out Gst.Video.NavigationCommand cmd); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_set_angles")] [Version (replacement = "Navigation.query_set_angles")] public static void navigation_query_set_angles (Gst.Query query, uint cur_angle, uint n_angles); [CCode (cheader_filename = "gst/video/video.h", cname = "gst_navigation_query_set_commandsv")] [Version (replacement = "Navigation.query_set_commandsv")] public static void navigation_query_set_commandsv (Gst.Query query, [CCode (array_length_cname = "n_cmds", array_length_pos = 1.5)] Gst.Video.NavigationCommand[] cmds); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOrientation.from_tag", since = "1.20")] public static bool orientation_from_tag (Gst.TagList taglist, out Gst.Video.OrientationMethod method); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type overlay_composition_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlayCompositionMeta.get_info")] public static unowned Gst.MetaInfo? overlay_composition_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlay.install_properties", since = "1.14")] public static void overlay_install_properties (GLib.ObjectClass oclass, int last_prop_id); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoOverlay.set_property", since = "1.14")] public static bool overlay_set_property (GLib.Object object, int last_prop_id, uint property_id, GLib.Value value); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type region_of_interest_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoRegionOfInterestMeta.get_info")] public static unowned Gst.MetaInfo? region_of_interest_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static GLib.Type sei_user_data_unregistered_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoSEIUserDataUnregisteredMeta.get_info", since = "1.22")] public static unowned Gst.MetaInfo? sei_user_data_unregistered_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.22")] public static bool sei_user_data_unregistered_parse_precision_time_stamp (Gst.Video.SEIUserDataUnregisteredMeta user_data, out uint8 status, out uint64 precision_time_stamp); [CCode (cheader_filename = "gst/video/video.h")] [Version (since = "1.4")] public static uint tile_get_index (Gst.Video.TileMode mode, int x, int y, int x_tiles, int y_tiles); [CCode (cheader_filename = "gst/video/video.h")] public static GLib.Type time_code_meta_api_get_type (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTimeCodeMeta.get_info")] public static unowned Gst.MetaInfo? time_code_meta_get_info (); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.decode", since = "1.20")] public static double transfer_function_decode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.encode", since = "1.20")] public static double transfer_function_encode (Gst.Video.TransferFunction func, double val); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.from_iso", since = "1.18")] public static Gst.Video.TransferFunction transfer_function_from_iso (uint value); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.is_equivalent", since = "1.18")] public static bool transfer_function_is_equivalent (Gst.Video.TransferFunction from_func, uint from_bpp, Gst.Video.TransferFunction to_func, uint to_bpp); [CCode (cheader_filename = "gst/video/video.h")] [Version (replacement = "VideoTransferFunction.to_iso", since = "1.18")] public static uint transfer_function_to_iso (Gst.Video.TransferFunction func); } } dino-0.5.0/plugins/rtp/vapi/webrtc-audio-processing.vapi0000664000000000000000000000000014776241610022010 0ustar rootrootdino-0.5.0/qlite/0000775000000000000000000000000014776241610012273 5ustar rootrootdino-0.5.0/qlite/meson.build0000664000000000000000000000166614776241610014446 0ustar rootrootdependencies = [ dep_gee, dep_glib, dep_sqlite3, ] sources = files( 'src/column.vala', 'src/database.vala', 'src/delete_builder.vala', 'src/insert_builder.vala', 'src/query_builder.vala', 'src/row.vala', 'src/statement_builder.vala', 'src/table.vala', 'src/update_builder.vala', 'src/upsert_builder.vala', ) c_args = [ '-DG_LOG_DOMAIN="qlite"', ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] lib_qlite = library('qlite', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.1', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath) dep_qlite = declare_dependency(link_with: lib_qlite, include_directories: include_directories('.')) install_data('qlite.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 dino-0.5.0/qlite/qlite.deps0000664000000000000000000000003114776241610014260 0ustar rootrootgee-0.8 glib-2.0 sqlite3 dino-0.5.0/qlite/src/0000775000000000000000000000000014776241610013062 5ustar rootrootdino-0.5.0/qlite/src/column.vala0000664000000000000000000001413514776241610015230 0ustar rootrootusing Sqlite; namespace Qlite { public abstract class Column { public const string DEFAULT_TABLE_NAME = ""; public string name { get; private set; } public string? default { get; set; } public int sqlite_type { get; private set; } public bool primary_key { get; set; } public bool auto_increment { get; set; } public bool unique { get; set; } public virtual bool not_null { get; set; } public long min_version { get; set; default = -1; } public long max_version { get; set; default = long.MAX; } internal Table table { get; set; } public abstract T get(Row row, string? table_name = DEFAULT_TABLE_NAME); public virtual bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return false; } internal abstract void bind(Statement stmt, int index, T value); public string to_string() { return table == null ? name : (table.name + "." + name); } public string to_column_definition() { string res = name; switch (sqlite_type) { case INTEGER: res += " INTEGER"; break; case FLOAT: res += " REAL"; break; case TEXT: res += " TEXT"; break; default: res += " UNKNOWN"; break; } if (primary_key) { res += " PRIMARY KEY"; if (auto_increment) res += " AUTOINCREMENT"; } if (not_null) res += " NOT NULL"; if (unique) res += " UNIQUE"; if (default != null) res += @" DEFAULT $((!) default)"; return res; } Column(string name, int type) { this.name = name; this.sqlite_type = type; } public class Integer : Column { public Integer(string name) { base(name, INTEGER); } public override int get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (int) row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, int value) { stmt.bind_int(index, value); } } public class Long : Column { public Long(string name) { base(name, INTEGER); } public override long get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (long) row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, long value) { stmt.bind_int64(index, value); } } public class NullableReal : Column { public NullableReal(string name) { base(name, FLOAT); } public override bool not_null { get { return false; } set {} } public override double? get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_real(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return !row.has_real(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } internal override void bind(Statement stmt, int index, double? value) { stmt.bind_double(index, value); } } public class Text : Column { public Text(string name) { base(name, TEXT); } public override string? get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return get(row, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == null; } internal override void bind(Statement stmt, int index, string? value) { if (value != null) { stmt.bind_text(index, (!) value); } else { stmt.bind_null(index); } } } public class NonNullText : Column { public NonNullText(string name) { base(name, TEXT); } public override bool not_null { get { return true; } set {} } public override string get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return (!)row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name); } public override bool is_null(Row row, string? table_name = DEFAULT_TABLE_NAME) { return false; } internal override void bind(Statement stmt, int index, string value) { stmt.bind_text(index, (!) value); } } public class BoolText : Column { public BoolText(string name) { base(name, TEXT); } public override bool get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_text(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == "1"; } internal override void bind(Statement stmt, int index, bool value) { stmt.bind_text(index, value ? "1" : "0"); } } public class BoolInt : Column { public BoolInt(string name) { base(name, INTEGER); } public override bool get(Row row, string? table_name = DEFAULT_TABLE_NAME) { return row.get_integer(name, table_name == DEFAULT_TABLE_NAME ? table.name : table_name) == 1; } internal override void bind(Statement stmt, int index, bool value) { stmt.bind_int(index, value ? 1 : 0); } } } } dino-0.5.0/qlite/src/database.vala0000664000000000000000000001204614776241610015476 0ustar rootrootusing Sqlite; namespace Qlite { public class Database { private string file_name; private Sqlite.Database db; private long expected_version; private Table[]? tables; private Column meta_name = new Column.Text("name") { primary_key = true }; private Column meta_int_val = new Column.Long("int_val"); private Column meta_text_val = new Column.Text("text_val"); private Table meta_table; public bool debug = false; public Database(string file_name, long expected_version) { this.file_name = file_name; this.expected_version = expected_version; meta_table = new Table(this, "_meta"); meta_table.init({meta_name, meta_int_val, meta_text_val}); } public void init(Table[] tables) { Sqlite.config(Config.SERIALIZED); int ec = Sqlite.Database.open_v2(file_name, out db, OPEN_READWRITE | OPEN_CREATE | 0x00010000); if (ec != Sqlite.OK) { error(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } this.tables = tables; if (debug) db.trace((message) => print(@"Qlite trace: $message\n")); start_migration(); } public void ensure_init() { if (tables == null) error(@"Database $file_name was not initialized, call init()"); } private void start_migration() { try { exec("BEGIN TRANSACTION"); } catch (Error e) { error("SQLite error: %d - %s", db.errcode(), db.errmsg()); } meta_table.create_table_at_version(expected_version); long old_version = 0; old_version = meta_table.row_with(meta_name, "version")[meta_int_val, -1]; if (old_version == -1) { foreach (Table t in tables) { t.create_table_at_version(expected_version); } meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform(); } else if (expected_version != old_version) { foreach (Table t in tables) { t.create_table_at_version(old_version); } foreach (Table t in tables) { t.add_columns_for_version(old_version, expected_version); } migrate(old_version); foreach (Table t in tables) { t.delete_columns_for_version(old_version, expected_version); } if (old_version == -1) { meta_table.insert().value(meta_name, "version").value(meta_int_val, expected_version).perform(); } else { meta_table.update().with(meta_name, "=", "version").set(meta_int_val, expected_version).perform(); } } foreach (Table t in tables) { t.post(); } try { exec("END TRANSACTION"); } catch (Error e) { error("SQLite error: %d - %s", db.errcode(), db.errmsg()); } } internal int errcode() { return db.errcode(); } internal string errmsg() { return db.errmsg(); } internal int64 last_insert_rowid() { return db.last_insert_rowid(); } // To be implemented by actual implementation if required // new table columns are added, outdated columns are still present and will be removed afterwards public virtual void migrate(long old_version) { } public QueryBuilder select(Column[]? columns = null) { ensure_init(); return new QueryBuilder(this).select(columns); } internal MatchQueryBuilder match_query(Table table) { ensure_init(); return new MatchQueryBuilder(this, table); } public InsertBuilder insert() { ensure_init(); return new InsertBuilder(this); } public UpdateBuilder update(Table table) { ensure_init(); return new UpdateBuilder(this, table); } public UpsertBuilder upsert(Table table) { ensure_init(); return new UpsertBuilder(this, table); } public UpdateBuilder update_named(string table) { ensure_init(); return new UpdateBuilder.for_name(this, table); } public DeleteBuilder delete() { ensure_init(); return new DeleteBuilder(this); } public RowIterator query_sql(string sql, string[]? args = null) { ensure_init(); return new RowIterator(this, sql, args); } internal Statement prepare(string sql) { ensure_init(); Sqlite.Statement statement; if (db.prepare_v2(sql, sql.length, out statement) != OK) { error("SQLite error: %d - %s: %s", db.errcode(), db.errmsg(), sql); } return statement; } public void exec(string sql) throws Error { ensure_init(); if (db.exec(sql) != OK) { throw new Error(-1, 0, "SQLite error: %d - %s", db.errcode(), db.errmsg()); } } public bool is_known_column(string table, string field) { ensure_init(); foreach (Table t in tables) { if (t.is_known_column(field)) return true; } return false; } } } dino-0.5.0/qlite/src/delete_builder.vala0000664000000000000000000000332014776241610016675 0ustar rootrootusing Sqlite; namespace Qlite { public class DeleteBuilder : StatementBuilder { // DELETE FROM [...] private Table? table; private string table_name; // WHERE [...] private string selection = "1"; private StatementBuilder.AbstractField[] selection_args = {}; internal DeleteBuilder(Database db) { base(db); } public DeleteBuilder from(Table table) { if (this.table != null) error("Qlite Error: ILLEGAL QUERY: cannot use from() multiple times."); this.table = table; this.table_name = table.name; return this; } public DeleteBuilder from_name(string table) { this.table_name = table; return this; } public DeleteBuilder where(string selection, string[]? selection_args = null) { if (this.selection != "1") error("selection was already done, but where() was called."); this.selection = selection; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public DeleteBuilder with(Column column, string comp, T value) { selection_args += new Field(column, value); selection = @"($selection) AND $(column.name) $comp ?"; return this; } internal override Statement prepare() { Statement stmt = db.prepare(@"DELETE FROM $table_name WHERE $selection"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } return stmt; } public void perform() { if (prepare().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } } } } dino-0.5.0/qlite/src/insert_builder.vala0000664000000000000000000000415214776241610016743 0ustar rootrootusing Sqlite; namespace Qlite { public class InsertBuilder : StatementBuilder { // INSERT [OR ...] private bool replace_val; private string? or_val; // INTO [...] private Table table; private string table_name; // VALUES [...] private StatementBuilder.AbstractField[] fields = {}; internal InsertBuilder(Database db) { base(db); } public InsertBuilder replace() { this.replace_val = true; return this; } public InsertBuilder or(string or) { this.or_val = or; return this; } public InsertBuilder into(Table table) { this.table = table; this.table_name = table.name; return this; } public InsertBuilder into_name(string table) { this.table_name = table; return this; } public InsertBuilder value(Column column, T value) { fields += new Field(column, value); return this; } public InsertBuilder value_null(Column column) { if (column.not_null) error("Qlite Error: ILLEGAL QUERY: Can't set non-null column %s to null", column.name); fields += new NullField(column); return this; } internal override Statement prepare() { string fields_text = ""; string value_qs = ""; for (int i = 0; i < fields.length; i++) { if (i != 0) { value_qs += ", "; fields_text += ", "; } fields_text += ((!)fields[i].column).name; value_qs += "?"; } string sql = replace_val ? "REPLACE" : "INSERT"; if (!replace_val && or_val != null) sql += @" OR $((!)or_val)"; sql += @" INTO $table_name ( $fields_text ) VALUES ($value_qs)"; Statement stmt = db.prepare(sql); for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i+1); } return stmt; } public int64 perform() { if (prepare().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } return db.last_insert_rowid(); } } } dino-0.5.0/qlite/src/query_builder.vala0000664000000000000000000001632714776241610016613 0ustar rootrootusing Sqlite; namespace Qlite { public class QueryBuilder : StatementBuilder { private bool single_result; // SELECT [...] private string column_selector = "*"; private Column[] columns = {}; // FROM [...] protected Table? table; protected string? table_name; // JOIN [...] private string joins = ""; // WHERE [...] protected string selection = "1"; internal StatementBuilder.AbstractField[] selection_args = {}; // ORDER BY [...] private OrderingTerm[]? order_by_terms = {}; // GROUP BY [...] private string? group_by_term; // LIMIT [...] OFFSET [...] private int limit_val; private int offset_val; internal QueryBuilder(Database db) { base(db); } public QueryBuilder select(Column[] columns = {}) { this.columns = columns; if (columns.length != 0) { for (int i = 0; i < columns.length; i++) { if (column_selector == "*") { column_selector = columns[i].to_string(); } else { column_selector += ", " + columns[i].to_string(); } } } else { column_selector = "*"; } return this; } public QueryBuilder select_string(string column_selector) { this.columns = {}; this.column_selector = column_selector; return this; } public virtual QueryBuilder from(Table table) { if (this.table_name != null) error("cannot use from() multiple times."); this.table = table; this.table_name = table.name; return this; } public virtual QueryBuilder from_name(string table) { this.table_name = table; return this; } public QueryBuilder outer_join_with(Table table, Column lhs, Column rhs, string? as = null) { return outer_join_on(table, @"$lhs = $rhs", as); } public QueryBuilder outer_join_on(Table table, string on, string? as = null) { if (as == null) as = table.name; joins += @" LEFT OUTER JOIN $(table.name) AS $as ON $on"; return this; } public QueryBuilder join_with(Table table, Column lhs, Column rhs, string? as = null) { return join_on(table, @"$lhs = $rhs", as); } public QueryBuilder join_on(Table table, string on, string? as = null) { if (as == null) as = table.name; joins += @" JOIN $(table.name) AS $as ON $on"; return this; } internal QueryBuilder join_name(string table_name, string on) { joins += @" JOIN $table_name ON $on"; return this; } public QueryBuilder where(string selection, string[] selection_args = {}) { this.selection = @"($(this.selection)) AND ($selection)"; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public QueryBuilder with(Column column, string comp, T value) { if ((column.unique || column.primary_key) && comp == "=") single_result = true; selection_args += new Field(column, value); selection = @"($selection) AND $column $comp ?"; return this; } public QueryBuilder with_null(Column column) { selection = @"($selection) AND $column ISNULL"; return this; } public QueryBuilder without_null(Column column) { selection = @"($selection) AND $column NOT NULL"; return this; } public QueryBuilder order_by(Column column, string dir = "ASC") { order_by_terms += new OrderingTerm(column, dir); return this; } public QueryBuilder order_by_name(string name, string dir) { order_by_terms += new OrderingTerm.by_name(name, dir); return this; } public QueryBuilder group_by(Column[] columns) { foreach(Column col in columns) { if (group_by_term == null) { group_by_term = col.to_string(); } else { group_by_term += @", $col"; } } return this; } public QueryBuilder limit(int limit) { if (this.limit_val != 0 && limit > this.limit_val) error("tried to increase an existing limit"); this.limit_val = limit; return this; } public QueryBuilder offset(int offset) { if (this.limit_val == 0) error("limit required before offset"); this.offset_val = offset; return this; } public QueryBuilder single() { this.single_result = true; return limit(1); } public int64 count() { this.column_selector = @"COUNT($column_selector) AS count"; this.single_result = true; return row().get_integer("count"); } private Row? row_() { if (!single_result) error("query is not suited to return a single row, but row() was called."); return iterator().get_next(); } public RowOption row() { return new RowOption(row_()); } public T get(Column field, T def = null) { return row().get(field, def); } internal override Statement prepare() { Statement stmt = db.prepare(@"SELECT $column_selector $(table_name == null ? "" : @"FROM $((!) table_name)") $joins WHERE $selection $(group_by_term == null ? "" : @"GROUP BY $group_by_term") $(OrderingTerm.all_to_string(order_by_terms)) $(limit_val > 0 ? @" LIMIT $limit_val OFFSET $offset_val" : "")"); for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i+1); } return stmt; } public RowIterator iterator() { return new RowIterator.from_query_builder(db, this); } class OrderingTerm { Column? column; string column_name; string dir; public OrderingTerm(Column column, string dir) { this.column = column; this.column_name = column.to_string(); this.dir = dir; } public OrderingTerm.by_name(string column_name, string dir) { this.column_name = column_name; this.dir = dir; } public string to_string() { return @"$column_name $dir"; } public static string all_to_string(OrderingTerm[]? terms) { if (terms == null || terms.length == 0) return ""; string res = "ORDER BY "+terms[0].to_string(); for (int i = 1; i < terms.length; i++) { res += @", $(terms[i])"; } return res; } } } public class MatchQueryBuilder : QueryBuilder { internal MatchQueryBuilder(Database db, Table table) { base(db); if (table.fts_columns == null) error("MATCH query on non FTS table"); from(table); join_name(@"_fts_$table_name", @"_fts_$table_name.docid = $table_name.rowid"); } public MatchQueryBuilder match(Column column, string match) { if (table == null) error("MATCH must occur after FROM statement"); if (!(column in table.fts_columns)) error("MATCH selection on non FTS column"); selection_args += new StatementBuilder.StringField(match); selection = @"($selection) AND _fts_$table_name.$(column.name) MATCH ?"; return this; } } } dino-0.5.0/qlite/src/row.vala0000664000000000000000000001007014776241610014534 0ustar rootrootusing Gee; using Sqlite; namespace Qlite { public class Row { private Map text_map = new HashMap(); private Map int_map = new HashMap(); private Map real_map = new HashMap(); internal Row(Statement stmt) { for (int i = 0; i < stmt.column_count(); i++) { string column_name; if (stmt.column_origin_name(i) != null) { column_name = @"$(stmt.column_table_name(i)).$(stmt.column_origin_name(i))"; } else { column_name = stmt.column_name(i); } switch(stmt.column_type(i)) { case TEXT: text_map[column_name] = stmt.column_text(i); break; case INTEGER: int_map[column_name] = (long) stmt.column_int64(i); break; case FLOAT: real_map[column_name] = stmt.column_double(i); break; } } } public T get(Column field) { return field[this]; } private string field_name(string field, string? table) { if (table != null) { return @"$table.$field"; } else { return field; } } public string? get_text(string field, string? table = null) { if (text_map.has_key(field_name(field, table))) { return text_map[field_name(field, table)]; } return null; } public long get_integer(string field, string? table = null) { return int_map[field_name(field, table)]; } public bool has_integer(string field, string? table = null) { return int_map.has_key(field_name(field, table)); } public double get_real(string field, string? table = null, double def = 0) { return real_map[field_name(field, table)] ?? def; } public bool has_real(string field, string? table = null) { return real_map.has_key(field_name(field, table)) && real_map[field_name(field, table)] != null; } public string to_string() { string ret = "{"; foreach (string key in text_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: \"$(text_map[key])\""; } foreach (string key in int_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: $(int_map[key])"; } foreach (string key in real_map.keys) { if (ret.length > 1) ret += ", "; ret = @"$ret$key: $(real_map[key])"; } return ret + "}"; } } public class RowIterator { private Database db; private Statement stmt; public RowIterator.from_query_builder(Database db, QueryBuilder query) { this.db = db; this.stmt = query.prepare(); } public RowIterator(Database db, string sql, string[]? args = null) { this.db = db; this.stmt = db.prepare(sql); if (args != null) { for (int i = 0; i < args.length; i++) { stmt.bind_text(i, sql, sql.length); } } } public bool next() { int r = stmt.step(); if (r == Sqlite.ROW) return true; if (r == Sqlite.DONE) return false; warning(@"SQLite error: $(db.errcode()) - $(db.errmsg())"); return false; } public Row get() { return new Row(stmt); } public Row? get_next() { if (next()) return get(); return null; } } public class RowOption { public Row? inner { get; private set; } public RowOption(Row? row) { this.inner = row; } public bool is_present() { return inner != null; } public T get(Column field, T def = null) { if (inner == null || field.is_null((!)inner)) return def; return field[(!)inner]; } internal long get_integer(string field, long def = 0) { if (inner == null || !((!)inner).has_integer(field)) return def; return ((!)inner).get_integer(field); } } } dino-0.5.0/qlite/src/statement_builder.vala0000664000000000000000000000245314776241610017445 0ustar rootrootusing Sqlite; namespace Qlite { public abstract class StatementBuilder { protected Database db; internal StatementBuilder(Database db) { this.db = db; } internal abstract Statement prepare(); internal abstract class AbstractField { public T value; public Column? column; AbstractField(T value) { this.value = value; } internal abstract void bind(Statement stmt, int index); } internal class Field : AbstractField { public Field(Column column, T value) { base(value); this.column = column; } internal override void bind(Statement stmt, int index) { ((!)column).bind(stmt, index, value); } } internal class NullField : AbstractField { public NullField(Column column) { base(null); this.column = column; } internal override void bind(Statement stmt, int index) { stmt.bind_null(index); } } internal class StringField : AbstractField { public StringField(string value) { base(value); } internal override void bind(Statement stmt, int index) { stmt.bind_text(index, value); } } } } dino-0.5.0/qlite/src/table.vala0000664000000000000000000001646414776241610015031 0ustar rootrootusing Sqlite; namespace Qlite { public class Table { protected Database db; public string name { get; private set; } protected Column[]? columns; private string constraints = ""; private string[] post_statements = {}; private string[] create_statements = {}; internal Column[]? fts_columns; public Table(Database db, string name) { this.db = db; this.name = name; } public void init(Column[] columns, string constraints = "") { this.columns = columns; this.constraints = constraints; foreach(Column c in columns) { c.table = this; } } public void fts(Column[] columns) { if (fts_columns != null) error("Only one FTS index may be used per table."); fts_columns = columns; string cs = ""; string cnames = ""; string cnews = ""; foreach (Column c in columns) { cs += @", $(c.to_column_definition())"; cnames += @", $(c.name)"; cnews += @", new.$(c.name)"; } add_create_statement(@"CREATE VIRTUAL TABLE IF NOT EXISTS _fts_$name USING fts4(tokenize=unicode61, content=\"$name\"$cs)"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bu_$(name) BEFORE UPDATE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_bd_$(name) BEFORE DELETE ON $name BEGIN DELETE FROM _fts_$name WHERE docid=old.rowid; END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_au_$(name) AFTER UPDATE ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); add_post_statement(@"CREATE TRIGGER IF NOT EXISTS _fts_ai_$(name) AFTER INSERT ON $name BEGIN INSERT INTO _fts_$name(docid$cnames) VALUES(new.rowid$cnews); END"); } public void fts_rebuild() { if (fts_columns == null) error("FTS not available on this table."); try { db.exec(@"INSERT INTO _fts_$name(_fts_$name) VALUES('rebuild');"); } catch (Error e) { critical(@"Qlite Error: Rebuilding FTS index: $(e.message)"); } } public void unique(Column[] columns, string? on_conflict = null) { constraints += ", UNIQUE ("; bool first = true; foreach (Column c in columns) { if (!first) constraints += ", "; constraints += c.name; first = false; } constraints += ")"; if (on_conflict != null) { constraints += " ON CONFLICT " + (!)on_conflict; } } public void add_post_statement(string stmt) { post_statements += stmt; } public void add_create_statement(string stmt) { create_statements += stmt; } public void index(string index_name, Column[] columns, bool unique = false) { string stmt = @"CREATE $(unique ? "UNIQUE" : "") INDEX IF NOT EXISTS $index_name ON $name ("; bool first = true; foreach (Column c in columns) { if (!first) stmt += ", "; stmt += c.name; first = false; } stmt += ")"; add_post_statement(stmt); } private void ensure_init() { if (columns == null) error("Table %s was not initialized, call init()", name); } public QueryBuilder select(Column[]? columns = null) { ensure_init(); return db.select(columns).from(this); } private MatchQueryBuilder match_query() { ensure_init(); return db.match_query(this); } public MatchQueryBuilder match(Column column, string query) { return match_query().match(column, query); } public InsertBuilder insert() { ensure_init(); return db.insert().into(this); } public UpdateBuilder update() { ensure_init(); return db.update(this); } public UpsertBuilder upsert() { ensure_init(); return db.upsert(this); } public DeleteBuilder delete() { ensure_init(); return db.delete().from(this); } public RowOption row_with(Column column, T value) { ensure_init(); if (!column.unique && !column.primary_key) error("%s is not suited to identify a row, but used with row_with()", column.name); return select().with(column, "=", value).row(); } public bool is_known_column(string column) { ensure_init(); foreach (Column c in columns) { if (c.name == column) return true; } return false; } public void create_table_at_version(long version) { ensure_init(); string sql = @"CREATE TABLE IF NOT EXISTS $name ("; bool first = true; for (int i = 0; i < columns.length; i++) { Column c = columns[i]; if (c.min_version <= version && c.max_version >= version) { sql += @"$(!first ? "," : "") $(c.to_column_definition())"; first = false; } } sql += @"$constraints)"; try { db.exec(sql); } catch (Error e) { error(@"Qlite Error: Create table at version: $(e.message)"); } foreach (string stmt in create_statements) { try { db.exec(stmt); } catch (Error e) { error(@"Qlite Error: Create table at version: $(e.message)"); } } } public void add_columns_for_version(long old_version, long new_version) { ensure_init(); foreach (Column c in columns) { if (c.min_version <= new_version && c.max_version >= new_version && c.min_version > old_version) { try { db.exec(@"ALTER TABLE $name ADD COLUMN $(c.to_column_definition())"); } catch (Error e) { critical(@"Qlite Error: Add columns for version: $(e.message)"); } } } } public void delete_columns_for_version(long old_version, long new_version) { bool column_deletion_required = false; string column_list = ""; foreach (Column c in columns) { if (c.min_version <= new_version && c.max_version >= new_version) { if (column_list == "") { column_list = c.name; } else { column_list += ", " + c.name; } } if (!(c.min_version <= new_version && c.max_version >= new_version) && c.min_version <= old_version && c.max_version >= old_version) { column_deletion_required = true; } } if (column_deletion_required) { try { db.exec(@"ALTER TABLE $name RENAME TO _$(name)_$old_version"); create_table_at_version(new_version); db.exec(@"INSERT INTO $name ($column_list) SELECT $column_list FROM _$(name)_$old_version"); db.exec(@"DROP TABLE _$(name)_$old_version"); } catch (Error e) { error(@"Qlite Error: Delete columns for version change: $(e.message)"); } } } internal void post() { foreach (string stmt in post_statements) { try { db.exec(stmt); } catch (Error e) { error(@"Qlite Error: Post: $(e.message)"); } } } } } dino-0.5.0/qlite/src/update_builder.vala0000664000000000000000000000554714776241610016732 0ustar rootrootusing Sqlite; namespace Qlite { public class UpdateBuilder : StatementBuilder { // UPDATE [OR ...] private string? or_val; // [...] private Table? table; private string table_name; // SET [...] private StatementBuilder.AbstractField[] fields = {}; // WHERE [...] private string selection = "1"; private StatementBuilder.AbstractField[] selection_args = {}; internal UpdateBuilder(Database db, Table table) { base(db); this.table = table; this.table_name = table.name; } internal UpdateBuilder.for_name(Database db, string table) { base(db); this.table_name = table; } public UpdateBuilder or(string or) { this.or_val = or; return this; } public UpdateBuilder set(Column column, T value) { fields += new Field(column, value); return this; } public UpdateBuilder set_null(Column column) { if (column.not_null) error("Can't set non-null column %s to null", column.name); fields += new NullField(column); return this; } public UpdateBuilder where(string selection, string[] selection_args = {}) { if (this.selection != "1") error("selection was already done, but where() was called."); this.selection = selection; foreach (string arg in selection_args) { this.selection_args += new StatementBuilder.StringField(arg); } return this; } public UpdateBuilder with(Column column, string comp, T value) { selection_args += new Field(column, value); selection = @"($selection) AND $(column.name) $comp ?"; return this; } public UpdateBuilder with_null(Column column) { selection = @"($selection) AND $(column.name) ISNULL"; return this; } public UpdateBuilder without_null(Column column) { selection = @"($selection) AND $(column.name) NOT NULL"; return this; } internal override Statement prepare() { string sql = "UPDATE"; if (or_val != null) sql += @" OR $((!)or_val)"; sql += @" $table_name SET "; for (int i = 0; i < fields.length; i++) { if (i != 0) { sql += ", "; } sql += @"$(((!)fields[i].column).name) = ?"; } sql += @" WHERE $selection"; Statement stmt = db.prepare(sql); for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i+1); } for (int i = 0; i < selection_args.length; i++) { selection_args[i].bind(stmt, i + fields.length + 1); } return stmt; } public void perform() { if (fields.length == 0) return; if (prepare().step() != DONE) { critical("SQLite error: %d - %s", db.errcode(), db.errmsg()); } } } } dino-0.5.0/qlite/src/upsert_builder.vala0000664000000000000000000000551414776241610016764 0ustar rootrootusing Sqlite; namespace Qlite { public class UpsertBuilder : StatementBuilder { // INTO [...] private Table table; private string table_name; // VALUES [...] private StatementBuilder.AbstractField[] keys = {}; private StatementBuilder.AbstractField[] fields = {}; internal UpsertBuilder(Database db, Table table) { base(db); this.table = table; this.table_name = table.name; } public UpsertBuilder value(Column column, T value, bool key = false) { if (key) { keys += new Field(column, value); } else { fields += new Field(column, value); } return this; } public UpsertBuilder value_null(Column column, bool key = false) { if (column.not_null) error("Can't set non-null column %s to null", column.name); if (key) { keys += new NullField(column); } else { fields += new NullField(column); } return this; } internal override Statement prepare() { error("prepare() not available for upsert."); } internal Statement prepare_upsert() { var unique_fields = new StringBuilder(); var unique_values = new StringBuilder(); var update_fields = new StringBuilder(); var update_values = new StringBuilder(); var update_fields_vals = new StringBuilder(); for (int i = 0; i < keys.length; i++) { if (i != 0) { unique_fields.append(", "); unique_values.append(", "); } unique_fields.append(keys[i].column.name); unique_values.append("?"); } for (int i = 0; i < fields.length; i++) { if (i != 0) { update_fields.append(", "); update_values.append(", "); update_fields_vals.append(", "); } update_fields.append(fields[i].column.name); update_values.append("?"); update_fields_vals.append(fields[i].column.name).append("=excluded.").append(fields[i].column.name); } string sql = @"INSERT INTO $table_name ($(unique_fields.str), $(update_fields.str)) VALUES ($(unique_values.str), $(update_values.str)) " + @"ON CONFLICT ($(unique_fields.str)) DO UPDATE SET $(update_fields_vals.str)"; Statement stmt = db.prepare(sql); for (int i = 0; i < keys.length; i++) { keys[i].bind(stmt, i + 1); } for (int i = 0; i < fields.length; i++) { fields[i].bind(stmt, i + keys.length + 1); } return stmt; } public int64 perform() { if (prepare_upsert().step() != DONE) { critical(@"SQLite error: %d - %s", db.errcode(), db.errmsg()); } return db.last_insert_rowid(); } } } dino-0.5.0/xmpp-vala/0000775000000000000000000000000014776241610013062 5ustar rootrootdino-0.5.0/xmpp-vala/meson.build0000664000000000000000000001571414776241610015234 0ustar rootrootdependencies = [ dep_gdk_pixbuf, dep_gee, dep_gio, dep_glib, dep_icu_uc, dep_m, ] sources = files( 'src/core/direct_tls_xmpp_stream.vala', 'src/core/io_xmpp_stream.vala', 'src/core/module_flag.vala', 'src/core/namespace_state.vala', 'src/core/stanza_attribute.vala', 'src/core/stanza_node.vala', 'src/core/stanza_reader.vala', 'src/core/stanza_writer.vala', 'src/core/starttls_xmpp_stream.vala', 'src/core/stream_connect.vala', 'src/core/tls_xmpp_stream.vala', 'src/core/xmpp_log.vala', 'src/core/xmpp_stream.vala', 'src/glib_fixes.vapi', 'src/module/bind.vala', 'src/module/bookmarks_provider.vala', 'src/module/conference.vala', 'src/module/iq/module.vala', 'src/module/iq/stanza.vala', 'src/module/jid.vala', 'src/module/message/module.vala', 'src/module/message/stanza.vala', 'src/module/presence/flag.vala', 'src/module/presence/module.vala', 'src/module/presence/stanza.vala', 'src/module/roster/flag.vala', 'src/module/roster/item.vala', 'src/module/roster/module.vala', 'src/module/roster/versioning_module.vala', 'src/module/sasl.vala', 'src/module/session.vala', 'src/module/stanza.vala', 'src/module/stanza_error.vala', 'src/module/stream_error.vala', 'src/module/util.vala', 'src/module/xep/0004_data_forms.vala', 'src/module/xep/0030_service_discovery/flag.vala', 'src/module/xep/0030_service_discovery/identity.vala', 'src/module/xep/0030_service_discovery/info_result.vala', 'src/module/xep/0030_service_discovery/item.vala', 'src/module/xep/0030_service_discovery/items_result.vala', 'src/module/xep/0030_service_discovery/module.vala', 'src/module/xep/0045_muc/flag.vala', 'src/module/xep/0045_muc/module.vala', 'src/module/xep/0045_muc/status_code.vala', 'src/module/xep/0047_in_band_bytestreams.vala', 'src/module/xep/0048_bookmarks.vala', 'src/module/xep/0048_conference.vala', 'src/module/xep/0394_message_markup.vala', 'src/module/xep/0049_private_xml_storage.vala', 'src/module/xep/0054_vcard/module.vala', 'src/module/xep/0059_result_set_management.vala', 'src/module/xep/0060_pubsub.vala', 'src/module/xep/0065_socks5_bytestreams.vala', 'src/module/xep/0066_out_of_band_data.vala', 'src/module/xep/0077_in_band_registration.vala', 'src/module/xep/0082_date_time_profiles.vala', 'src/module/xep/0084_user_avatars.vala', 'src/module/xep/0085_chat_state_notifications.vala', 'src/module/xep/0104_http_scheme_url_data.vala', 'src/module/xep/0115_entity_capabilities.vala', 'src/module/xep/0166_jingle/component.vala', 'src/module/xep/0166_jingle/content.vala', 'src/module/xep/0166_jingle/content_description.vala', 'src/module/xep/0166_jingle/content_node.vala', 'src/module/xep/0166_jingle/content_security.vala', 'src/module/xep/0166_jingle/content_transport.vala', 'src/module/xep/0166_jingle/jingle_flag.vala', 'src/module/xep/0166_jingle/jingle_module.vala', 'src/module/xep/0166_jingle/jingle_structs.vala', 'src/module/xep/0166_jingle/reason_element.vala', 'src/module/xep/0166_jingle/session.vala', 'src/module/xep/0166_jingle/session_info.vala', 'src/module/xep/0167_jingle_rtp/content_parameters.vala', 'src/module/xep/0167_jingle_rtp/content_type.vala', 'src/module/xep/0167_jingle_rtp/jingle_rtp_module.vala', 'src/module/xep/0167_jingle_rtp/payload_type.vala', 'src/module/xep/0167_jingle_rtp/session_info_type.vala', 'src/module/xep/0167_jingle_rtp/stream.vala', 'src/module/xep/0176_jingle_ice_udp/candidate.vala', 'src/module/xep/0176_jingle_ice_udp/jingle_ice_udp_module.vala', 'src/module/xep/0176_jingle_ice_udp/transport_parameters.vala', 'src/module/xep/0177_jingle_raw_udp.vala', 'src/module/xep/0184_message_delivery_receipts.vala', 'src/module/xep/0191_blocking_command.vala', 'src/module/xep/0198_stream_management.vala', 'src/module/xep/0199_ping.vala', 'src/module/xep/0203_delayed_delivery.vala', 'src/module/xep/0215_external_service_discovery.vala', 'src/module/xep/0234_jingle_file_transfer.vala', 'src/module/xep/0249_direct_muc_invitations.vala', 'src/module/xep/0260_jingle_socks5_bytestreams.vala', 'src/module/xep/0261_jingle_in_band_bytestreams.vala', 'src/module/xep/0264_jingle_content_thumbnails.vala', 'src/module/xep/0272_muji.vala', 'src/module/xep/0280_message_carbons.vala', 'src/module/xep/0297_stanza_forwarding.vala', 'src/module/xep/0298_coin.vala', 'src/module/xep/0300_cryptographic_hashes.vala', 'src/module/xep/0308_last_message_correction.vala', 'src/module/xep/0313_2_message_archive_management.vala', 'src/module/xep/0313_message_archive_management.vala', 'src/module/xep/0333_chat_markers.vala', 'src/module/xep/0334_message_processing_hints.vala', 'src/module/xep/0353_jingle_message_initiation.vala', 'src/module/xep/0359_unique_stable_stanza_ids.vala', 'src/module/xep/0363_http_file_upload.vala', 'src/module/xep/0367_message_attaching.vala', 'src/module/xep/0380_explicit_encryption.vala', 'src/module/xep/0384_omemo/omemo_decryptor.vala', 'src/module/xep/0384_omemo/omemo_encryptor.vala', 'src/module/xep/0391_jingle_encrypted_transports.vala', 'src/module/xep/0392_consistent_color/consistent_color.vala', 'src/module/xep/0392_consistent_color/hsluv.vala', 'src/module/xep/0402_bookmarks2.vala', 'src/module/xep/0410_muc_self_ping.vala', 'src/module/xep/0421_occupant_ids.vala', 'src/module/xep/0428_fallback_indication.vala', 'src/module/xep/0444_reactions.vala', 'src/module/xep/0446_file_metadata_element.vala', 'src/module/xep/0447_stateless_file_sharing.vala', 'src/module/xep/0461_replies.vala', 'src/module/xep/0482_call_invites.vala', 'src/module/xep/pixbuf_storage.vala', 'src/util.vala', ) c_args = [ '-DG_LOG_DOMAIN="xmpp-vala"', ] vala_args = [ '--vapidir', meson.current_source_dir() / 'vapi', ] if meson.get_compiler('vala').version().version_compare('=0.56.11') vala_args += ['-D', 'VALA_0_56_11'] endif lib_xmpp_vala = library('xmpp-vala', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, version: '0.1', install: true, install_dir: [true, true, true], install_rpath: default_install_rpath) dep_xmpp_vala = declare_dependency(link_with: lib_xmpp_vala, include_directories: include_directories('.')) install_data('xmpp-vala.deps', install_dir: get_option('datadir') / 'vala/vapi', install_tag: 'devel') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 test_sources = [ 'tests/common.vala', 'tests/testcase.vala', 'tests/jid.vala', 'tests/stanza.vala', 'tests/color.vala', 'tests/util.vala', ] exe_xmpp_vala_test = executable('xmpp-vala-test', test_sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies + dep_xmpp_vala, install: false) test('Tests for xmpp-vala', exe_xmpp_vala_test) dino-0.5.0/xmpp-vala/src/0000775000000000000000000000000014776241610013651 5ustar rootrootdino-0.5.0/xmpp-vala/src/core/0000775000000000000000000000000014776241610014601 5ustar rootrootdino-0.5.0/xmpp-vala/src/core/direct_tls_xmpp_stream.vala0000664000000000000000000000271714776241610022230 0ustar rootrootpublic class Xmpp.DirectTlsXmppStream : TlsXmppStream { const string[] ADVERTISED_PROTOCOLS = {"xmpp-client", null}; string host; uint16 port; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert; public DirectTlsXmppStream(Jid remote_name, string host, uint16 port, TlsXmppStream.OnInvalidCertWrapper on_invalid_cert) { base(remote_name); this.host = host; this.port = port; this.on_invalid_cert = on_invalid_cert; } public override async void connect() throws IOError { SocketClient client = new SocketClient(); try { debug("Connecting to %s:%i (tls)", host, port); IOStream? io_stream = yield client.connect_to_host_async(host, port, cancellable); TlsConnection tls_connection = TlsClientConnection.new(io_stream, new NetworkAddress(remote_name.to_string(), port)); #if GLIB_2_60 tls_connection.set_advertised_protocols(ADVERTISED_PROTOCOLS); #endif tls_connection.accept_certificate.connect(on_invalid_certificate); tls_connection.accept_certificate.connect((cert, flags) => on_invalid_cert.func(cert, flags)); reset_stream(tls_connection); yield setup(); attach_negotation_modules(); } catch (IOError e) { throw e; } catch (Error e) { throw new IOError.CONNECTION_REFUSED("Failed connecting to %s:%i (tls): %s", host, port, e.message); } } } dino-0.5.0/xmpp-vala/src/core/io_xmpp_stream.vala0000664000000000000000000000732714776241610020505 0ustar rootrootusing Gee; public interface Xmpp.WriteNodeFunc : Object { public abstract async void write_stanza(XmppStream stream, StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError; } public abstract class Xmpp.IoXmppStream : XmppStream { private IOStream? stream; internal Cancellable cancellable; internal StanzaReader? reader; internal StanzaWriter? writer; internal WriteNodeFunc? write_obj = null; protected IoXmppStream(Jid remote_name, Cancellable? cancellable = null) { base(remote_name); this.cancellable = cancellable ?? new Cancellable(); } public void cancel() { cancellable.cancel(); } public override async void disconnect() throws IOError { disconnected = true; cancel(); if (writer == null || reader == null || stream == null) { throw new IOError.CLOSED("trying to disconnect, but no stream open"); } log.str("OUT", "", this); yield writer.write("", Priority.LOW, new Cancellable()); yield stream.close_async(); } public void reset_stream(IOStream stream) { this.stream = stream; reader = new StanzaReader.for_stream(stream.input_stream, cancellable); writer = new StanzaWriter.for_stream(stream.output_stream, cancellable); require_setup(); } public override async StanzaNode read() throws IOError { StanzaReader? reader = this.reader; if (reader == null) throw new IOError.NOT_CONNECTED("trying to read, but no stream open"); StanzaNode node = yield ((!)reader).read_node(); log.node("IN", node, this); return node; } [Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")] public override void write(StanzaNode node, int io_priority = Priority.DEFAULT) { write_async.begin(node, io_priority, null, (obj, res) => { try { write_async.end(res); } catch (Error e) { warning("Error while writing: %s", e.message); } }); } public override async void write_async(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { if (write_obj != null) { yield write_obj.write_stanza(this, node, io_priority, cancellable ?? this.cancellable); } else { StanzaWriter? writer = this.writer; if (writer == null) throw new IOError.NOT_CONNECTED("trying to write, but no stream open"); log.node("OUT", node, this); yield ((!)writer).write_node(node, io_priority, cancellable ?? this.cancellable); } } internal IOStream? get_stream() { return stream; } public override async void setup() throws IOError { StanzaNode outs = new StanzaNode.build("stream", "http://etherx.jabber.org/streams") .put_attribute("to", remote_name.to_string()) .put_attribute("version", "1.0") .put_attribute("xmlns", "jabber:client") .put_attribute("stream", "http://etherx.jabber.org/streams", XMLNS_URI); outs.has_nodes = true; log.node("OUT ROOT", outs, this); yield write_async(outs, Priority.HIGH, cancellable); received_root_node(this, yield read_root()); setup_needed = false; } private async StanzaNode read_root() throws IOError { StanzaReader? reader = this.reader; if (reader == null) throw new IOError.NOT_CONNECTED("trying to read, but no stream open"); StanzaNode node = yield ((!)reader).read_root_node(); log.node("IN ROOT", node, this); return node; } }dino-0.5.0/xmpp-vala/src/core/module_flag.vala0000664000000000000000000000356314776241610017733 0ustar rootrootnamespace Xmpp { public class FlagIdentity : Object { public string ns { get; private set; } public string id { get; private set; } public FlagIdentity(string ns, string id) { this.ns = ns; this.id = id; } public T? cast(XmppStreamFlag flag) { #if VALA_0_56_11 // We can't typecheck due to compiler bug return (T) module; #else return flag.get_type().is_a(typeof(T)) ? (T?) flag : null; #endif } public bool matches(XmppStreamFlag module) { return module.get_ns() == ns && module.get_id() == id; } } public abstract class XmppStreamFlag : Object { public abstract string get_ns(); public abstract string get_id(); } public class ModuleIdentity : Object { public string ns { get; private set; } public string id { get; private set; } public ModuleIdentity(string ns, string id) { this.ns = ns; this.id = id; } public T? cast(XmppStreamModule module) { #if VALA_0_56_11 // We can't typecheck due to compiler bug return (T) module; #else return module.get_type().is_a(typeof(T)) ? (T?) module : null; #endif } public bool matches(XmppStreamModule module) { return module.get_ns() == ns && module.get_id() == id; } } public abstract class XmppStreamModule : Object { public abstract void attach(XmppStream stream); public abstract void detach(XmppStream stream); public abstract string get_ns(); public abstract string get_id(); } public abstract class XmppStreamNegotiationModule : XmppStreamModule { public abstract bool mandatory_outstanding(XmppStream stream); public abstract bool negotiation_active(XmppStream stream); } }dino-0.5.0/xmpp-vala/src/core/namespace_state.vala0000664000000000000000000000477514776241610020617 0ustar rootrootusing Gee; namespace Xmpp { public class NamespaceState { private HashMap uri_to_name = new HashMap (); private HashMap name_to_uri = new HashMap (); public string current_ns_uri; private NamespaceState parent; public NamespaceState() { add_assoc(XMLNS_URI, "xmlns"); add_assoc(XML_URI, "xml"); current_ns_uri = XML_URI; } public NamespaceState.for_stanza() { this(); add_assoc("http://etherx.jabber.org/streams", "stream"); current_ns_uri = "jabber:client"; } private NamespaceState.copy(NamespaceState old) { foreach (string key in old.uri_to_name.keys) { add_assoc(key, old.uri_to_name[key]); } set_current(old.current_ns_uri); } private NamespaceState.with_parent(NamespaceState parent) { this.copy(parent); this.parent = parent; } public NamespaceState.with_assoc(NamespaceState old, string ns_uri, string name) { this.copy(old); add_assoc(ns_uri, name); } public NamespaceState.with_current(NamespaceState old, string current_ns_uri) { this.copy(old); set_current(current_ns_uri); } public void add_assoc(string ns_uri, string name) { name_to_uri[name] = ns_uri; uri_to_name[ns_uri] = name; } public void set_current(string current_ns_uri) { this.current_ns_uri = current_ns_uri; } public string find_name(string ns_uri) throws IOError { if (uri_to_name.has_key(ns_uri)) { return uri_to_name[ns_uri]; } throw new IOError.INVALID_DATA(@"XML: NS URI $ns_uri not found."); } public string find_uri(string name) throws IOError { if (name_to_uri.has_key(name)) { return name_to_uri[name]; } throw new IOError.INVALID_DATA(@"XML: NS name $name not found."); } public NamespaceState push() { return new NamespaceState.with_parent(this); } public NamespaceState pop() { return parent; } public string to_string() { StringBuilder sb = new StringBuilder (); sb.append ("NamespaceState{"); foreach (string key in uri_to_name.keys) { sb.append(key); sb.append_c('='); sb.append(uri_to_name[key]); sb.append_c(','); } sb.append("current="); sb.append(current_ns_uri); sb.append_c('}'); return sb.str; } } } dino-0.5.0/xmpp-vala/src/core/stanza_attribute.vala0000664000000000000000000000530114776241610021030 0ustar rootrootnamespace Xmpp { public class StanzaAttribute : StanzaEntry { internal const string ATTRIBUTE_STRING_FORMAT = "{%s}:%s='%s'"; internal const string ATTRIBUTE_STRING_NO_NS_FORMAT = "%s='%s'"; internal const string ATTRIBUTE_STRING_ANSI_FORMAT = ANSI_COLOR_GRAY+"{%s}:"+ANSI_COLOR_END+"%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT = "%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_XML_FORMAT = "%s:%s='%s'"; internal const string ATTRIBUTE_XML_NO_NS_FORMAT = "%s='%s'"; internal const string ATTRIBUTE_XML_ANSI_FORMAT = "%s:%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal const string ATTRIBUTE_XML_ANSI_NO_NS_FORMAT = "%s="+ANSI_COLOR_GREEN+"'%s'"+ANSI_COLOR_END; internal StanzaAttribute() { } public StanzaAttribute.build(string ns_uri, string name, string val) { this.ns_uri = ns_uri; this.name = name; this.val = val; } public bool equals(StanzaAttribute other) { if (other.ns_uri != ns_uri) return false; if (other.name != name) return false; if (other.val != val) return false; return true; } internal string printf(string fmt, bool no_ns = false, string? ns_name = null) { if (no_ns) { return fmt.printf(name, (!)encoded_val); } else { if (ns_name == null) { return fmt.printf((!)ns_uri, name, (!)encoded_val); } else { return fmt.printf((!)ns_name, name, (!)encoded_val); } } } public override string to_string(int i = 0) { return printf(ATTRIBUTE_STRING_FORMAT); } public string to_ansi_string(bool hide_ns = false) { if (hide_ns) { return printf(ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_STRING_ANSI_FORMAT); } } public string to_xml(NamespaceState? state_ = null) { NamespaceState state = state_ ?? new NamespaceState(); if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { return printf(ATTRIBUTE_XML_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_XML_FORMAT, false, state.find_name((!)ns_uri)); } } public string to_ansi_xml(NamespaceState? state_ = null) { NamespaceState state = state_ ?? new NamespaceState(); if (ns_uri == state.current_ns_uri || (ns_uri == XMLNS_URI && name == "xmlns")) { return printf(ATTRIBUTE_XML_ANSI_NO_NS_FORMAT, true); } else { return printf(ATTRIBUTE_XML_ANSI_FORMAT, false, state.find_name((!)ns_uri)); } } } } dino-0.5.0/xmpp-vala/src/core/stanza_node.vala0000664000000000000000000003745414776241610017770 0ustar rootrootusing Gee; namespace Xmpp { public abstract class StanzaEntry { protected const string ANSI_COLOR_END = "\x1b[0m"; protected const string ANSI_COLOR_GREEN = "\x1b[32m"; protected const string ANSI_COLOR_YELLOW = "\x1b[33m"; protected const string ANSI_COLOR_GRAY = "\x1b[37m"; public string? ns_uri; public string name; public string? val; public string? encoded_val { owned get { if (val == null) return null; return ((!)val).replace("&", "&").replace("\"", """).replace("'", "'").replace("<", "<").replace(">", ">"); } set { if (value == null) { val = null; return; } string tmp = ((!)value).replace(">", ">").replace("<", "<").replace("'","'").replace(""","\""); while (tmp.contains("&#")) { int start = tmp.index_of("&#"); int end = tmp.index_of(";", start); if (end < start) break; unichar num = -1; if (tmp[start+2]=='x') { tmp.substring(start+3, start-end-3).scanf("%x", &num); } else { num = int.parse(tmp.substring(start+2, start-end-2)); } tmp = tmp.splice(start, end, num.to_string()); } val = tmp.replace("&", "&"); } } public virtual unowned string? get_string_content() { return val; } public virtual string to_string(int i = 0) { return get_string_content() ?? "(null)"; } } public class StanzaNode : StanzaEntry { public Gee.List sub_nodes = new ArrayList(); public Gee.List attributes = new ArrayList(); public bool has_nodes = false; public bool pseudo = false; internal StanzaNode() { } public StanzaNode.build(string name, string ns_uri = "jabber:client", ArrayList? nodes = null, ArrayList? attrs = null) { this.ns_uri = ns_uri; this.name = name; if (nodes != null) this.sub_nodes.add_all((!)nodes); if (attrs != null) this.attributes.add_all((!)attrs); } public StanzaNode.text(string text) { this.name = "#text"; this.val = text; } public StanzaNode.encoded_text(string text) { this.name = "#text"; this.encoded_val = text; } public StanzaNode add_self_xmlns() { if (ns_uri == null) return this; return put_attribute("xmlns", (!)ns_uri); } public unowned string? get_attribute(string name, string? ns_uri = null) { string _name = name; string? _ns_uri = ns_uri; if (_ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var attr in attributes) { if (attr.ns_uri == (!)_ns_uri && attr.name == _name) return attr.val; } return null; } public int get_attribute_int(string name, int def = -1, string? ns_uri = null) { string? attribute_str = get_attribute(name, ns_uri); if (attribute_str == null) return def; int res = def; bool parse_success = int.try_parse(attribute_str, out res, null, 10); if (!parse_success) { info("Could not parse int attribute %s: %s", name, attribute_str); return def; } return res; } public uint get_attribute_uint(string name, uint def = 0, string? ns_uri = null) { string? attribute_str = get_attribute(name, ns_uri); if (attribute_str == null || attribute_str.strip().has_prefix("-")) return def; uint res = def; bool parse_success = uint.try_parse(attribute_str, out res, null, 10); if (!parse_success) { info("Could not parse uint attribute %s: %s", name, attribute_str); return def; } return res; } public bool get_attribute_bool(string name, bool def = false, string? ns_uri = null) { string? res = get_attribute(name, ns_uri); if (res == null) return def; return ((!)res).down() == "true" || res == "1"; } public StanzaAttribute? get_attribute_raw(string name, string? ns_uri = null) { string _name = name; string? _ns_uri = ns_uri; if (_ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var attr in attributes) { if (attr.ns_uri == _ns_uri && attr.name == _name) return attr; } return null; } public Gee.List get_attributes_by_ns_uri(string ns_uri) { ArrayList ret = new ArrayList (); foreach (var attr in attributes) { if (attr.ns_uri == ns_uri) ret.add(attr); } return ret; } public unowned string? get_deep_attribute(...) { va_list l = va_list(); StanzaAttribute? res = get_deep_attribute_(va_list.copy(l)); if (res == null) return null; return ((!)res).val; } public StanzaAttribute? get_deep_attribute_(va_list l) { StanzaNode node = this; string? attribute_name = l.arg(); if (attribute_name == null) return null; while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)attribute_name); if (node_tmp == null) return null; node = (!)node_tmp; attribute_name = s; } return node.get_attribute_raw((!)attribute_name); } public StanzaNode? get_subnode(string name, string? ns_uri = null, bool recurse = false) { string _name = name; string? _ns_uri = ns_uri; if (ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var node in sub_nodes) { if (node.ns_uri == _ns_uri && node.name == _name) return node; if (recurse) { var x = node.get_subnode(_name, _ns_uri, recurse); if (x != null) return x; } } return null; } public Gee.List get_subnodes(string name, string? ns_uri = null, bool recurse = false) { ArrayList ret = new ArrayList(); string _name = name; string? _ns_uri = ns_uri; if (ns_uri == null) { if (_name.contains(":")) { var lastIndex = _name.last_index_of_char(':'); _ns_uri = _name.substring(0, lastIndex); _name = _name.substring(lastIndex + 1); } else { _ns_uri = this.ns_uri; } } foreach (var node in sub_nodes) { if (node.ns_uri == _ns_uri && node.name == _name) ret.add(node); if (recurse) { ret.add_all(node.get_subnodes(_name, _ns_uri, recurse)); } } return ret; } public StanzaNode? get_deep_subnode(...) { va_list l = va_list(); return get_deep_subnode_(va_list.copy(l)); } public StanzaNode? get_deep_subnode_(va_list l) { StanzaNode node = this; while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)s); if (node_tmp == null) return null; node = (!)node_tmp; } return node; } public Gee.List get_deep_subnodes(...) { va_list l = va_list(); return get_deep_subnodes_(va_list.copy(l)); } public Gee.List get_deep_subnodes_(va_list l) { StanzaNode node = this; string? subnode_name = l.arg(); if (subnode_name == null) return new ArrayList(); while (true) { string? s = l.arg(); if (s == null) break; StanzaNode? node_tmp = node.get_subnode((!)subnode_name); if (node_tmp == null) return new ArrayList(); node = (!)node_tmp; subnode_name = s; } return node.get_subnodes((!)subnode_name); } public Gee.List get_all_subnodes() { return sub_nodes; } public Gee.List get_deep_all_subnodes(...) { va_list l = va_list(); StanzaNode? node = get_deep_subnode_(va_list.copy(l)); if (node != null) return ((!)node).get_all_subnodes(); return new ArrayList(); } public void add_attribute(StanzaAttribute attr) { attributes.add(attr); } public override unowned string? get_string_content() { if (val != null) return val; if (sub_nodes.size == 1) return sub_nodes[0].get_string_content(); return null; } public unowned string? get_deep_string_content(...) { va_list l = va_list(); StanzaNode? node = get_deep_subnode_(va_list.copy(l)); if (node != null) return ((!)node).get_string_content(); return null; } public StanzaNode put_attribute(string name, string val, string? ns_uri = null) { string? _ns_uri = ns_uri; if (name == "xmlns") _ns_uri = XMLNS_URI; if (_ns_uri == null) _ns_uri = this.ns_uri; if (_ns_uri == null) return this; attributes.add(new StanzaAttribute.build((!)_ns_uri, name, val)); return this; } /** * Set only occurrence **/ public void set_attribute(string name, string val, string? ns_uri = null) { if (ns_uri == null) ns_uri = this.ns_uri; foreach (var attr in attributes) { if (attr.ns_uri == ns_uri && attr.name == name) { attr.val = val; return; } } put_attribute(name, val, ns_uri); } public StanzaNode put_node(StanzaNode node) { sub_nodes.add(node); return this; } public bool equals(StanzaNode other) { if (other.name != name) return false; if (other.val != val) return false; if (name == "#text") return true; if (other.ns_uri != ns_uri) return false; if (other.sub_nodes.size != sub_nodes.size) return false; for (int i = 0; i < sub_nodes.size; i++) { if (!other.sub_nodes[i].equals(sub_nodes[i])) return false; } if (other.attributes.size != attributes.size) return false; for (int i = 0; i < attributes.size; i++) { if (!other.attributes[i].equals(attributes[i])) return false; } return true; } private const string TAG_START_BEGIN_FORMAT = "%s<{%s}:%s"; private const string TAG_START_EMPTY_END = " />\n"; private const string TAG_START_CONTENT_END = ">\n"; private const string TAG_END_FORMAT = "%s\n"; private const string TAG_ANSI_START_BEGIN_FORMAT = "%s"+ANSI_COLOR_YELLOW+"<"+ANSI_COLOR_GRAY+"{%s}:"+ANSI_COLOR_YELLOW+"%s"+ANSI_COLOR_END; private const string TAG_ANSI_START_BEGIN_NO_NS_FORMAT = "%s"+ANSI_COLOR_YELLOW+"<%s"+ANSI_COLOR_END; private const string TAG_ANSI_START_EMPTY_END = ANSI_COLOR_YELLOW+" />"+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_START_CONTENT_END = ANSI_COLOR_YELLOW+">"+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_END_FORMAT = "%s"+ANSI_COLOR_YELLOW+""+ANSI_COLOR_END+"\n"; private const string TAG_ANSI_END_NO_NS_FORMAT = "%s"+ANSI_COLOR_YELLOW+""+ANSI_COLOR_END+"\n"; internal string printf(int i, string fmt_start_begin, string start_empty_end, string start_content_end, string fmt_end, string fmt_attr, bool no_ns = false) { string indent = string.nfill (i * 2, ' '); if (name == "#text") { if (((!)val).length > 1000) { return indent + "[... retracted for brevity ...]\n"; } return indent + ((!)val).replace("\n", "\n" + indent) + "\n"; } var sb = new StringBuilder(); if (no_ns) { sb.append_printf(fmt_start_begin, indent, name); } else { sb.append_printf(fmt_start_begin, indent, (!)ns_uri, name); } foreach (StanzaAttribute attr in attributes) { sb.append_printf(" %s", attr.printf(fmt_attr, no_ns)); } if (!has_nodes && sub_nodes.size == 0) { sb.append(start_empty_end); } else { sb.append(start_content_end); if (sub_nodes.size != 0) { foreach (StanzaNode subnode in sub_nodes) { sb.append(subnode.printf(i+1, fmt_start_begin, start_empty_end, start_content_end, fmt_end, fmt_attr, no_ns)); } if (no_ns) { sb.append_printf(fmt_end, indent, name); } else { sb.append_printf(fmt_end, indent, (!)ns_uri, name); } } } return sb.str; } public override string to_string(int i = 0) { return printf(i, TAG_START_BEGIN_FORMAT, TAG_START_EMPTY_END, TAG_START_CONTENT_END, TAG_END_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_FORMAT); } public string to_ansi_string(bool hide_ns = false, int i = 0) { if (hide_ns) { return printf(i, TAG_ANSI_START_BEGIN_NO_NS_FORMAT, TAG_ANSI_START_EMPTY_END, TAG_ANSI_START_CONTENT_END, TAG_ANSI_END_NO_NS_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_ANSI_NO_NS_FORMAT, true); } else { return printf(i, TAG_ANSI_START_BEGIN_FORMAT, TAG_ANSI_START_EMPTY_END, TAG_ANSI_START_CONTENT_END, TAG_ANSI_END_FORMAT, StanzaAttribute.ATTRIBUTE_STRING_ANSI_FORMAT); } } public string to_xml(NamespaceState? state = null) throws IOError { NamespaceState my_state = state ?? new NamespaceState.for_stanza(); if (name == "#text") return val == null ? "" : (!)encoded_val; my_state = my_state.push(); foreach (var xmlns in get_attributes_by_ns_uri (XMLNS_URI)) { if (xmlns.val == null) continue; if (xmlns.name == "xmlns") { my_state.set_current((!)xmlns.val); } else { my_state.add_assoc((!)xmlns.val, xmlns.name); } } var sb = new StringBuilder(); if (ns_uri == my_state.current_ns_uri) { sb.append_printf("<%s", name); } else { sb.append_printf("<%s:%s", my_state.find_name ((!)ns_uri), name); } var attr_ns_state = new NamespaceState.with_current(my_state, (!)ns_uri); foreach (StanzaAttribute attr in attributes) { sb.append_printf(" %s", attr.to_xml(attr_ns_state)); } if (!has_nodes && sub_nodes.size == 0) { sb.append("/>"); } else { sb.append(">"); if (sub_nodes.size != 0) { foreach (StanzaNode subnode in sub_nodes) { sb.append(subnode.to_xml(my_state)); } if (ns_uri == my_state.current_ns_uri) { sb.append(@""); } else { sb.append_printf("", my_state.find_name ((!)ns_uri), name); } } } my_state = my_state.pop(); return sb.str; } } } dino-0.5.0/xmpp-vala/src/core/stanza_reader.vala0000664000000000000000000002372614776241610020302 0ustar rootrootusing Gee; namespace Xmpp { public const string XMLNS_URI = "http://www.w3.org/2000/xmlns/"; public const string XML_URI = "http://www.w3.org/XML/1998/namespace"; public const string JABBER_URI = "jabber:client"; public class StanzaReader { private static int BUFFER_MAX = 4096; private InputStream? input; private uint8[] buffer; private int buffer_fill = 0; private int buffer_pos = 0; private Cancellable? cancellable; private NamespaceState ns_state = new NamespaceState(); public StanzaReader.for_buffer(uint8[] buffer) { this.buffer = buffer; this.buffer_fill = buffer.length; } public StanzaReader.for_string(string s) { this.for_buffer(s.data); } public StanzaReader.for_stream(InputStream input, Cancellable? cancellable = null) { this.input = input; this.cancellable = cancellable; buffer = new uint8[BUFFER_MAX]; } private async void update_buffer() throws IOError { InputStream? input = this.input; if (input == null) throw new IOError.CLOSED("No input stream specified and end of buffer reached."); if (cancellable != null && cancellable.is_cancelled()) throw new IOError.CANCELLED("Input stream is canceled."); try { buffer_fill = (int) yield ((!)input).read_async(buffer, GLib.Priority.DEFAULT, cancellable); } catch (TlsError.EOF e) { throw new IOError.CLOSED("End of TLS stream reached"); } if (buffer_fill == 0) throw new IOError.CLOSED("End of input stream reached."); buffer_pos = 0; } private async char read_single() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } return (char) buffer[buffer_pos++]; } private async char peek_single() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } return (char) buffer[buffer_pos]; } private bool is_ws(uint8 what) { return what == ' ' || what == '\t' || what == '\r' || what == '\n'; } private void skip_single() { buffer_pos++; } private async void skip_until_non_ws() throws IOError { if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (is_ws(buffer[buffer_pos])) { buffer_pos++; if (buffer_pos >= buffer_fill) { yield update_buffer(); } } } private async string read_until_ws() throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (!is_ws(buffer[buffer_pos])) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async string read_until_char_or_ws(char x, char y = 0) throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (buffer[buffer_pos] != x && buffer[buffer_pos] != y && !is_ws(buffer[buffer_pos])) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async string read_until_char(char x) throws IOError { var res = new StringBuilder(); if (buffer_pos >= buffer_fill) { yield update_buffer(); } while (buffer[buffer_pos] != x) { res.append_c((char) buffer[buffer_pos++]); if (buffer_pos >= buffer_fill) { yield update_buffer(); } } return res.str; } private async StanzaAttribute read_attribute() throws IOError { var res = new StanzaAttribute(); res.name = yield read_until_char_or_ws('='); if ((yield read_single()) == '=') { var quot = yield peek_single(); if (quot == '\'' || quot == '"') { skip_single(); res.encoded_val = yield read_until_char(quot); skip_single(); } else { res.encoded_val = yield read_until_ws(); } } return res; } private void handle_entry_ns(StanzaEntry entry, string default_uri = ns_state.current_ns_uri) throws IOError { if (entry.ns_uri != null) return; if (entry.name.contains(":")) { var split = entry.name.split(":"); entry.ns_uri = ns_state.find_uri(split[0]); entry.name = split[1]; } else { entry.ns_uri = default_uri; } } private void handle_stanza_ns(StanzaNode res) throws IOError { foreach (StanzaAttribute attr in res.attributes) { if (attr.name == "xmlns" && attr.val != null) { attr.ns_uri = XMLNS_URI; ns_state.set_current((!)attr.val); } else if (attr.name.contains(":") && attr.val != null) { var split = attr.name.split(":"); if (split[0] == "xmlns") { attr.ns_uri = XMLNS_URI; attr.name = split[1]; ns_state.add_assoc((!)attr.val, attr.name); } } } handle_entry_ns(res); foreach (StanzaAttribute attr in res.attributes) { handle_entry_ns(attr, res.ns_uri ?? ns_state.current_ns_uri); } } public async StanzaNode read_node_start() throws IOError { var res = new StanzaNode(); res.attributes = new ArrayList(); var eof = false; if ((yield peek_single()) == '<') skip_single(); if ((yield peek_single()) == '?') res.pseudo = true; if ((yield peek_single()) == '/') { eof = true; skip_single(); res.name = yield read_until_char_or_ws('>'); while ((yield peek_single()) != '>') { skip_single(); } skip_single(); res.has_nodes = false; res.pseudo = false; handle_stanza_ns(res); return res; } res.name = yield read_until_char_or_ws('>', '/'); yield skip_until_non_ws(); char next_char = yield peek_single(); while (next_char != '/' && next_char != '>' && next_char != '?') { res.attributes.add(yield read_attribute()); yield skip_until_non_ws(); next_char = yield peek_single(); } if ((yield read_single()) == '/' || res.pseudo) { res.has_nodes = false; skip_single(); } else { res.has_nodes = true; } handle_stanza_ns(res); return res; } public async StanzaNode read_text_node() throws IOError { var res = new StanzaNode(); res.name = "#text"; res.ns_uri = ns_state.current_ns_uri; res.encoded_val = (yield read_until_char('<')); return res; } public async StanzaNode read_root_node() throws IOError { yield skip_until_non_ws(); if ((yield peek_single()) == '<') { var res = yield read_node_start(); if (res.pseudo) { return yield read_root_node(); } return res; } else { throw new IOError.INVALID_DATA("XML: Content before root node"); } } public async StanzaNode read_stanza_node() throws IOError { try { ns_state = ns_state.push(); var res = yield read_node_start(); if (res.has_nodes) { bool finish_node_seen = false; StanzaNode? text_node = null; do { text_node = yield read_text_node(); if ((yield peek_single()) == '<') { skip_single(); if ((yield peek_single()) == '/') { skip_single(); string desc = yield read_until_char('>'); skip_single(); if (desc.contains(":")) { var split = desc.split(":"); if (split[0] != ns_state.find_name((!)res.ns_uri)) throw new IOError.INVALID_DATA("XML: Closing namespace prefix mismatch"); if (split[1] != res.name) throw new IOError.INVALID_DATA("XML: Closing element name mismatch"); } else { if (ns_state.current_ns_uri != res.ns_uri) throw new IOError.INVALID_DATA("XML: Closing element namespace mismatch"); if (desc != res.name) throw new IOError.INVALID_DATA("XML: Closing element name mismatch"); } finish_node_seen = true; } else { res.sub_nodes.add(yield read_stanza_node()); } } } while (!finish_node_seen); if (res.sub_nodes.size == 0) { if (text_node == null || text_node.val.length == 0) { res.has_nodes = false; } else { res.sub_nodes.add(text_node); } } } ns_state = ns_state.pop(); return res; } catch (IOError.INVALID_DATA e) { uint8[] buffer_cpy = new uint8[buffer.length + 1]; Memory.copy(buffer_cpy, buffer, buffer.length); warning("invalid data at: %s".printf((string)buffer_cpy) + "\n"); throw e; } } public async StanzaNode read_node() throws IOError { yield skip_until_non_ws(); if ((yield peek_single()) == '<') { return yield read_stanza_node(); } else { return yield read_text_node(); } } } } dino-0.5.0/xmpp-vala/src/core/stanza_writer.vala0000664000000000000000000000476314776241610020354 0ustar rootrootnamespace Xmpp { public class StanzaWriter { private Cancellable? connection_cancellable; private OutputStream output; private Queue queue = new Queue(); private bool running = false; public StanzaWriter.for_stream(OutputStream output, Cancellable? cancellable = null) { this.output = output; this.connection_cancellable = cancellable; } public async void write_node(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { yield write_data(node.to_xml().data, io_priority, cancellable ?? connection_cancellable); } public async void write_nodes(StanzaNode node1, StanzaNode node2, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { var data1 = node1.to_xml().data; var data2 = node2.to_xml().data; uint8[] concat = new uint8[data1.length + data2.length]; int i = 0; foreach (var datum in data1) { concat[i++] = datum; } foreach (var datum in data2) { concat[i++] = datum; } yield write_data(concat, io_priority, cancellable ?? connection_cancellable); } public async void write(string s, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { yield write_data(s.data, io_priority, cancellable ?? connection_cancellable); } private async void write_data(owned uint8[] data, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { if (running) { queue.push_tail(new SourceFuncWrapper(write_data.callback)); yield; } running = true; try { yield output.write_all_async(data, io_priority, cancellable, null); } catch (IOError e) { if (!(e is IOError.CANCELLED)) { connection_cancellable.cancel(); } throw e; } catch (GLib.Error e) { connection_cancellable.cancel(); throw new IOError.FAILED("Error in GLib: %s".printf(e.message)); } finally { SourceFuncWrapper? sfw = queue.pop_head(); if (sfw != null) { sfw.sfun(); } else { running = false; } } } } public class SourceFuncWrapper : Object { public SourceFunc sfun; public SourceFuncWrapper(owned SourceFunc sfun) { this.sfun = (owned)sfun; } } } dino-0.5.0/xmpp-vala/src/core/starttls_xmpp_stream.vala0000664000000000000000000000414514776241610021751 0ustar rootrootpublic class Xmpp.StartTlsXmppStream : TlsXmppStream { private const string TLS_NS_URI = "urn:ietf:params:xml:ns:xmpp-tls"; string host; uint16 port; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert; public StartTlsXmppStream(Jid remote, string host, uint16 port, TlsXmppStream.OnInvalidCertWrapper on_invalid_cert) { base(remote); this.host = host; this.port = port; this.on_invalid_cert = on_invalid_cert; } public override async void connect() throws IOError { try { SocketClient client = new SocketClient(); debug("Connecting to %s:%i (starttls)", host, port); IOStream stream = yield client.connect_to_host_async(host, port, cancellable); reset_stream(stream); yield setup(); StanzaNode node = yield read(); var starttls_node = node.get_subnode("starttls", TLS_NS_URI); if (starttls_node == null) { warning("%s does not offer starttls", remote_name.to_string()); } write(new StanzaNode.build("starttls", TLS_NS_URI).add_self_xmlns()); node = yield read(); if (node.ns_uri != TLS_NS_URI || node.name != "proceed") { warning("Server did not 'proceed' starttls request"); } try { var identity = new NetworkService("xmpp-client", "tcp", remote_name.to_string()); var conn = TlsClientConnection.new(get_stream(), identity); reset_stream(conn); conn.accept_certificate.connect(on_invalid_certificate); conn.accept_certificate.connect((cert, flags) => on_invalid_cert.func(cert, flags)); } catch (Error e) { stderr.printf("Failed to start TLS: %s\n", e.message); } yield setup(); attach_negotation_modules(); } catch (IOError e) { throw e; } catch (Error e) { throw new IOError.CONNECTION_REFUSED("Failed connecting to %s:%i (starttls): %s", host, port, e.message); } } } dino-0.5.0/xmpp-vala/src/core/stream_connect.vala0000664000000000000000000001074514776241610020461 0ustar rootrootnamespace Xmpp { private class SrvTargetInfo { public string host { get; set; } public uint16 port { get; set; } public string service { get; set; } public uint16 priority { get; set; } } public class XmppStreamResult { public TlsXmppStream? stream { get; set; } public TlsCertificateFlags? tls_errors { get; set; } public IOError? io_error { get; set; } } public async XmppStreamResult establish_stream(Jid bare_jid, Gee.List modules, string? log_options, owned TlsXmppStream.OnInvalidCert on_invalid_cert) { Jid remote = bare_jid.domain_jid; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert_wrapper = new TlsXmppStream.OnInvalidCertWrapper(on_invalid_cert); //Lookup xmpp-client and xmpps-client SRV records GLib.List? targets = new GLib.List(); GLibFixes.Resolver resolver = GLibFixes.Resolver.get_default(); try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpp-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpp-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpp-client DNS records for %s: %s", remote.to_string(), e.message); } try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpps-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpps-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpps-client DNS records for %s: %s", remote.to_string(), e.message); } targets.sort((a, b) => { return a.priority - b.priority; }); // Add fallback connection bool should_add_fallback = true; foreach (SrvTargetInfo target in targets) { if (target.service == "xmpp-client" && target.port == 5222 && target.host == remote.to_string()) { should_add_fallback = false; } } if (should_add_fallback) { targets.append(new SrvTargetInfo() { host=remote.to_string(), port=5222, service="xmpp-client", priority=uint16.MAX}); } // Try all connection options from lowest to highest priority TlsXmppStream? stream = null; TlsCertificateFlags? tls_errors = null; IOError? io_error = null; uint connection_timeout_id = 0; foreach (SrvTargetInfo target in targets) { try { if (target.service == "xmpp-client") { stream = new StartTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } else { stream = new DirectTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } stream.log = new XmppLog(bare_jid.to_string(), log_options); foreach (XmppStreamModule module in modules) { stream.add_module(module); } connection_timeout_id = Timeout.add_seconds(30, () => { warning("Connection attempt timed out"); stream.disconnect(); return Source.REMOVE; }); yield stream.connect(); Source.remove(connection_timeout_id); connection_timeout_id = 0; return new XmppStreamResult() { stream=stream }; } catch (IOError e) { warning("Could not establish XMPP session with %s:%i: %s", target.host, target.port, e.message); if (stream != null) { if (connection_timeout_id != 0) { Source.remove(connection_timeout_id); connection_timeout_id = 0; } if (stream.errors != null) { tls_errors = stream.errors; } io_error = e; stream.detach_modules(); } } } return new XmppStreamResult() { io_error=io_error, tls_errors=tls_errors }; } }dino-0.5.0/xmpp-vala/src/core/tls_xmpp_stream.vala0000664000000000000000000000226414776241610020673 0ustar rootrootpublic abstract class Xmpp.TlsXmppStream : IoXmppStream { public TlsCertificateFlags? errors; public delegate bool OnInvalidCert(GLib.TlsCertificate peer_cert, GLib.TlsCertificateFlags errors); public class OnInvalidCertWrapper { public OnInvalidCert func; public OnInvalidCertWrapper(owned OnInvalidCert func) { this.func = (owned) func; } } protected TlsXmppStream(Jid remote_name) { base(remote_name); } protected bool on_invalid_certificate(TlsCertificate peer_cert, TlsCertificateFlags errors) { this.errors = errors; string error_str = ""; foreach (var f in new TlsCertificateFlags[]{TlsCertificateFlags.UNKNOWN_CA, TlsCertificateFlags.BAD_IDENTITY, TlsCertificateFlags.NOT_ACTIVATED, TlsCertificateFlags.EXPIRED, TlsCertificateFlags.REVOKED, TlsCertificateFlags.INSECURE, TlsCertificateFlags.GENERIC_ERROR, TlsCertificateFlags.VALIDATE_ALL}) { if (f in errors) { error_str += @"$(f), "; } } warning(@"[%p, %s] Tls Certificate Errors: %s", this, this.remote_name.to_string(), error_str); return false; } }dino-0.5.0/xmpp-vala/src/core/xmpp_log.vala0000664000000000000000000001144314776241610017276 0ustar rootrootusing Gee; namespace Xmpp { public class XmppLog { protected const string ANSI_COLOR_END = "\x1b[0m"; protected const string ANSI_COLOR_WHITE = "\x1b[37;1m"; class NodeLogDesc { public string? name; private string? ns_uri; private string? val; private Map attrs = new HashMap(); private NodeLogDesc? inner; public NodeLogDesc(string desc) { string d = desc; if (d.contains("[")) { int start = d.index_of("["); int end = d.index_of("]"); string attrs = d.substring(start + 1, end - start - 1); d = d.substring(0, start) + d.substring(end + 1); foreach (string attr in attrs.split(",")) { if (attr.contains("=")) { string key = attr.substring(0, attr.index_of("=")); string val = attr.substring(attr.index_of("=") + 1); this.attrs[key] = val; } else { this.attrs[attr] = null; } } } if (d.contains(":") && d.index_of("{") == 0 && d.index_of("}") != -1) { int end = d.index_of("}"); this.ns_uri = d.substring(1, end - 1); d = d.substring(end + 2); } if (d.contains(".")) { inner = new NodeLogDesc(d.substring(d.index_of(".") + 1)); d = d.substring(0, d.index_of(".")); } else if (d.contains("=")) { this.val = d.substring(d.index_of("=")); d = d.substring(0, d.index_of("=")); } if (d != "") this.name = d; } public bool matches(StanzaNode node) { if (name != null && node.name != name) return false; if (ns_uri != null && node.ns_uri != ns_uri) return false; if (val != null && node.val != val) return false; foreach (var pair in attrs.entries) { if (pair.value == null && node.get_attribute(pair.key) == null) return false; else if (pair.value != null && pair.value != node.get_attribute(pair.key)) return false; } if (inner == null) return true; foreach (StanzaNode snode in node.get_all_subnodes()) { if (((!)inner).matches(snode)) return true; } return false; } } private bool use_ansi; private bool hide_ns = true; private string ident; private string desc; private Gee.List descs = new ArrayList(); public XmppLog(string? ident = null, string? desc = null) { this.ident = ident ?? ""; this.desc = desc ?? ""; this.use_ansi = is_atty(stderr.fileno()); while (this.desc.contains(";")) { string opt = this.desc.substring(0, this.desc.index_of(";")); this.desc = this.desc.substring(opt.length + 1); switch (opt) { case "ansi": use_ansi = true; break; case "no-ansi": use_ansi = false; break; case "hide-ns": hide_ns = true; break; case "show-ns": hide_ns = false; break; } } if (desc != "") { foreach (string d in this.desc.split("|")) { descs.add(new NodeLogDesc(d)); } } } public virtual bool should_log_node(StanzaNode node) { if (ident == "" || desc == "") return false; if (desc == "all") return true; foreach (var desc in descs) { if (desc.matches(node)) return true; } return false; } public virtual bool should_log_str(string str) { if (ident == "" || desc == "") return false; if (desc == "all") return true; foreach (var desc in descs) { if (desc.name == "#text") return true; } return false; } public void node(string what, StanzaNode node, XmppStream stream) { if (should_log_node(node)) { stderr.printf("%sXMPP %s [%s stream:%p thread:%p %s]%s\n%s\n", use_ansi ? ANSI_COLOR_WHITE : "", what, ident, stream, Thread.self(), new DateTime.now_local().to_string(), use_ansi ? ANSI_COLOR_END : "", use_ansi ? node.to_ansi_string(hide_ns) : node.to_string()); } } public void str(string what, string str, XmppStream stream) { if (should_log_str(str)) { stderr.printf("%sXMPP %s [%s stream:%p thread:%p %s]%s\n%s\n", use_ansi ? ANSI_COLOR_WHITE : "", what, ident, stream, Thread.self(), new DateTime.now_local().to_string(), use_ansi ? ANSI_COLOR_END : "", str); } } [CCode (cname = "isatty")] private static extern bool is_atty(int fd); } } dino-0.5.0/xmpp-vala/src/core/xmpp_stream.vala0000664000000000000000000001516014776241610020010 0ustar rootrootusing Gee; public abstract class Xmpp.XmppStream : Object { public signal void received_node(XmppStream stream, StanzaNode node); public signal void received_root_node(XmppStream stream, StanzaNode node); public signal void received_features_node(XmppStream stream); public signal void received_message_stanza(XmppStream stream, StanzaNode node); public signal void received_presence_stanza(XmppStream stream, StanzaNode node); public signal void received_iq_stanza(XmppStream stream, StanzaNode node); public signal void received_nonza(XmppStream stream, StanzaNode node); public signal void stream_negotiated(XmppStream stream); public signal void attached_modules(XmppStream stream); public const string NS_URI = "http://etherx.jabber.org/streams"; public Gee.List flags { get; private set; default=new ArrayList(); } public Gee.List modules { get; private set; default=new ArrayList(); } public StanzaNode? features { get; private set; default = new StanzaNode.build("features", NS_URI); } public Jid remote_name; public XmppLog log = new XmppLog(); public bool negotiation_complete { get; set; default=false; } protected bool non_negotiation_modules_attached = false; protected bool setup_needed = false; protected bool disconnected = false; protected XmppStream(Jid remote_name) { this.remote_name = remote_name; } public abstract new async void connect() throws IOError; public abstract new async void disconnect() throws IOError; public abstract async StanzaNode read() throws IOError; [Version (deprecated = true, deprecated_since = "0.1", replacement = "write_async")] public abstract void write(StanzaNode node, int io_priority = Priority.DEFAULT); public abstract async void write_async(StanzaNode node, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError; public abstract async void setup() throws IOError; public void require_setup() { setup_needed = true; } public bool is_setup_needed() { return setup_needed; } public void add_flag(XmppStreamFlag flag) { flags.add(flag); } public bool has_flag(FlagIdentity? identity) { return get_flag(identity) != null; } public T? get_flag(FlagIdentity? identity) { if (identity == null) return null; foreach (var flag in flags) { if (((!)identity).matches(flag)) return ((!)identity).cast(flag); } return null; } public void remove_flag(XmppStreamFlag flag) { flags.remove(flag); } public XmppStream add_module(XmppStreamModule module) { foreach (XmppStreamModule m in modules) { if (m.get_ns() == module.get_ns() && m.get_id() == module.get_id()) { warning("[%p] Adding already added module: %s\n", this, module.get_id()); return this; } } modules.add(module); if (negotiation_complete) module.attach(this); return this; } public void detach_modules() { foreach (XmppStreamModule module in modules) { module.detach(this); } } public T? get_module(ModuleIdentity? identity) { if (identity == null) return null; foreach (var module in modules) { if (((!)identity).matches(module)) return ((!)identity).cast(module); } return null; } public async void loop() throws IOError { while (true) { if (setup_needed) { yield setup(); } StanzaNode node = yield read(); Idle.add(loop.callback); yield; if (disconnected) break; yield handle_stanza(node); if (!non_negotiation_modules_attached && negotiation_modules_done()) { attach_non_negotation_modules(); non_negotiation_modules_attached = true; if (!negotiation_complete) { stream_negotiated(this); negotiation_complete = true; } } } } private async void handle_stanza(StanzaNode node) { received_node(this, node); if (node.ns_uri == NS_URI && node.name == "features") { features = node; received_features_node(this); } else if (node.ns_uri == NS_URI && node.name == "stream" && node.pseudo) { debug("[%p] Server closed stream", this); try { yield disconnect(); } catch (Error e) {} return; } else if (node.ns_uri == JABBER_URI) { if (node.name == "message") { received_message_stanza(this, node); } else if (node.name == "presence") { received_presence_stanza(this, node); } else if (node.name == "iq") { received_iq_stanza(this, node); } else { received_nonza(this, node); } } else { received_nonza(this, node); } } public bool is_negotiation_active() { foreach (XmppStreamModule module in modules) { if (module is XmppStreamNegotiationModule) { XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module; if (negotiation_module.negotiation_active(this)) return true; } } return false; } private bool negotiation_modules_done() throws IOError { if (setup_needed) return false; if (is_negotiation_active()) return false; foreach (XmppStreamModule module in modules) { if (module is XmppStreamNegotiationModule) { XmppStreamNegotiationModule negotiation_module = (XmppStreamNegotiationModule) module; if (negotiation_module.mandatory_outstanding(this)) { throw new IOError.FAILED("mandatory-to-negotiate feature not negotiated: " + negotiation_module.get_id()); } } } return true; } private void attach_non_negotation_modules() { foreach (XmppStreamModule module in modules) { if (module as XmppStreamNegotiationModule == null) { module.attach(this); } } attached_modules(this); } public void attach_negotation_modules() { foreach (XmppStreamModule module in modules) { if (module as XmppStreamNegotiationModule != null) { module.attach(this); } } } }dino-0.5.0/xmpp-vala/src/glib_fixes.vapi0000664000000000000000000000414714776241610016653 0ustar rootroot[CCode (cprefix = "G", gir_namespace = "Gio", gir_version = "2.0", lower_case_cprefix = "g_")] namespace GLibFixes { [CCode (cheader_filename = "gio/gio.h", type_id = "g_resolver_get_type ()")] public class Resolver : GLib.Object { [CCode (has_construct_function = false)] protected Resolver(); [Version (since = "2.22")] public static Resolver get_default(); [Version (since = "2.22")] public virtual string lookup_by_address(GLib.InetAddress address, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual async string lookup_by_address_async(GLib.InetAddress address, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual GLib.List lookup_by_name(string hostname, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual async GLib.List lookup_by_name_async(string hostname, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.34")] public virtual GLib.List lookup_records(string rrname, GLib.ResolverRecordType record_type, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.34")] public virtual async GLib.List lookup_records_async(string rrname, GLib.ResolverRecordType record_type, GLib.Cancellable? cancellable = null) throws GLib.Error ; [Version (since = "2.22")] public virtual GLib.List lookup_service(string service, string protocol, string domain, GLib.Cancellable? cancellable = null) throws GLib.Error ; [CCode (finish_vfunc_name = "lookup_service_finish", vfunc_name = "lookup_service_async")] public async GLib.List lookup_service_async (string service, string protocol, string domain, GLib.Cancellable? cancellable = null) throws GLib.Error; [Version (since = "2.22")] public void set_default(); public virtual signal void reload (); } } dino-0.5.0/xmpp-vala/src/module/0000775000000000000000000000000014776241610015136 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/bind.vala0000664000000000000000000000633014776241610016721 0ustar rootrootnamespace Xmpp.Bind { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-bind"; /** The parties to a stream MUST consider resource binding as mandatory-to-negotiate. (RFC6120 7.3.1) */ public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "bind_module"); public string? requested_resource { get; set; } public signal void bound_to_resource(XmppStream stream, Jid my_jid); public Module(string? requested_resource) { this.requested_resource = requested_resource; } public void iq_response_stanza(XmppStream stream, Iq.Stanza iq) { var flag = stream.get_flag(Flag.IDENTITY); if (flag == null || flag.finished) return; if (iq.type_ == Iq.Stanza.TYPE_RESULT) { try { flag.my_jid = new Jid(iq.stanza.get_subnode("jid", NS_URI, true).get_string_content()); flag.finished = true; bound_to_resource(stream, flag.my_jid); } catch (InvalidJidError e) { warning("Received invalid Jid when binding: %s", e.message); } } } public void received_features_node(XmppStream stream) { if (stream.is_setup_needed()) return; if (stream.is_negotiation_active()) return; var bind = stream.features.get_subnode("bind", NS_URI); if (bind != null) { var flag = new Flag(); StanzaNode bind_node = new StanzaNode.build("bind", NS_URI).add_self_xmlns(); if (requested_resource != null) { bind_node.put_node(new StanzaNode.build("resource", NS_URI).put_node(new StanzaNode.text(requested_resource))); } stream.get_module(Iq.Module.IDENTITY).send_iq(stream, new Iq.Stanza.set(bind_node), iq_response_stanza); stream.add_flag(flag); } } public override void attach(XmppStream stream) { stream.received_features_node.connect(this.received_features_node); } public override void detach(XmppStream stream) { stream.received_features_node.disconnect(this.received_features_node); } public override bool mandatory_outstanding(XmppStream stream) { return !stream.has_flag(Flag.IDENTITY) || !stream.get_flag(Flag.IDENTITY).finished; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "bind"); public Jid? my_jid; public bool finished = false; public static Jid? get_my_jid(XmppStream stream) { return stream.get_flag(IDENTITY).my_jid; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/bookmarks_provider.vala0000664000000000000000000000143514776241610021710 0ustar rootrootusing Gee; namespace Xmpp { public interface BookmarksProvider : Object { public signal void conference_added(XmppStream stream, Conference conferences); public signal void conference_removed(XmppStream stream, Jid jid); public signal void conference_changed(XmppStream stream, Conference conferences); public signal void received_conferences(XmppStream stream, Set conferences); public async abstract async Set? get_conferences(XmppStream stream); public async abstract void add_conference(XmppStream stream, Conference conference); public async abstract void remove_conference(XmppStream stream, Conference conference); public async abstract void replace_conference(XmppStream stream, Jid muc_jid, Conference modified_conference); } } dino-0.5.0/xmpp-vala/src/module/conference.vala0000664000000000000000000000110714776241610020111 0ustar rootrootnamespace Xmpp { public class Conference : Object { public virtual Jid? jid { get; set; } public virtual bool autojoin { get; set; } public virtual string? nick { get; set; } public virtual string? name { get; set; } public virtual string? password { get; set; } public bool equals(Conference c) { return equals_func(this, c); } public static bool equals_func(Conference a, Conference b) { return Jid.equals_func(a.jid, b.jid); } public static uint hash_func(Conference a) { return Jid.hash_func(a.jid); } } } dino-0.5.0/xmpp-vala/src/module/iq/0000775000000000000000000000000014776241610015547 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/iq/module.vala0000664000000000000000000001266314776241610017711 0ustar rootrootusing Gee; namespace Xmpp.Iq { private const string NS_URI = "jabber:client"; public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "iq_module"); public signal void preprocess_incoming_iq_set_get(XmppStream stream, Stanza iq_stanza); public signal void preprocess_outgoing_iq_set_get(XmppStream stream, Stanza iq_stanza); private HashMap responseListeners = new HashMap(); private HashMap> namespaceRegistrants = new HashMap>(); public async Iq.Stanza send_iq_async(XmppStream stream, Iq.Stanza iq, int io_priority = Priority.DEFAULT, Cancellable? cancellable = null) throws IOError { assert(iq.type_ == Iq.Stanza.TYPE_GET || iq.type_ == Iq.Stanza.TYPE_SET); preprocess_outgoing_iq_set_get(stream, iq); Iq.Stanza? return_stanza = null; responseListeners[iq.id] = new ResponseListener((_, result_iq) => { return_stanza = result_iq; Idle.add(send_iq_async.callback); }); stream.write_async(iq.stanza, io_priority, cancellable); yield; cancellable.set_error_if_cancelled(); return return_stanza; } public delegate void OnResult(XmppStream stream, Iq.Stanza iq); public void send_iq(XmppStream stream, Iq.Stanza iq, owned OnResult? listener = null, int io_priority = Priority.DEFAULT) { preprocess_outgoing_iq_set_get(stream, iq); stream.write(iq.stanza, io_priority); if (listener != null) { responseListeners[iq.id] = new ResponseListener((owned) listener); } } public void register_for_namespace(string namespace, Handler module) { if (!namespaceRegistrants.has_key(namespace)) { namespaceRegistrants.set(namespace, new ArrayList()); } namespaceRegistrants[namespace].add(module); } public void unregister_from_namespace(string namespace, Handler module) { ArrayList? handlers = namespaceRegistrants[namespace]; if (handlers != null) handlers.remove(module); } public override void attach(XmppStream stream) { stream.received_iq_stanza.connect(on_received_iq_stanza); } public override void detach(XmppStream stream) { stream.received_iq_stanza.disconnect(on_received_iq_stanza); } public override bool mandatory_outstanding(XmppStream stream) { return false; } public override bool negotiation_active(XmppStream stream) { return false; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private async void on_received_iq_stanza(XmppStream stream, StanzaNode node) { Iq.Stanza iq = new Iq.Stanza.from_stanza(node, stream.has_flag(Bind.Flag.IDENTITY) ? stream.get_flag(Bind.Flag.IDENTITY).my_jid : null); if (iq.type_ == Iq.Stanza.TYPE_RESULT || iq.is_error()) { if (responseListeners.has_key(iq.id)) { ResponseListener? listener = responseListeners.get(iq.id); if (listener != null) { listener.on_result(stream, iq); } responseListeners.unset(iq.id); } } else { Gee.List children = node.get_all_subnodes(); if (children.size == 1 && namespaceRegistrants.has_key(children[0].ns_uri)) { preprocess_incoming_iq_set_get(stream, iq); Gee.List handlers = namespaceRegistrants[children[0].ns_uri]; foreach (Handler handler in handlers) { if (iq.type_ == Iq.Stanza.TYPE_GET) { yield handler.on_iq_get(stream, iq); } else if (iq.type_ == Iq.Stanza.TYPE_SET) { yield handler.on_iq_set(stream, iq); } } } else { // Send error if we don't handle the NS of the IQ get/set payload (RFC6120 10.3.3 (2)) Iq.Stanza unavailable_error = new Iq.Stanza.error(iq, new ErrorStanza.service_unavailable()) { to=iq.from }; send_iq(stream, unavailable_error); } } } private class ResponseListener { public OnResult on_result { get; private owned set; } public ResponseListener(owned OnResult on_result) { this.on_result = (owned) on_result; } } } public interface Handler : Object { public async virtual void on_iq_get(XmppStream stream, Iq.Stanza iq) { Iq.Stanza bad_request = new Iq.Stanza.error(iq, new ErrorStanza.bad_request("unexpected IQ get for this namespace")); stream.get_module(Module.IDENTITY).send_iq(stream, bad_request); } public async virtual void on_iq_set(XmppStream stream, Iq.Stanza iq) { Iq.Stanza bad_request = new Iq.Stanza.error(iq, new ErrorStanza.bad_request("unexpected IQ set for this namespace")); stream.get_module(Module.IDENTITY).send_iq(stream, bad_request); } } } dino-0.5.0/xmpp-vala/src/module/iq/stanza.vala0000664000000000000000000000231614776241610017716 0ustar rootrootusing Gee; namespace Xmpp.Iq { public class Stanza : Xmpp.Stanza { public const string TYPE_GET = "get"; public const string TYPE_RESULT = "result"; public const string TYPE_SET = "set"; private Stanza(string? id = null) { base.outgoing(new StanzaNode.build("iq")); this.id = id ?? random_uuid(); } public Stanza.get(StanzaNode stanza_node, string? id = null) { this(id); this.type_ = TYPE_GET; stanza.put_node(stanza_node); } public Stanza.result(Stanza request, StanzaNode? stanza_node = null) { this(request.id); this.to = request.from; this.type_ = TYPE_RESULT; if (stanza_node != null) { stanza.put_node(stanza_node); } } public Stanza.set(StanzaNode stanza_node, string? id = null) { this(id); this.type_ = TYPE_SET; stanza.put_node(stanza_node); } public Stanza.error(Stanza request, ErrorStanza error_stanza) { this(request.id); this.type_ = TYPE_ERROR; stanza.put_node(error_stanza.error_node); } public Stanza.from_stanza(StanzaNode stanza_node, Jid? my_jid) { base.incoming(stanza_node, my_jid); } } } dino-0.5.0/xmpp-vala/src/module/jid.vala0000664000000000000000000001574414776241610016564 0ustar rootrootnamespace Xmpp { public class Jid { public string? localpart; public string domainpart; public string? resourcepart; public Jid bare_jid { owned get { return is_bare() ? this : new Jid.intern(null, localpart, domainpart, null); } } public Jid domain_jid { owned get { return is_domain() ? this : new Jid.intern(domainpart, null, domainpart, null); } } private string jid; public Jid(string jid) throws InvalidJidError { int slash_index = jid.index_of("/"); int at_index = jid.index_of("@"); if (at_index > slash_index && slash_index != -1) at_index = -1; string resourcepart = slash_index < 0 ? null : jid.slice(slash_index + 1, jid.length); string localpart = at_index < 0 ? null : jid.slice(0, at_index); string domainpart; if (at_index < 0) { if (slash_index < 0) { domainpart = jid; } else { domainpart = jid.slice(0, slash_index); } } else { if (slash_index < 0) { domainpart = jid.slice(at_index + 1, jid.length); } else { domainpart = jid.slice(at_index + 1, slash_index); } } this.components(localpart, domainpart, resourcepart); } private Jid.intern(owned string? jid, owned string? localpart, owned string domainpart, owned string? resourcepart) { this.jid = (owned) jid; this.localpart = (owned) localpart; this.domainpart = (owned) domainpart; this.resourcepart = (owned) resourcepart; } public Jid.components(string? localpart, string domainpart, string? resourcepart) throws InvalidJidError { // TODO verify and normalize all parts if (domainpart.length == 0) throw new InvalidJidError.EMPTY_DOMAIN("Domain is empty"); if (localpart != null && localpart.length == 0) throw new InvalidJidError.EMPTY_LOCAL("Localpart is empty but non-null"); if (resourcepart != null && resourcepart.length == 0) throw new InvalidJidError.EMPTY_RESOURCE("Resource is empty but non-null"); string domain = domainpart[domainpart.length - 1] == '.' ? domainpart.substring(0, domainpart.length - 1) : domainpart; if (domain.contains("xn--")) { domain = idna_decode(domain); } this.localpart = prepare(localpart, ICU.PrepType.RFC3920_NODEPREP); this.domainpart = prepare(domain, ICU.PrepType.RFC3491_NAMEPREP); this.resourcepart = prepare(resourcepart, ICU.PrepType.RFC3920_RESOURCEPREP); idna_verify(this.domainpart); } private static string idna_decode(string src) throws InvalidJidError { ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.IDNAInfo info; char[] dest = new char[src.length * 2]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToUnicodeUTF8(src, -1, dest, out info, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status.is_failure() || info.errors > 0) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } return (string) dest; } private static void idna_verify(string src) throws InvalidJidError { ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.IDNAInfo info; char[] dest = new char[src.length * 2]; ICU.IDNA.openUTS46(ICU.IDNAOptions.DEFAULT, ref status).nameToASCII_UTF8(src, -1, dest, out info, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status.is_failure() || info.errors > 0) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } } private static string? prepare(string? src, ICU.PrepType type, bool strict = false) throws InvalidJidError { if (src == null) return src; try { ICU.ParseError error; ICU.ErrorCode status = ICU.ErrorCode.ZERO_ERROR; ICU.PrepProfile profile = ICU.PrepProfile.openByType(type, ref status); ICU.String src16 = ICU.String.from_string(src); int32 dest16_capacity = src16.len() * 2 + 1; ICU.String dest16 = ICU.String.alloc(dest16_capacity); long dest16_length = profile.prepare(src16, src16.len(), dest16, dest16_capacity, strict ? ICU.PrepOptions.DEFAULT : ICU.PrepOptions.ALLOW_UNASSIGNED, out error, ref status); if (status == ICU.ErrorCode.INVALID_CHAR_FOUND) { throw new InvalidJidError.INVALID_CHAR("Found invalid character"); } else if (status == ICU.ErrorCode.STRINGPREP_PROHIBITED_ERROR) { throw new InvalidJidError.INVALID_CHAR("Found prohibited character"); } else if (status != ICU.ErrorCode.ZERO_ERROR) { throw new InvalidJidError.UNKNOWN(@"Unknown error: $(status.errorName())"); } else if (dest16_length < 0) { throw new InvalidJidError.UNKNOWN("Unknown error"); } return dest16.to_string(); } catch (ConvertError e) { throw new InvalidJidError.INVALID_CHAR(@"Conversion error: $(e.message)"); } } public Jid with_resource(string? resourcepart) throws InvalidJidError { return new Jid.components(localpart, domainpart, resourcepart); } public bool is_domain() { return localpart == null && resourcepart == null; } public bool is_bare() { return resourcepart == null; } public bool is_full() { return localpart != null && resourcepart != null; } public string to_string() { if (jid == null) { if (localpart != null && resourcepart != null) { jid = @"$localpart@$domainpart/$resourcepart"; } else if (localpart != null) { jid = @"$localpart@$domainpart"; } else if (resourcepart != null) { jid = @"$domainpart/$resourcepart"; } else { jid = domainpart; } } return jid; } public bool equals_bare(Jid? jid) { return jid != null && equals_bare_func(this, jid); } public bool equals(Jid? jid) { return jid != null && equals_func(this, jid); } public static new bool equals_bare_func(Jid jid1, Jid jid2) { return jid1.localpart == jid2.localpart && jid1.domainpart == jid2.domainpart; } public static bool equals_func(Jid jid1, Jid jid2) { return equals_bare_func(jid1, jid2) && jid1.resourcepart == jid2.resourcepart; } public static new uint hash_bare_func(Jid jid) { return jid.bare_jid.to_string().hash(); } public static new uint hash_func(Jid jid) { return jid.to_string().hash(); } } public errordomain InvalidJidError { EMPTY_DOMAIN, EMPTY_RESOURCE, EMPTY_LOCAL, INVALID_CHAR, UNKNOWN } } dino-0.5.0/xmpp-vala/src/module/message/0000775000000000000000000000000014776241610016562 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/message/module.vala0000664000000000000000000000433714776241610020723 0ustar rootrootusing Gee; namespace Xmpp { private const string NS_URI = "jabber:client"; public class MessageModule : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "message_module"); public StanzaListenerHolder received_pipeline = new StanzaListenerHolder(); public StanzaListenerHolder send_pipeline = new StanzaListenerHolder(); public signal void received_message(XmppStream stream, MessageStanza message); public signal void received_error(XmppStream stream, MessageStanza message, ErrorStanza error); public signal void received_message_unprocessed(XmppStream stream, MessageStanza message); public async void send_message(XmppStream stream, MessageStanza message) throws IOError { yield send_pipeline.run(stream, message); yield stream.write_async(message.stanza); } public async void received_message_stanza_async(XmppStream stream, StanzaNode node) { MessageStanza message = new MessageStanza.from_stanza(node, stream.get_flag(Bind.Flag.IDENTITY).my_jid); received_message_unprocessed(stream, message); if (message.is_error()) { ErrorStanza? error_stanza = message.get_error(); if (error_stanza == null) return; received_error(stream, message, error_stanza); } else { bool abort = yield received_pipeline.run(stream, message); if (abort) return; received_message(stream, message); } } private void received_message_stanza(XmppStream stream, StanzaNode node) { received_message_stanza_async.begin(stream, node); } public override void attach(XmppStream stream) { stream.received_message_stanza.connect(received_message_stanza); } public override void detach(XmppStream stream) { stream.received_message_stanza.disconnect(received_message_stanza); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/message/stanza.vala0000664000000000000000000000405314776241610020731 0ustar rootrootusing Gee; namespace Xmpp { public class MessageStanza : Xmpp.Stanza { public const string NODE_BODY = "body"; public const string NODE_SUBJECT = "subject"; public const string NODE_THREAD = "thread"; public const string TYPE_CHAT = "chat"; public const string TYPE_GROUPCHAT = "groupchat"; public const string TYPE_HEADLINE = "headline"; public const string TYPE_NORMAL = "normal"; public bool rerun_parsing = false; private ArrayList flags = new ArrayList(); public string? body { get { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); return body_node == null ? null : body_node.get_string_content(); } set { StanzaNode? body_node = stanza.get_subnode(NODE_BODY); if (value == null) { if (body_node != null) stanza.sub_nodes.remove(body_node); return; } if (body_node == null) { body_node = new StanzaNode.build(NODE_BODY); stanza.put_node(body_node); } body_node.sub_nodes.clear(); body_node.put_node(new StanzaNode.text(value)); } } public override string? type_ { get { return base.type_ ?? TYPE_NORMAL; } set { base.type_ = value; } } public MessageStanza(string? id = null) { base.outgoing(new StanzaNode.build("message")); stanza.set_attribute(ATTRIBUTE_ID, id ?? random_uuid()); } public MessageStanza.from_stanza(StanzaNode stanza_node, Jid my_jid) { base.incoming(stanza_node, my_jid); } public void add_flag(MessageFlag flag) { flags.add(flag); } public MessageFlag? get_flag(string ns, string id) { foreach (MessageFlag flag in flags) { if (flag.get_ns() == ns && flag.get_id() == id) return flag; } return null; } } public abstract class MessageFlag : Object { public abstract string get_ns(); public abstract string get_id(); } } dino-0.5.0/xmpp-vala/src/module/presence/0000775000000000000000000000000014776241610016742 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/presence/flag.vala0000664000000000000000000000434214776241610020523 0ustar rootrootusing Gee; namespace Xmpp.Presence { public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "presence"); private HashMap> resources = new HashMap>(Jid.hash_bare_func, Jid.equals_bare_func); private HashMap presences = new HashMap(Jid.hash_func, Jid.equals_func); public Set get_available_jids() { return resources.keys; } public Gee.List? get_resources(Jid jid) { if (!resources.has_key(jid)) return null; ArrayList ret = new ArrayList(Jid.equals_func); ret.add_all(resources[jid]); return ret; } public Presence.Stanza? get_presence(Jid full_jid) { return presences[full_jid]; } public Gee.List get_presences(Jid jid) { Gee.List ret = new ArrayList(); Gee.List? jid_res = resources[jid]; if (jid_res == null) return ret; foreach (Jid full_jid in jid_res) { ret.add(presences[full_jid]); } return ret; } public void add_presence(Presence.Stanza presence) { if (!resources.has_key(presence.from)) { resources[presence.from] = new ArrayList(Jid.equals_func); } if (resources[presence.from].contains(presence.from)) { resources[presence.from].remove(presence.from); } resources[presence.from].add(presence.from); presences[presence.from] = presence; } public void remove_presence(Jid jid) { if (resources.has_key(jid)) { if (jid.is_bare()) { foreach (Jid full_jid in resources[jid]) { presences.unset(full_jid); } resources.unset(jid); } else { resources[jid].remove(jid); if (resources[jid].size == 0) { resources.unset(jid); } presences.unset(jid); } } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/presence/module.vala0000664000000000000000000001156314776241610021102 0ustar rootrootusing Gee; namespace Xmpp.Presence { private const string NS_URI = "jabber:client"; public class Module : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "presence_module"); public signal void received_presence(XmppStream stream, Presence.Stanza presence); public signal void pre_send_presence_stanza(XmppStream stream, Presence.Stanza presence); public signal void initial_presence_sent(XmppStream stream, Presence.Stanza presence); public signal void received_available(XmppStream stream, Presence.Stanza presence); public signal void received_available_show(XmppStream stream, Jid jid, string show); public signal void received_unavailable(XmppStream stream, Presence.Stanza presence); public signal void received_subscription_request(XmppStream stream, Jid jid); public signal void received_subscription_approval(XmppStream stream, Jid jid); public signal void received_unsubscription(XmppStream stream, Jid jid); public bool available_resource = true; public void request_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_SUBSCRIBE; send_presence(stream, presence); } public void approve_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_SUBSCRIBED; send_presence(stream, presence); } public void deny_subscription(XmppStream stream, Jid bare_jid) { cancel_subscription(stream, bare_jid); } public void cancel_subscription(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBED; send_presence(stream, presence); } public void unsubscribe(XmppStream stream, Jid bare_jid) { Presence.Stanza presence = new Presence.Stanza(); presence.to = bare_jid; presence.type_ = Presence.Stanza.TYPE_UNSUBSCRIBE; send_presence(stream, presence); } public void send_presence(XmppStream stream, Presence.Stanza presence) { pre_send_presence_stanza(stream, presence); stream.write(presence.stanza); } public override void attach(XmppStream stream) { stream.add_flag(new Flag()); stream.received_presence_stanza.connect(on_received_presence_stanza); stream.stream_negotiated.connect(on_stream_negotiated); } public override void detach(XmppStream stream) { stream.received_presence_stanza.disconnect(on_received_presence_stanza); stream.stream_negotiated.disconnect(on_stream_negotiated); } private void on_received_presence_stanza(XmppStream stream, StanzaNode node) { Presence.Stanza presence = new Presence.Stanza.from_stanza(node, stream.get_flag(Bind.Flag.IDENTITY).my_jid); received_presence(stream, presence); switch (presence.type_) { case Presence.Stanza.TYPE_AVAILABLE: stream.get_flag(Flag.IDENTITY).add_presence(presence); received_available(stream, presence); received_available_show(stream, presence.from, presence.show); break; case Presence.Stanza.TYPE_UNAVAILABLE: stream.get_flag(Flag.IDENTITY).remove_presence(presence.from); received_unavailable(stream, presence); break; case Presence.Stanza.TYPE_SUBSCRIBE: received_subscription_request(stream, presence.from); break; case Presence.Stanza.TYPE_SUBSCRIBED: received_subscription_approval(stream, presence.from); break; case Presence.Stanza.TYPE_UNSUBSCRIBE: stream.get_flag(Flag.IDENTITY).remove_presence(presence.from); received_unsubscription(stream, presence.from); break; case Presence.Stanza.TYPE_UNSUBSCRIBED: break; } } private void on_stream_negotiated(XmppStream stream) { if (available_resource) { Presence.Stanza presence = new Presence.Stanza(); send_presence(stream, presence); initial_presence_sent(stream, presence); } } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/presence/stanza.vala0000664000000000000000000000566414776241610021122 0ustar rootrootnamespace Xmpp.Presence { public class Stanza : Xmpp.Stanza { public const string NODE_PRIORITY = "priority"; public const string NODE_STATUS = "status"; public const string NODE_SHOW = "show"; public const string SHOW_ONLINE = "online"; public const string SHOW_AWAY = "away"; public const string SHOW_CHAT = "chat"; public const string SHOW_DND = "dnd"; public const string SHOW_XA = "xa"; public const string TYPE_AVAILABLE = "available"; public const string TYPE_PROBE = "probe"; public const string TYPE_SUBSCRIBE = "subscribe"; public const string TYPE_SUBSCRIBED = "subscribed"; public const string TYPE_UNAVAILABLE = "unavailable"; public const string TYPE_UNSUBSCRIBE = "unsubscribe"; public const string TYPE_UNSUBSCRIBED = "unsubscribed"; public int priority { get { StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); if (priority_node == null) { return 0; } else { return int.parse(priority_node.get_string_content()); } } set { StanzaNode? priority_node = stanza.get_subnode(NODE_PRIORITY); if (priority_node == null) { priority_node = new StanzaNode.build(NODE_PRIORITY); stanza.put_node(priority_node); } priority_node.val = value.to_string(); } } public string? status { get { StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); return status_node != null ? status_node.get_string_content() : null; } set { StanzaNode? status_node = stanza.get_subnode(NODE_STATUS); if (status_node == null) { status_node = new StanzaNode.build(NODE_STATUS); stanza.put_node(status_node); } status_node.val = value; } } public string show { get { StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); if (show_node == null) { return SHOW_ONLINE; } return show_node.get_string_content() ?? SHOW_ONLINE; } set { if (value != SHOW_ONLINE) { StanzaNode? show_node = stanza.get_subnode(NODE_SHOW); if (show_node == null) { show_node = new StanzaNode.build(NODE_SHOW); stanza.put_node(show_node); } show_node.val = value; } } } public override string? type_ { get { return base.type_ ?? TYPE_AVAILABLE; } set { base.type_ = value; } } public Stanza(string? id = null) { stanza = new StanzaNode.build("presence"); this.id = id ?? random_uuid(); } public Stanza.from_stanza(StanzaNode stanza_node, Jid my_jid) { base.incoming(stanza_node, my_jid); } } } dino-0.5.0/xmpp-vala/src/module/roster/0000775000000000000000000000000014776241610016454 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/roster/flag.vala0000664000000000000000000000110414776241610020226 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class Flag : XmppStreamFlag { public const string ID = "roster"; public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, ID); public HashMap roster_items = new HashMap(); public string? iq_id; public Collection get_roster() { return roster_items.values; } public Item? get_item(Jid jid) { return roster_items[jid]; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } }dino-0.5.0/xmpp-vala/src/module/roster/item.vala0000664000000000000000000000335014776241610020260 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class Item { public const string SUBSCRIPTION_NONE = "none"; /** the user has a subscription to the contact's presence, but the contact does not have a subscription to the user's presence */ public const string SUBSCRIPTION_TO = "to"; /** the contact has a subscription to the user's presence, but the user does not have a subscription to the contact's presence */ public const string SUBSCRIPTION_FROM = "from"; public const string SUBSCRIPTION_BOTH = "both"; public const string SUBSCRIPTION_REMOVE = "remove"; public StanzaNode stanza_node; private Jid jid_; public Jid? jid { get { try { return jid_ ?? (jid_ = new Jid(stanza_node.get_attribute("jid"))); } catch (InvalidJidError e) { warning("Ignoring invalid Jid in roster entry: %s", e.message); return null; } } set { stanza_node.set_attribute("jid", value.to_string()); } } public string? name { get { return stanza_node.get_attribute("name"); } set { if (value != null) stanza_node.set_attribute("name", value); } } public string? subscription { get { return stanza_node.get_attribute("subscription"); } set { if (value != null) stanza_node.set_attribute("subscription", value); } } public string? ask { get { return stanza_node.get_attribute("ask"); } } public bool subscription_requested { get { return this.ask != null; } } public Item() { stanza_node = new StanzaNode.build("item", NS_URI); } public Item.from_stanza_node(StanzaNode stanza_node) { this.stanza_node = stanza_node; } } } dino-0.5.0/xmpp-vala/src/module/roster/module.vala0000664000000000000000000001124214776241610020606 0ustar rootrootusing Gee; namespace Xmpp.Roster { private const string NS_URI = "jabber:iq:roster"; public class Module : XmppStreamModule, Iq.Handler { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "roster_module"); public signal void received_roster(XmppStream stream, Collection roster, Iq.Stanza stanza); public signal void pre_get_roster(XmppStream stream, Iq.Stanza iq); public signal void item_removed(XmppStream stream, Item item, Iq.Stanza iq); public signal void item_updated(XmppStream stream, Item item, Iq.Stanza iq); public signal void mutual_subscription(XmppStream stream, Jid jid); public bool interested_resource = true; public void add_jid(XmppStream stream, Jid jid, string? handle = null) { Item roster_item = new Item(); roster_item.jid = jid; if (handle != null) { roster_item.name = handle; } roster_set(stream, roster_item); } public void remove_jid(XmppStream stream, Jid jid) { Item roster_item = new Item(); roster_item.jid = jid; roster_item.subscription = Item.SUBSCRIPTION_REMOVE; roster_set(stream, roster_item); } /** * Set a handle for a jid * @param handle Handle to be set. If null, any handle will be removed. */ public void set_jid_handle(XmppStream stream, Jid jid, string? handle) { Flag flag = stream.get_flag(Flag.IDENTITY); Item item = flag.get_item(jid) ?? new Item() { jid=jid }; item.name = handle != null ? handle : ""; roster_set(stream, item); } public async void on_iq_set(XmppStream stream, Iq.Stanza iq) { StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); if (query_node == null) return; if (!iq.from.equals(stream.get_flag(Bind.Flag.IDENTITY).my_jid.bare_jid)) { warning("Received alleged roster push from %s, ignoring", iq.from.to_string()); return; } Flag flag = stream.get_flag(Flag.IDENTITY); Item item = new Item.from_stanza_node(query_node.get_subnode("item", NS_URI)); switch (item.subscription) { case Item.SUBSCRIPTION_REMOVE: flag.roster_items.unset(item.jid); item_removed(stream, item, iq); break; default: bool is_new = false; Item old = flag.get_item(item.jid); is_new = item.subscription == Item.SUBSCRIPTION_BOTH && (old == null || old.subscription == Item.SUBSCRIPTION_BOTH); flag.roster_items[item.jid] = item; item_updated(stream, item, iq); if(is_new) mutual_subscription(stream, item.jid); break; } } public override void attach(XmppStream stream) { stream.get_module(Iq.Module.IDENTITY).register_for_namespace(NS_URI, this); stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.connect(roster_get); stream.add_flag(new Flag()); } public override void detach(XmppStream stream) { stream.get_module(Presence.Module.IDENTITY).initial_presence_sent.disconnect(roster_get); } internal override string get_ns() { return NS_URI; } internal override string get_id() { return IDENTITY.id; } private void roster_get(XmppStream stream) { stream.get_flag(Flag.IDENTITY).iq_id = random_uuid(); StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns(); Iq.Stanza iq = new Iq.Stanza.get(query_node, stream.get_flag(Flag.IDENTITY).iq_id); pre_get_roster(stream, iq); stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq, on_roster_get_received); } private static void on_roster_get_received(XmppStream stream, Iq.Stanza iq) { Flag flag = stream.get_flag(Flag.IDENTITY); if (iq.id == flag.iq_id) { StanzaNode? query_node = iq.stanza.get_subnode("query", NS_URI); if (query_node != null) { foreach (StanzaNode item_node in query_node.sub_nodes) { Item item = new Item.from_stanza_node(item_node); flag.roster_items[item.jid] = item; } } stream.get_module(Module.IDENTITY).received_roster(stream, flag.roster_items.values, iq); } } private void roster_set(XmppStream stream, Item roster_item) { StanzaNode query_node = new StanzaNode.build("query", NS_URI).add_self_xmlns() .put_node(roster_item.stanza_node); Iq.Stanza iq = new Iq.Stanza.set(query_node); stream.get_module(Iq.Module.IDENTITY).send_iq(stream, iq); } } } dino-0.5.0/xmpp-vala/src/module/roster/versioning_module.vala0000664000000000000000000000544214776241610023056 0ustar rootrootusing Gee; namespace Xmpp.Roster { public class VersioningModule : XmppStreamModule { private const string NS_URI_FEATURE = "urn:xmpp:features:rosterver"; public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "roster_versioning"); private Storage storage; public VersioningModule(Storage storage) { this.storage = storage; } public override void attach(XmppStream stream) { stream.get_module(Module.IDENTITY).pre_get_roster.connect(on_pre_get_roster); stream.get_module(Module.IDENTITY).received_roster.connect(on_received_roster); stream.get_module(Module.IDENTITY).item_updated.connect(on_item_updated); stream.get_module(Module.IDENTITY).item_removed.connect(on_item_removed); } public override void detach(XmppStream stream) { stream.get_module(Module.IDENTITY).pre_get_roster.disconnect(on_pre_get_roster); } internal override string get_ns() { return NS_URI; } internal override string get_id() { return IDENTITY.id; } private void on_pre_get_roster(XmppStream stream, Iq.Stanza iq) { StanzaNode? ver_feature = stream.features.get_subnode("ver", NS_URI_FEATURE); if (ver_feature != null) { iq.stanza.get_subnode("query", NS_URI).set_attribute("ver", storage.get_roster_version() ?? ""); } } private void on_received_roster(XmppStream stream, Collection roster, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); if (iq.stanza.get_subnode("query", NS_URI) != null) { storage.set_roster(roster); } else { Flag flag = stream.get_flag(Flag.IDENTITY); foreach (Item item in storage.get_roster()) { flag.roster_items[item.jid] = item; } } } private void on_item_updated(XmppStream stream, Item item, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); storage.set_item(item); } private void on_item_removed(XmppStream stream, Item item, Iq.Stanza iq) { string? ver = iq.stanza.get_deep_attribute(NS_URI + ":query", NS_URI + ":ver"); if (ver != null) storage.set_roster_version(ver); storage.remove_item(item); } } public interface Storage : Object { public abstract string? get_roster_version(); public abstract Collection get_roster(); public abstract void set_roster_version(string version); public abstract void set_roster(Collection items); public abstract void set_item(Roster.Item item); public abstract void remove_item(Roster.Item item); } } dino-0.5.0/xmpp-vala/src/module/sasl.vala0000664000000000000000000002564114776241610016755 0ustar rootrootnamespace Xmpp.Sasl { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-sasl"; public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "sasl"); public string mechanism; public string name; public string password; public string client_nonce; public uint8[] server_signature; public bool finished = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } namespace Mechanism { public const string PLAIN = "PLAIN"; public const string SCRAM_SHA_1 = "SCRAM-SHA-1"; public const string SCRAM_SHA_1_PLUS = "SCRAM-SHA-1-PLUS"; } public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "sasl"); public string name { get; set; } public string password { get; set; } public bool use_full_name = false; public signal void received_auth_failure(XmppStream stream, StanzaNode node); public Module(string name, string password) { this.name = name; this.password = password; } public override void attach(XmppStream stream) { stream.received_features_node.connect(this.received_features_node); stream.received_nonza.connect(this.received_nonza); } public override void detach(XmppStream stream) { stream.received_features_node.disconnect(this.received_features_node); stream.received_nonza.disconnect(this.received_nonza); } private static size_t SHA1_SIZE = 20; private static uint8[] sha1(uint8[] data) { Checksum checksum = new Checksum(ChecksumType.SHA1); checksum.update(data, data.length); uint8[] res = new uint8[SHA1_SIZE]; checksum.get_digest(res, ref SHA1_SIZE); return res; } private static uint8[] hmac_sha1(uint8[] key, uint8[] data) { Hmac hmac = new Hmac(ChecksumType.SHA1, key); hmac.update(data); uint8[] res = new uint8[SHA1_SIZE]; hmac.get_digest(res, ref SHA1_SIZE); return res; } private static uint8[] pbkdf2_sha1(string password, uint8[] salt, uint iterations) { uint8[] res = new uint8[SHA1_SIZE]; uint8[] last = new uint8[salt.length + 4]; for(int i = 0; i < salt.length; i++) { last[i] = salt[i]; } last[salt.length + 3] = 1; for(int i = 0; i < iterations; i++) { last = hmac_sha1((uint8[]) password.to_utf8(), last); xor_inplace(res, last); } return res; } private static void xor_inplace(uint8[] mix, uint8[] a2) { for(int i = 0; i < mix.length; i++) { mix[i] = mix[i] ^ a2[i]; } } private static uint8[] xor(uint8[] a1, uint8[] a2) { uint8[] mix = new uint8[a1.length]; for(int i = 0; i < a1.length; i++) { mix[i] = a1[i] ^ a2[i]; } return mix; } public void received_nonza(XmppStream stream, StanzaNode node) { if (node.ns_uri == NS_URI) { if (node.name == "success") { Flag flag = stream.get_flag(Flag.IDENTITY); if (flag.mechanism == Mechanism.SCRAM_SHA_1) { string confirm = (string) Base64.decode(node.get_string_content()); uint8[] server_signature = null; foreach(string c in confirm.split(",")) { string[] split = c.split("=", 2); if (split.length != 2) continue; switch(split[0]) { case "v": server_signature = Base64.decode(split[1]); break; } } if (server_signature == null) return; if (server_signature.length != flag.server_signature.length) return; for(int i = 0; i < server_signature.length; i++) { if (server_signature[i] != flag.server_signature[i]) return; } } stream.require_setup(); flag.password = null; // Remove password from memory flag.finished = true; } else if (node.name == "failure") { stream.remove_flag(stream.get_flag(Flag.IDENTITY)); received_auth_failure(stream, node); } else if (node.name == "challenge" && stream.has_flag(Flag.IDENTITY)) { Flag flag = stream.get_flag(Flag.IDENTITY); if (flag.mechanism == Mechanism.SCRAM_SHA_1) { string challenge = (string) Base64.decode(node.get_string_content()); string? server_nonce = null; uint8[] salt = null; uint iterations = 0; foreach(string c in challenge.split(",")) { string[] split = c.split("=", 2); if (split.length != 2) continue; switch(split[0]) { case "r": server_nonce = split[1]; break; case "s": salt = Base64.decode(split[1]); break; case "i": iterations = int.parse(split[1]); break; } } if (server_nonce == null || salt == null || iterations == 0) return; if (!server_nonce.has_prefix(flag.client_nonce)) return; string client_final_message_bare = @"c=biws,r=$server_nonce"; uint8[] salted_password = pbkdf2_sha1(flag.password, salt, iterations); uint8[] client_key = hmac_sha1(salted_password, (uint8[]) "Client Key".to_utf8()); uint8[] stored_key = sha1(client_key); string auth_message = @"n=$(flag.name),r=$(flag.client_nonce),$challenge,$client_final_message_bare"; uint8[] client_signature = hmac_sha1(stored_key, (uint8[]) auth_message.to_utf8()); uint8[] client_proof = xor(client_key, client_signature); uint8[] server_key = hmac_sha1(salted_password, (uint8[]) "Server Key".to_utf8()); flag.server_signature = hmac_sha1(server_key, (uint8[]) auth_message.to_utf8()); string client_final_message = @"$client_final_message_bare,p=$(Base64.encode(client_proof))"; stream.write(new StanzaNode.build("response", NS_URI).add_self_xmlns() .put_node(new StanzaNode.text(Base64.encode((uchar[]) (client_final_message).to_utf8())))); } } } } public void received_features_node(XmppStream stream) { if (stream.has_flag(Flag.IDENTITY)) return; if (stream.is_setup_needed()) return; var mechanisms = stream.features.get_subnode("mechanisms", NS_URI); string[] supported_mechanisms = {}; foreach (var mechanism in mechanisms.sub_nodes) { if (mechanism.name != "mechanism" || mechanism.ns_uri != NS_URI) continue; supported_mechanisms += mechanism.get_string_content(); } if (!name.contains("@")) { name = "%s@%s".printf(name, stream.remote_name.to_string()); } if (!use_full_name && name.contains("@")) { var split = name.split("@"); if (split[1] == stream.remote_name.to_string()) { name = split[0]; } else { use_full_name = true; } } string name = this.name; if (!use_full_name && name.contains("@")) { var split = name.split("@"); if (split[1] == stream.remote_name.to_string()) { name = split[0]; } } if (Mechanism.SCRAM_SHA_1 in supported_mechanisms) { string normalized_password = password.normalize(-1, NormalizeMode.NFKC); string client_nonce = Random.next_int().to_string("%.8x") + Random.next_int().to_string("%.8x") + Random.next_int().to_string("%.8x"); string initial_message = @"n=$name,r=$client_nonce"; stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() .put_attribute("mechanism", Mechanism.SCRAM_SHA_1) .put_node(new StanzaNode.text(Base64.encode((uchar[]) ("n,,"+initial_message).to_utf8())))); var flag = new Flag(); flag.mechanism = Mechanism.SCRAM_SHA_1; flag.name = name; flag.password = normalized_password; flag.client_nonce = client_nonce; stream.add_flag(flag); } else if (Mechanism.PLAIN in supported_mechanisms) { stream.write(new StanzaNode.build("auth", NS_URI).add_self_xmlns() .put_attribute("mechanism", Mechanism.PLAIN) .put_node(new StanzaNode.text(Base64.encode(get_plain_bytes(name, password))))); var flag = new Flag(); flag.mechanism = Mechanism.PLAIN; flag.name = name; stream.add_flag(flag); } else { stderr.printf("No supported mechanism provided by server at %s\n", stream.remote_name.to_string()); return; } } private static uchar[] get_plain_bytes(string name_s, string password_s) { var name = name_s.to_utf8(); var password = password_s.to_utf8(); uchar[] res = new uchar[name.length + password.length + 2]; res[0] = 0; res[name.length + 1] = 0; for(int i = 0; i < name.length; i++) { res[i + 1] = (uchar) name[i]; } for(int i = 0; i < password.length; i++) { res[i + name.length + 2] = (uchar) password[i]; } return res; } public override bool mandatory_outstanding(XmppStream stream) { return !stream.has_flag(Flag.IDENTITY) || !stream.get_flag(Flag.IDENTITY).finished; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/session.vala0000664000000000000000000000354514776241610017475 0ustar rootroot/* Legacy. RFC 3921 3*/ namespace Xmpp.Session { private const string NS_URI = "urn:ietf:params:xml:ns:xmpp-session"; public class Module : XmppStreamNegotiationModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "session"); public override void attach(XmppStream stream) { stream.get_module(Bind.Module.IDENTITY).bound_to_resource.connect(on_bound_resource); } public override void detach(XmppStream stream) { stream.get_module(Bind.Module.IDENTITY).bound_to_resource.disconnect(on_bound_resource); } public override bool mandatory_outstanding(XmppStream stream) { return false; } public override bool negotiation_active(XmppStream stream) { return stream.has_flag(Flag.IDENTITY) && !stream.get_flag(Flag.IDENTITY).finished; } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private async void on_bound_resource(XmppStream stream, Jid my_jid) { StanzaNode? session_node = stream.features.get_subnode("session", NS_URI); if (session_node != null && session_node.get_subnode("optional", NS_URI) == null) { stream.add_flag(new Flag()); Iq.Stanza iq = new Iq.Stanza.set(new StanzaNode.build("session", NS_URI).add_self_xmlns()) { to=stream.remote_name }; Iq.Stanza result_iq = yield stream.get_module(Iq.Module.IDENTITY).send_iq_async(stream, iq); if (!result_iq.is_error()) { stream.get_flag(Flag.IDENTITY).finished = true; } } } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "session"); public bool finished = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/stanza.vala0000664000000000000000000000477714776241610017322 0ustar rootrootnamespace Xmpp { public class Stanza : Object { public const string ATTRIBUTE_FROM = "from"; public const string ATTRIBUTE_ID = "id"; public const string ATTRIBUTE_TO = "to"; public const string ATTRIBUTE_TYPE = "type"; public const string TYPE_ERROR = "error"; private Jid? my_jid; private Jid? from_; private Jid? to_; public virtual Jid? from { owned get { string? from_attribute = stanza.get_attribute(ATTRIBUTE_FROM); // "when a client receives a stanza that does not include a 'from' attribute, it MUST assume that the stanza // is from the user's account on the server." (RFC6120 8.1.2.1) if (from_attribute != null) { try { return from_ = new Jid(from_attribute); } catch (InvalidJidError e) { warning("Ignoring invalid from Jid: %s", e.message); } } if (my_jid != null) { return my_jid.bare_jid; } return null; } set { stanza.set_attribute(ATTRIBUTE_FROM, value.to_string()); } } public virtual string? id { get { return stanza.get_attribute(ATTRIBUTE_ID); } set { stanza.set_attribute(ATTRIBUTE_ID, value); } } public virtual Jid? to { owned get { string? to_attribute = stanza.get_attribute(ATTRIBUTE_TO); // "if the stanza does not include a 'to' address then the client MUST treat it as if the 'to' address were // included with a value of the client's full JID." (RFC6120 8.1.1.1) try { return to_attribute == null ? my_jid : to_ = new Jid(to_attribute); } catch (InvalidJidError e) { warning("Ignoring invalid to Jid: %s", e.message); } return my_jid; } set { stanza.set_attribute(ATTRIBUTE_TO, value.to_string()); } } public virtual string? type_ { get { return stanza.get_attribute(ATTRIBUTE_TYPE); } set { stanza.set_attribute(ATTRIBUTE_TYPE, value); } } public StanzaNode stanza; public Stanza.incoming(StanzaNode stanza, Jid? my_jid) { this.stanza = stanza; this.my_jid = my_jid; } public Stanza.outgoing(StanzaNode stanza) { this.stanza = stanza; } public bool is_error() { return type_ == TYPE_ERROR; } public ErrorStanza? get_error() { return ErrorStanza.from_stanza(this.stanza); } } }dino-0.5.0/xmpp-vala/src/module/stanza_error.vala0000664000000000000000000001203214776241610020512 0ustar rootrootusing Gee; namespace Xmpp { private const string ERROR_NS_URI = "urn:ietf:params:xml:ns:xmpp-stanzas"; public class ErrorStanza { public const string CONDITION_BAD_REQUEST = "bad-request"; public const string CONDITION_CONFLICT = "conflict"; public const string CONDITION_FEATURE_NOT_IMPLEMENTED = "feature-not-implemented"; public const string CONDITION_FORBIDDEN = "forbidden"; public const string CONDITION_GONE = "gone"; public const string CONDITION_INTERNAL_SERVER_ERROR = "internal-server-error"; public const string CONDITION_ITEM_NOT_FOUND = "item-not-found"; public const string CONDITION_JID_MALFORMED = "jid-malformed"; public const string CONDITION_NOT_ACCEPTABLE = "not-acceptable"; public const string CONDITION_NOT_ALLOWED = "not-allowed"; public const string CONDITION_NOT_AUTHORIZED = "not-authorized"; public const string CONDITION_POLICY_VIOLATION = "policy-violation"; public const string CONDITION_RECIPIENT_UNAVAILABLE = "recipient-unavailable"; public const string CONDITION_REDIRECT = "redirect"; public const string CONDITION_REGISTRATION_REQUIRED = "registration-required"; public const string CONDITION_REMOTE_SERVER_NOT_FOUND = "remote-server-not-found"; public const string CONDITION_REMOTE_SERVER_TIMEOUT = "remote-server-timeout"; public const string CONDITION_RESOURCE_CONSTRAINT = "resource-constraint"; public const string CONDITION_SERVICE_UNAVAILABLE = "service-unavailable"; public const string CONDITION_SUBSCRIPTION_REQUIRED = "subscription-required"; public const string CONDITION_UNDEFINED_CONDITION = "undefined-condition"; public const string CONDITION_UNEXPECTED_REQUEST = "unexpected-request"; public const string TYPE_AUTH = "auth"; public const string TYPE_CANCEL = "cancel"; public const string TYPE_CONTINUE = "continue"; public const string TYPE_MODIFY = "modify"; public const string TYPE_WAIT = "wait"; public string? by { get { return error_node.get_attribute("by"); } } public string? text { get { return error_node.get_deep_string_content(ERROR_NS_URI + ":text"); } } public string condition { get { Gee.List subnodes = error_node.sub_nodes; foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns if (subnode.ns_uri == ERROR_NS_URI) { return subnode.name; } } return CONDITION_UNDEFINED_CONDITION; // TODO hm! } } public string type_ { get { return error_node.get_attribute("type"); } } public StanzaNode error_node; public static ErrorStanza? from_stanza(StanzaNode stanza) { var error_stanza = new ErrorStanza(); error_stanza.error_node = stanza.get_subnode("error"); if (error_stanza.error_node == null) return null; return error_stanza; } public ErrorStanza.build(string type, string condition, string? human_readable, StanzaNode? application_condition) { error_node = new StanzaNode.build("error") .put_attribute("type", type) .put_node(new StanzaNode.build(condition, ERROR_NS_URI).add_self_xmlns()); if (application_condition != null) { error_node.put_node(application_condition); } if (human_readable != null) { error_node.put_node(new StanzaNode.build("text", ERROR_NS_URI) .add_self_xmlns() .put_attribute("xml:lang", "en") .put_node(new StanzaNode.text(human_readable)) ); } } public ErrorStanza.bad_request(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_BAD_REQUEST, human_readable, null); } public ErrorStanza.feature_not_implemented(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_FEATURE_NOT_IMPLEMENTED, human_readable, null); } public ErrorStanza.item_not_found(StanzaNode? application_condition = null) { this.build(TYPE_CANCEL, CONDITION_ITEM_NOT_FOUND, null, application_condition); } public ErrorStanza.not_acceptable(string? human_readable = null) { this.build(TYPE_MODIFY, CONDITION_NOT_ACCEPTABLE, human_readable, null); } public ErrorStanza.not_allowed(string? human_readable = null) { this.build(TYPE_CANCEL, CONDITION_NOT_ALLOWED, human_readable, null); } public ErrorStanza.resource_constraint(string? human_readable = null) { this.build(TYPE_WAIT, CONDITION_RESOURCE_CONSTRAINT, human_readable, null); } public ErrorStanza.service_unavailable() { this.build(TYPE_CANCEL, CONDITION_SERVICE_UNAVAILABLE, null, null); } } } dino-0.5.0/xmpp-vala/src/module/stream_error.vala0000664000000000000000000001013214776241610020504 0ustar rootrootusing Gee; namespace Xmpp.StreamError { private const string NS_URI = "jabber:client"; private const string NS_ERROR = "urn:ietf:params:xml:ns:xmpp-streams"; public class Module : XmppStreamModule { public static ModuleIdentity IDENTITY = new ModuleIdentity(NS_URI, "stream_error_module"); public override void attach(XmppStream stream) { stream.received_nonza.connect(on_received_nonstanza); } public override void detach(XmppStream stream) { stream.received_nonza.disconnect(on_received_nonstanza); } public static void require(XmppStream stream) { if (stream.get_module(IDENTITY) == null) stream.add_module(new Module()); } public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } private void on_received_nonstanza(XmppStream stream, StanzaNode node) { if (node.name == "error" && node.ns_uri == "http://etherx.jabber.org/streams") { stream.add_flag(generate_error_flag(node)); } } private Flag generate_error_flag(StanzaNode node) { string? subnode_name = null; Gee.List subnodes = node.sub_nodes; foreach (StanzaNode subnode in subnodes) { // TODO get subnode by ns if (subnode.ns_uri == "urn:ietf:params:xml:ns:xmpp-streams" && subnode.name != "text") { subnode_name = subnode.name; } } Flag flag = new StreamError.Flag(); flag.error_type = subnode_name; switch (subnode_name) { case "bad-format": case "conflict": case "connection-timeout": case "bad-namespace-prefix": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "host-gone": case "host-unknown": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "improper-addressing": case "internal-server-error": case "invalid-from": case "invalid-namespace": case "invalid-xml": case "not-authorized": case "not-well-formed": case "policy-violation": case "remote-connection-failed": case "reset": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "resource-constraint": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "restricted-xml": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; case "see-other-host": case "system-shutdown": flag.reconnection_recomendation = StreamError.Flag.Reconnect.LATER; break; case "undefined-condition": case "unsupported-encoding": case "unsupported-feature": case "unsupported-stanza-type": case "unsupported-version": flag.reconnection_recomendation = StreamError.Flag.Reconnect.NOW; break; } if (subnode_name == "conflict") flag.resource_rejected = true; return flag; } } public class Flag : XmppStreamFlag { public static FlagIdentity IDENTITY = new FlagIdentity(NS_URI, "stream_error"); public enum Reconnect { UNKNOWN, NOW, LATER, NEVER } public string? error_type; public Reconnect reconnection_recomendation = Reconnect.UNKNOWN; public bool resource_rejected = false; public override string get_ns() { return NS_URI; } public override string get_id() { return IDENTITY.id; } } } dino-0.5.0/xmpp-vala/src/module/util.vala0000664000000000000000000000523714776241610016767 0ustar rootrootusing Gee; namespace Xmpp { public string random_uuid() { uint32 b1 = Random.next_int(); uint16 b2 = (uint16)Random.next_int(); uint16 b3 = (uint16)(Random.next_int() | 0x4000u) & ~0xb000u; uint16 b4 = (uint16)(Random.next_int() | 0x8000u) & ~0x4000u; uint16 b5_1 = (uint16)Random.next_int(); uint32 b5_2 = Random.next_int(); return "%08x-%04x-%04x-%04x-%04x%08x".printf(b1, b2, b3, b4, b5_1, b5_2); } public abstract class StanzaListener : OrderedListener { public abstract async bool run(XmppStream stream, T stanza); } public class StanzaListenerHolder : ListenerHolder { public async bool run(XmppStream stream, T stanza) { // listeners can change e.g. when switching to another stream ArrayList listeners_copy = new ArrayList(); listeners_copy.add_all(listeners); foreach (OrderedListener ol in listeners_copy) { StanzaListener l = ol as StanzaListener; bool stop = yield l.run(stream, stanza); if (stop) return true; } return false; } } public abstract class OrderedListener : Object { public abstract string action_group { get; } public abstract string[] after_actions { get; } } public abstract class ListenerHolder : Object { protected ArrayList listeners = new ArrayList(); public new void connect(OrderedListener listener) { listeners.add(listener); resort_list(); } public new void disconnect(OrderedListener listener) { listeners.remove(listener); resort_list(); } private bool set_contains_action(Gee.List s, string[] actions) { foreach(OrderedListener l in s) { if (l.action_group in actions) { return true; } } return false; } private void resort_list() { ArrayList new_list = new ArrayList(); ArrayList remaining = new ArrayList(); remaining.add_all(listeners); while (remaining.size > 0) { bool changed = false; Gee.Iterator iter = remaining.iterator(); while (iter.has_next()) { iter.next(); OrderedListener l = iter.get(); if (!set_contains_action(remaining, l.after_actions)) { new_list.add(l); iter.remove(); changed = true; } } if (!changed) error("Can't sort listeners"); } listeners = new_list; } } } dino-0.5.0/xmpp-vala/src/module/xep/0000775000000000000000000000000014776241610015732 5ustar rootrootdino-0.5.0/xmpp-vala/src/module/xep/0004_data_forms.vala0000664000000000000000000001735414776241610021373 0ustar rootrootusing Gee; namespace Xmpp.Xep.DataForms { public const string NS_URI = "jabber:x:data"; public class DataForm { public StanzaNode stanza_node { get; set; } public Gee.List fields = new ArrayList(); public string? form_type = null; public string? instructions = null; public string? title = null; public StanzaNode get_submit_node() { stanza_node.set_attribute("type", "submit"); return stanza_node; } public enum Type { BOOLEAN, FIXED, HIDDEN, JID_MULTI, LIST_SINGLE, LIST_MULTI, TEXT_PRIVATE, TEXT_SINGLE, } public class Option { public string label { get; set; } public string value { get; set; } public Option(string label, string value) { this.label = label; this.value = value; } } public class Field : Object { public StanzaNode node { get; set; } public string? label { get { return node.get_attribute("label", NS_URI); } set { node.set_attribute("label", value); } } public virtual Type? type_ { get; internal set; default=null; } public string? var { get { return node.get_attribute("var", NS_URI); } set { node.set_attribute("var", value); } } public Field() { this.node = new StanzaNode.build("field", NS_URI); } public Field.from_node(StanzaNode node) { this.node = node; } internal Gee.List get_values() { Gee.List ret = new ArrayList(); Gee.List value_nodes = node.get_subnodes("value", NS_URI); foreach (StanzaNode node in value_nodes) { ret.add(node.get_string_content()); } return ret; } public string get_value_string() { Gee.List values = get_values(); return values.size > 0 ? values[0] : ""; } public void set_value_string(string val) { StanzaNode? value_node = node.get_subnode("value", NS_URI); if (value_node == null) { value_node = new StanzaNode.build("value", NS_URI); node.put_node(value_node); } value_node.sub_nodes.clear(); value_node.put_node(new StanzaNode.text(val)); } internal void add_value_string(string val) { StanzaNode node = new StanzaNode.build("value"); node.put_node(new StanzaNode.text(val)); } internal Gee.List