One Hat Cyber Team
Your IP:
216.73.216.162
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
self
/
cwd
/
Edit File:
translation.tar
Resources/schemas/xml.xsd 0000644 00000021220 15111175534 0011461 0 ustar 00 <?xml version='1.0'?> <?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?> <xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns ="http://www.w3.org/1999/xhtml" xml:lang="en"> <xs:annotation> <xs:documentation> <div> <h1>About the XML namespace</h1> <div class="bodytext"> <p> This schema document describes the XML namespace, in a form suitable for import by other schema documents. </p> <p> See <a href="http://www.w3.org/XML/1998/namespace.html"> http://www.w3.org/XML/1998/namespace.html</a> and <a href="http://www.w3.org/TR/REC-xml"> http://www.w3.org/TR/REC-xml</a> for information about this namespace. </p> <p> Note that local names in this namespace are intended to be defined only by the World Wide Web Consortium or its subgroups. The names currently defined in this namespace are listed below. They should not be used with conflicting semantics by any Working Group, specification, or document instance. </p> <p> See further below in this document for more information about <a href="#usage">how to refer to this schema document from your own XSD schema documents</a> and about <a href="#nsversioning">the namespace-versioning policy governing this schema document</a>. </p> </div> </div> </xs:documentation> </xs:annotation> <xs:attribute name="lang"> <xs:annotation> <xs:documentation> <div> <h3>lang (as an attribute name)</h3> <p> denotes an attribute whose value is a language code for the natural language of the content of any element; its value is inherited. This name is reserved by virtue of its definition in the XML specification.</p> </div> <div> <h4>Notes</h4> <p> Attempting to install the relevant ISO 2- and 3-letter codes as the enumerated possible values is probably never going to be a realistic possibility. </p> <p> See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt"> http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a> and the IANA language subtag registry at <a href="http://www.iana.org/assignments/language-subtag-registry"> http://www.iana.org/assignments/language-subtag-registry</a> for further information. </p> <p> The union allows for the 'un-declaration' of xml:lang with the empty string. </p> </div> </xs:documentation> </xs:annotation> <xs:simpleType> <xs:union memberTypes="xs:language"> <xs:simpleType> <xs:restriction base="xs:string"> <xs:enumeration value=""/> </xs:restriction> </xs:simpleType> </xs:union> </xs:simpleType> </xs:attribute> <xs:attribute name="space"> <xs:annotation> <xs:documentation> <div> <h3>space (as an attribute name)</h3> <p> denotes an attribute whose value is a keyword indicating what whitespace processing discipline is intended for the content of the element; its value is inherited. This name is reserved by virtue of its definition in the XML specification.</p> </div> </xs:documentation> </xs:annotation> <xs:simpleType> <xs:restriction base="xs:NCName"> <xs:enumeration value="default"/> <xs:enumeration value="preserve"/> </xs:restriction> </xs:simpleType> </xs:attribute> <xs:attribute name="base" type="xs:anyURI"> <xs:annotation> <xs:documentation> <div> <h3>base (as an attribute name)</h3> <p> denotes an attribute whose value provides a URI to be used as the base for interpreting any relative URIs in the scope of the element on which it appears; its value is inherited. This name is reserved by virtue of its definition in the XML Base specification.</p> <p> See <a href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a> for information about this attribute. </p> </div> </xs:documentation> </xs:annotation> </xs:attribute> <xs:attribute name="id" type="xs:ID"> <xs:annotation> <xs:documentation> <div> <h3>id (as an attribute name)</h3> <p> denotes an attribute whose value should be interpreted as if declared to be of type ID. This name is reserved by virtue of its definition in the xml:id specification.</p> <p> See <a href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a> for information about this attribute. </p> </div> </xs:documentation> </xs:annotation> </xs:attribute> <xs:attributeGroup name="specialAttrs"> <xs:attribute ref="xml:base"/> <xs:attribute ref="xml:lang"/> <xs:attribute ref="xml:space"/> <xs:attribute ref="xml:id"/> </xs:attributeGroup> <xs:annotation> <xs:documentation> <div> <h3>Father (in any context at all)</h3> <div class="bodytext"> <p> denotes Jon Bosak, the chair of the original XML Working Group. This name is reserved by the following decision of the W3C XML Plenary and XML Coordination groups: </p> <blockquote> <p> In appreciation for his vision, leadership and dedication the W3C XML Plenary on this 10th day of February, 2000, reserves for Jon Bosak in perpetuity the XML name "xml:Father". </p> </blockquote> </div> </div> </xs:documentation> </xs:annotation> <xs:annotation> <xs:documentation> <div xml:id="usage" id="usage"> <h2><a name="usage">About this schema document</a></h2> <div class="bodytext"> <p> This schema defines attributes and an attribute group suitable for use by schemas wishing to allow <code>xml:base</code>, <code>xml:lang</code>, <code>xml:space</code> or <code>xml:id</code> attributes on elements they define. </p> <p> To enable this, such a schema must import this schema for the XML namespace, e.g. as follows: </p> <pre> <schema.. .> .. . <import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> </pre> <p> or </p> <pre> <import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2009/01/xml.xsd"/> </pre> <p> Subsequently, qualified reference to any of the attributes or the group defined below will have the desired effect, e.g. </p> <pre> <type.. .> .. . <attributeGroup ref="xml:specialAttrs"/> </pre> <p> will define a type which will schema-validate an instance element with any of those attributes. </p> </div> </div> </xs:documentation> </xs:annotation> <xs:annotation> <xs:documentation> <div id="nsversioning" xml:id="nsversioning"> <h2><a name="nsversioning">Versioning policy for this schema document</a></h2> <div class="bodytext"> <p> In keeping with the XML Schema WG's standard versioning policy, this schema document will persist at <a href="http://www.w3.org/2009/01/xml.xsd"> http://www.w3.org/2009/01/xml.xsd</a>. </p> <p> At the date of issue it can also be found at <a href="http://www.w3.org/2001/xml.xsd"> http://www.w3.org/2001/xml.xsd</a>. </p> <p> The schema document at that URI may however change in the future, in order to remain compatible with the latest version of XML Schema itself, or with the XML namespace itself. In other words, if the XML Schema or XML namespaces change, the version of this document at <a href="http://www.w3.org/2001/xml.xsd"> http://www.w3.org/2001/xml.xsd </a> will change accordingly; the version at <a href="http://www.w3.org/2009/01/xml.xsd"> http://www.w3.org/2009/01/xml.xsd </a> will not change. </p> <p> Previous dated (and unchanging) versions of this schema document are at: </p> <ul> <li><a href="http://www.w3.org/2009/01/xml.xsd"> http://www.w3.org/2009/01/xml.xsd</a></li> <li><a href="http://www.w3.org/2007/08/xml.xsd"> http://www.w3.org/2007/08/xml.xsd</a></li> <li><a href="http://www.w3.org/2004/10/xml.xsd"> http://www.w3.org/2004/10/xml.xsd</a></li> <li><a href="http://www.w3.org/2001/03/xml.xsd"> http://www.w3.org/2001/03/xml.xsd</a></li> </ul> </div> </div> </xs:documentation> </xs:annotation> </xs:schema> Resources/schemas/xliff-core-1.2-transitional.xsd 0000644 00000315625 15111175534 0015741 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <!-- May-19-2004: - Changed the <choice> for ElemType_header, moving minOccurs="0" maxOccurs="unbounded" from its elements to <choice> itself. - Added <choice> for ElemType_trans-unit to allow "any order" for <context-group>, <count-group>, <prop-group>, <note>, and <alt-trans>. Oct-2005 - updated version info to 1.2 - equiv-trans attribute to <trans-unit> element - merged-trans attribute for <group> element - Add the <seg-source> element as optional in the <trans-unit> and <alt-trans> content models, at the same level as <source> - Create a new value "seg" for the mtype attribute of the <mrk> element - Add mid as an optional attribute for the <alt-trans> element Nov-14-2005 - Changed name attribute for <context-group> from required to optional - Added extension point at <xliff> Jan-9-2006 - Added alttranstype type attribute to <alt-trans>, and values Jan-10-2006 - Corrected error with overwritten purposeValueList - Corrected name="AttrType_Version", attribute should have been "name" --> <xsd:schema xmlns:xlf="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" targetNamespace="urn:oasis:names:tc:xliff:document:1.2" xml:lang="en"> <!-- Import for xml:lang and xml:space --> <xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/> <!-- Attributes Lists --> <xsd:simpleType name="XTend"> <xsd:restriction base="xsd:string"> <xsd:pattern value="x-[^\s]+"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="context-typeValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'context-type'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:string"> <xsd:enumeration value="database"> <xsd:annotation> <xsd:documentation>Indicates a database content.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="element"> <xsd:annotation> <xsd:documentation>Indicates the content of an element within an XML document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="elementtitle"> <xsd:annotation> <xsd:documentation>Indicates the name of an element within an XML document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="linenumber"> <xsd:annotation> <xsd:documentation>Indicates the line number from the sourcefile (see context-type="sourcefile") where the <source> is found.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="numparams"> <xsd:annotation> <xsd:documentation>Indicates a the number of parameters contained within the <source>.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="paramnotes"> <xsd:annotation> <xsd:documentation>Indicates notes pertaining to the parameters in the <source>.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="record"> <xsd:annotation> <xsd:documentation>Indicates the content of a record within a database.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="recordtitle"> <xsd:annotation> <xsd:documentation>Indicates the name of a record within a database.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="sourcefile"> <xsd:annotation> <xsd:documentation>Indicates the original source file in the case that multiple files are merged to form the original file from which the XLIFF file is created. This differs from the original <file> attribute in that this sourcefile is one of many that make up that file.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="count-typeValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'count-type'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="num-usages"> <xsd:annotation> <xsd:documentation>Indicates the count units are items that are used X times in a certain context; example: this is a reusable text unit which is used 42 times in other texts.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="repetition"> <xsd:annotation> <xsd:documentation>Indicates the count units are translation units existing already in the same document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="total"> <xsd:annotation> <xsd:documentation>Indicates a total count.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="InlineDelimitersValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'ctype' when used other elements than <ph> or <x>.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="bold"> <xsd:annotation> <xsd:documentation>Indicates a run of bolded text.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="italic"> <xsd:annotation> <xsd:documentation>Indicates a run of text in italics.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="underlined"> <xsd:annotation> <xsd:documentation>Indicates a run of underlined text.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="link"> <xsd:annotation> <xsd:documentation>Indicates a run of hyper-text.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="InlinePlaceholdersValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'ctype' when used with <ph> or <x>.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="image"> <xsd:annotation> <xsd:documentation>Indicates a inline image.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pb"> <xsd:annotation> <xsd:documentation>Indicates a page break.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="lb"> <xsd:annotation> <xsd:documentation>Indicates a line break.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="mime-typeValueList"> <xsd:restriction base="xsd:string"> <xsd:pattern value="(text|multipart|message|application|image|audio|video|model)(/.+)*"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="datatypeValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'datatype'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="asp"> <xsd:annotation> <xsd:documentation>Indicates Active Server Page data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="c"> <xsd:annotation> <xsd:documentation>Indicates C source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cdf"> <xsd:annotation> <xsd:documentation>Indicates Channel Definition Format (CDF) data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cfm"> <xsd:annotation> <xsd:documentation>Indicates ColdFusion data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cpp"> <xsd:annotation> <xsd:documentation>Indicates C++ source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="csharp"> <xsd:annotation> <xsd:documentation>Indicates C-Sharp data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cstring"> <xsd:annotation> <xsd:documentation>Indicates strings from C, ASM, and driver files data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="csv"> <xsd:annotation> <xsd:documentation>Indicates comma-separated values data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="database"> <xsd:annotation> <xsd:documentation>Indicates database data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="documentfooter"> <xsd:annotation> <xsd:documentation>Indicates portions of document that follows data and contains metadata.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="documentheader"> <xsd:annotation> <xsd:documentation>Indicates portions of document that precedes data and contains metadata.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="filedialog"> <xsd:annotation> <xsd:documentation>Indicates data from standard UI file operations dialogs (e.g., Open, Save, Save As, Export, Import).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="form"> <xsd:annotation> <xsd:documentation>Indicates standard user input screen data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="html"> <xsd:annotation> <xsd:documentation>Indicates HyperText Markup Language (HTML) data - document instance.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="htmlbody"> <xsd:annotation> <xsd:documentation>Indicates content within an HTML document’s <body> element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="ini"> <xsd:annotation> <xsd:documentation>Indicates Windows INI file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="interleaf"> <xsd:annotation> <xsd:documentation>Indicates Interleaf data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="javaclass"> <xsd:annotation> <xsd:documentation>Indicates Java source file data (extension '.java').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="javapropertyresourcebundle"> <xsd:annotation> <xsd:documentation>Indicates Java property resource bundle data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="javalistresourcebundle"> <xsd:annotation> <xsd:documentation>Indicates Java list resource bundle data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="javascript"> <xsd:annotation> <xsd:documentation>Indicates JavaScript source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="jscript"> <xsd:annotation> <xsd:documentation>Indicates JScript source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="layout"> <xsd:annotation> <xsd:documentation>Indicates information relating to formatting.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="lisp"> <xsd:annotation> <xsd:documentation>Indicates LISP source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="margin"> <xsd:annotation> <xsd:documentation>Indicates information relating to margin formats.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="menufile"> <xsd:annotation> <xsd:documentation>Indicates a file containing menu.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="messagefile"> <xsd:annotation> <xsd:documentation>Indicates numerically identified string table.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="mif"> <xsd:annotation> <xsd:documentation>Indicates Maker Interchange Format (MIF) data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="mimetype"> <xsd:annotation> <xsd:documentation>Indicates that the datatype attribute value is a MIME Type value and is defined in the mime-type attribute.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="mo"> <xsd:annotation> <xsd:documentation>Indicates GNU Machine Object data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="msglib"> <xsd:annotation> <xsd:documentation>Indicates Message Librarian strings created by Novell's Message Librarian Tool.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pagefooter"> <xsd:annotation> <xsd:documentation>Indicates information to be displayed at the bottom of each page of a document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pageheader"> <xsd:annotation> <xsd:documentation>Indicates information to be displayed at the top of each page of a document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="parameters"> <xsd:annotation> <xsd:documentation>Indicates a list of property values (e.g., settings within INI files or preferences dialog).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pascal"> <xsd:annotation> <xsd:documentation>Indicates Pascal source file data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="php"> <xsd:annotation> <xsd:documentation>Indicates Hypertext Preprocessor data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="plaintext"> <xsd:annotation> <xsd:documentation>Indicates plain text file (no formatting other than, possibly, wrapping).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="po"> <xsd:annotation> <xsd:documentation>Indicates GNU Portable Object file.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="report"> <xsd:annotation> <xsd:documentation>Indicates dynamically generated user defined document. e.g. Oracle Report, Crystal Report, etc.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="resources"> <xsd:annotation> <xsd:documentation>Indicates Windows .NET binary resources.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="resx"> <xsd:annotation> <xsd:documentation>Indicates Windows .NET Resources.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rtf"> <xsd:annotation> <xsd:documentation>Indicates Rich Text Format (RTF) data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="sgml"> <xsd:annotation> <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - document instance.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="sgmldtd"> <xsd:annotation> <xsd:documentation>Indicates Standard Generalized Markup Language (SGML) data - Document Type Definition (DTD).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="svg"> <xsd:annotation> <xsd:documentation>Indicates Scalable Vector Graphic (SVG) data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="vbscript"> <xsd:annotation> <xsd:documentation>Indicates VisualBasic Script source file.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="warning"> <xsd:annotation> <xsd:documentation>Indicates warning message.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="winres"> <xsd:annotation> <xsd:documentation>Indicates Windows (Win32) resources (i.e. resources extracted from an RC script, a message file, or a compiled file).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="xhtml"> <xsd:annotation> <xsd:documentation>Indicates Extensible HyperText Markup Language (XHTML) data - document instance.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="xml"> <xsd:annotation> <xsd:documentation>Indicates Extensible Markup Language (XML) data - document instance.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="xmldtd"> <xsd:annotation> <xsd:documentation>Indicates Extensible Markup Language (XML) data - Document Type Definition (DTD).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="xsl"> <xsd:annotation> <xsd:documentation>Indicates Extensible Stylesheet Language (XSL) data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="xul"> <xsd:annotation> <xsd:documentation>Indicates XUL elements.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="mtypeValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'mtype'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="abbrev"> <xsd:annotation> <xsd:documentation>Indicates the marked text is an abbreviation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="abbreviated-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8: A term resulting from the omission of any part of the full term while designating the same concept.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="abbreviation"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8.1: An abbreviated form of a simple term resulting from the omission of some of its letters (e.g. 'adj.' for 'adjective').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="acronym"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8.4: An abbreviated form of a term made up of letters from the full form of a multiword term strung together into a sequence pronounced only syllabically (e.g. 'radar' for 'radio detecting and ranging').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="appellation"> <xsd:annotation> <xsd:documentation>ISO-12620: A proper-name term, such as the name of an agency or other proper entity.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="collocation"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.18.1: A recurrent word combination characterized by cohesion in that the components of the collocation must co-occur within an utterance or series of utterances, even though they do not necessarily have to maintain immediate proximity to one another.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="common-name"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.5: A synonym for an international scientific term that is used in general discourse in a given language.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="datetime"> <xsd:annotation> <xsd:documentation>Indicates the marked text is a date and/or time.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="equation"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.15: An expression used to represent a concept based on a statement that two mathematical expressions are, for instance, equal as identified by the equal sign (=), or assigned to one another by a similar sign.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="expanded-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.7: The complete representation of a term for which there is an abbreviated form.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="formula"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.14: Figures, symbols or the like used to express a concept briefly, such as a mathematical or chemical formula.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="head-term"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.1: The concept designation that has been chosen to head a terminological record.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="initialism"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8.3: An abbreviated form of a term consisting of some of the initial letters of the words making up a multiword term or the term elements making up a compound term when these letters are pronounced individually (e.g. 'BSE' for 'bovine spongiform encephalopathy').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="international-scientific-term"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.4: A term that is part of an international scientific nomenclature as adopted by an appropriate scientific body.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="internationalism"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.6: A term that has the same or nearly identical orthographic or phonemic form in many languages.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="logical-expression"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.16: An expression used to represent a concept based on mathematical or logical relations, such as statements of inequality, set relationships, Boolean operations, and the like.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="materials-management-unit"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.17: A unit to track object.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="name"> <xsd:annotation> <xsd:documentation>Indicates the marked text is a name.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="near-synonym"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.3: A term that represents the same or a very similar concept as another term in the same language, but for which interchangeability is limited to some contexts and inapplicable in others.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="part-number"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.17.2: A unique alphanumeric designation assigned to an object in a manufacturing system.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="phrase"> <xsd:annotation> <xsd:documentation>Indicates the marked text is a phrase.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="phraseological-unit"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.18: Any group of two or more words that form a unit, the meaning of which frequently cannot be deduced based on the combined sense of the words making up the phrase.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="protected"> <xsd:annotation> <xsd:documentation>Indicates the marked text should not be translated.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="romanized-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.12: A form of a term resulting from an operation whereby non-Latin writing systems are converted to the Latin alphabet.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="seg"> <xsd:annotation> <xsd:documentation>Indicates that the marked text represents a segment.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="set-phrase"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.18.2: A fixed, lexicalized phrase.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="short-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8.2: A variant of a multiword term that includes fewer words than the full form of the term (e.g. 'Group of Twenty-four' for 'Intergovernmental Group of Twenty-four on International Monetary Affairs').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="sku"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.17.1: Stock keeping unit, an inventory item identified by a unique alphanumeric designation assigned to an object in an inventory control system.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="standard-text"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.19: A fixed chunk of recurring text.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="symbol"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.13: A designation of a concept by letters, numerals, pictograms or any combination thereof.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="synonym"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.2: Any term that represents the same or a very similar concept as the main entry term in a term entry.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="synonymous-phrase"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.18.3: Phraseological unit in a language that expresses the same semantic content as another phrase in that same language.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="term"> <xsd:annotation> <xsd:documentation>Indicates the marked text is a term.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="transcribed-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.11: A form of a term resulting from an operation whereby the characters of one writing system are represented by characters from another writing system, taking into account the pronunciation of the characters converted.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="transliterated-form"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.10: A form of a term resulting from an operation whereby the characters of an alphabetic writing system are represented by characters from another alphabetic writing system.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="truncated-term"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.8.5: An abbreviated form of a term resulting from the omission of one or more term elements or syllables (e.g. 'flu' for 'influenza').</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="variant"> <xsd:annotation> <xsd:documentation>ISO-12620 2.1.9: One of the alternate forms of a term.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="restypeValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'restype'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="auto3state"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC AUTO3STATE control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="autocheckbox"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC AUTOCHECKBOX control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="autoradiobutton"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC AUTORADIOBUTTON control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="bedit"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC BEDIT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="bitmap"> <xsd:annotation> <xsd:documentation>Indicates a bitmap, for example a BITMAP resource in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="button"> <xsd:annotation> <xsd:documentation>Indicates a button object, for example a BUTTON control Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="caption"> <xsd:annotation> <xsd:documentation>Indicates a caption, such as the caption of a dialog box.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cell"> <xsd:annotation> <xsd:documentation>Indicates the cell in a table, for example the content of the <td> element in HTML.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="checkbox"> <xsd:annotation> <xsd:documentation>Indicates check box object, for example a CHECKBOX control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="checkboxmenuitem"> <xsd:annotation> <xsd:documentation>Indicates a menu item with an associated checkbox.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="checkedlistbox"> <xsd:annotation> <xsd:documentation>Indicates a list box, but with a check-box for each item.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="colorchooser"> <xsd:annotation> <xsd:documentation>Indicates a color selection dialog.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="combobox"> <xsd:annotation> <xsd:documentation>Indicates a combination of edit box and listbox object, for example a COMBOBOX control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="comboboxexitem"> <xsd:annotation> <xsd:documentation>Indicates an initialization entry of an extended combobox DLGINIT resource block. (code 0x1234).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="comboboxitem"> <xsd:annotation> <xsd:documentation>Indicates an initialization entry of a combobox DLGINIT resource block (code 0x0403).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="component"> <xsd:annotation> <xsd:documentation>Indicates a UI base class element that cannot be represented by any other element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="contextmenu"> <xsd:annotation> <xsd:documentation>Indicates a context menu.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="ctext"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC CTEXT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cursor"> <xsd:annotation> <xsd:documentation>Indicates a cursor, for example a CURSOR resource in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="datetimepicker"> <xsd:annotation> <xsd:documentation>Indicates a date/time picker.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="defpushbutton"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC DEFPUSHBUTTON control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="dialog"> <xsd:annotation> <xsd:documentation>Indicates a dialog box.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="dlginit"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC DLGINIT resource block.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="edit"> <xsd:annotation> <xsd:documentation>Indicates an edit box object, for example an EDIT control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="file"> <xsd:annotation> <xsd:documentation>Indicates a filename.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="filechooser"> <xsd:annotation> <xsd:documentation>Indicates a file dialog.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="fn"> <xsd:annotation> <xsd:documentation>Indicates a footnote.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="font"> <xsd:annotation> <xsd:documentation>Indicates a font name.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="footer"> <xsd:annotation> <xsd:documentation>Indicates a footer.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="frame"> <xsd:annotation> <xsd:documentation>Indicates a frame object.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="grid"> <xsd:annotation> <xsd:documentation>Indicates a XUL grid element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="groupbox"> <xsd:annotation> <xsd:documentation>Indicates a groupbox object, for example a GROUPBOX control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="header"> <xsd:annotation> <xsd:documentation>Indicates a header item.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="heading"> <xsd:annotation> <xsd:documentation>Indicates a heading, such has the content of <h1>, <h2>, etc. in HTML.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="hedit"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC HEDIT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="hscrollbar"> <xsd:annotation> <xsd:documentation>Indicates a horizontal scrollbar.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="icon"> <xsd:annotation> <xsd:documentation>Indicates an icon, for example an ICON resource in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="iedit"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC IEDIT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="keywords"> <xsd:annotation> <xsd:documentation>Indicates keyword list, such as the content of the Keywords meta-data in HTML, or a K footnote in WinHelp RTF.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="label"> <xsd:annotation> <xsd:documentation>Indicates a label object.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="linklabel"> <xsd:annotation> <xsd:documentation>Indicates a label that is also a HTML link (not necessarily a URL).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="list"> <xsd:annotation> <xsd:documentation>Indicates a list (a group of list-items, for example an <ol> or <ul> element in HTML).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="listbox"> <xsd:annotation> <xsd:documentation>Indicates a listbox object, for example an LISTBOX control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="listitem"> <xsd:annotation> <xsd:documentation>Indicates an list item (an entry in a list).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="ltext"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC LTEXT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="menu"> <xsd:annotation> <xsd:documentation>Indicates a menu (a group of menu-items).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="menubar"> <xsd:annotation> <xsd:documentation>Indicates a toolbar containing one or more tope level menus.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="menuitem"> <xsd:annotation> <xsd:documentation>Indicates a menu item (an entry in a menu).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="menuseparator"> <xsd:annotation> <xsd:documentation>Indicates a XUL menuseparator element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="message"> <xsd:annotation> <xsd:documentation>Indicates a message, for example an entry in a MESSAGETABLE resource in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="monthcalendar"> <xsd:annotation> <xsd:documentation>Indicates a calendar control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="numericupdown"> <xsd:annotation> <xsd:documentation>Indicates an edit box beside a spin control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="panel"> <xsd:annotation> <xsd:documentation>Indicates a catch all for rectangular areas.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="popupmenu"> <xsd:annotation> <xsd:documentation>Indicates a standalone menu not necessarily associated with a menubar.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pushbox"> <xsd:annotation> <xsd:documentation>Indicates a pushbox object, for example a PUSHBOX control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pushbutton"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC PUSHBUTTON control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="radio"> <xsd:annotation> <xsd:documentation>Indicates a radio button object.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="radiobuttonmenuitem"> <xsd:annotation> <xsd:documentation>Indicates a menuitem with associated radio button.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rcdata"> <xsd:annotation> <xsd:documentation>Indicates raw data resources for an application.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="row"> <xsd:annotation> <xsd:documentation>Indicates a row in a table.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rtext"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC RTEXT control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="scrollpane"> <xsd:annotation> <xsd:documentation>Indicates a user navigable container used to show a portion of a document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="separator"> <xsd:annotation> <xsd:documentation>Indicates a generic divider object (e.g. menu group separator).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="shortcut"> <xsd:annotation> <xsd:documentation>Windows accelerators, shortcuts in resource or property files.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="spinner"> <xsd:annotation> <xsd:documentation>Indicates a UI control to indicate process activity but not progress.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="splitter"> <xsd:annotation> <xsd:documentation>Indicates a splitter bar.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="state3"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC STATE3 control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="statusbar"> <xsd:annotation> <xsd:documentation>Indicates a window for providing feedback to the users, like 'read-only', etc.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="string"> <xsd:annotation> <xsd:documentation>Indicates a string, for example an entry in a STRINGTABLE resource in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="tabcontrol"> <xsd:annotation> <xsd:documentation>Indicates a layers of controls with a tab to select layers.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="table"> <xsd:annotation> <xsd:documentation>Indicates a display and edits regular two-dimensional tables of cells.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="textbox"> <xsd:annotation> <xsd:documentation>Indicates a XUL textbox element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="togglebutton"> <xsd:annotation> <xsd:documentation>Indicates a UI button that can be toggled to on or off state.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="toolbar"> <xsd:annotation> <xsd:documentation>Indicates an array of controls, usually buttons.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="tooltip"> <xsd:annotation> <xsd:documentation>Indicates a pop up tool tip text.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="trackbar"> <xsd:annotation> <xsd:documentation>Indicates a bar with a pointer indicating a position within a certain range.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="tree"> <xsd:annotation> <xsd:documentation>Indicates a control that displays a set of hierarchical data.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="uri"> <xsd:annotation> <xsd:documentation>Indicates a URI (URN or URL).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="userbutton"> <xsd:annotation> <xsd:documentation>Indicates a Windows RC USERBUTTON control.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="usercontrol"> <xsd:annotation> <xsd:documentation>Indicates a user-defined control like CONTROL control in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="var"> <xsd:annotation> <xsd:documentation>Indicates the text of a variable.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="versioninfo"> <xsd:annotation> <xsd:documentation>Indicates version information about a resource like VERSIONINFO in Windows.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="vscrollbar"> <xsd:annotation> <xsd:documentation>Indicates a vertical scrollbar.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="window"> <xsd:annotation> <xsd:documentation>Indicates a graphical window.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="size-unitValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'size-unit'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="byte"> <xsd:annotation> <xsd:documentation>Indicates a size in 8-bit bytes.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="char"> <xsd:annotation> <xsd:documentation>Indicates a size in Unicode characters.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="col"> <xsd:annotation> <xsd:documentation>Indicates a size in columns. Used for HTML text area.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="cm"> <xsd:annotation> <xsd:documentation>Indicates a size in centimeters.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="dlgunit"> <xsd:annotation> <xsd:documentation>Indicates a size in dialog units, as defined in Windows resources.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="em"> <xsd:annotation> <xsd:documentation>Indicates a size in 'font-size' units (as defined in CSS).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="ex"> <xsd:annotation> <xsd:documentation>Indicates a size in 'x-height' units (as defined in CSS).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="glyph"> <xsd:annotation> <xsd:documentation>Indicates a size in glyphs. A glyph is considered to be one or more combined Unicode characters that represent a single displayable text character. Sometimes referred to as a 'grapheme cluster'</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="in"> <xsd:annotation> <xsd:documentation>Indicates a size in inches.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="mm"> <xsd:annotation> <xsd:documentation>Indicates a size in millimeters.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="percent"> <xsd:annotation> <xsd:documentation>Indicates a size in percentage.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="pixel"> <xsd:annotation> <xsd:documentation>Indicates a size in pixels.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="point"> <xsd:annotation> <xsd:documentation>Indicates a size in point.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="row"> <xsd:annotation> <xsd:documentation>Indicates a size in rows. Used for HTML text area.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="stateValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'state'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="final"> <xsd:annotation> <xsd:documentation>Indicates the terminating state.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-adaptation"> <xsd:annotation> <xsd:documentation>Indicates only non-textual information needs adaptation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-l10n"> <xsd:annotation> <xsd:documentation>Indicates both text and non-textual information needs adaptation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-review-adaptation"> <xsd:annotation> <xsd:documentation>Indicates only non-textual information needs review.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-review-l10n"> <xsd:annotation> <xsd:documentation>Indicates both text and non-textual information needs review.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-review-translation"> <xsd:annotation> <xsd:documentation>Indicates that only the text of the item needs to be reviewed.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="needs-translation"> <xsd:annotation> <xsd:documentation>Indicates that the item needs to be translated.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="new"> <xsd:annotation> <xsd:documentation>Indicates that the item is new. For example, translation units that were not in a previous version of the document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="signed-off"> <xsd:annotation> <xsd:documentation>Indicates that changes are reviewed and approved.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="translated"> <xsd:annotation> <xsd:documentation>Indicates that the item has been translated.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="state-qualifierValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'state-qualifier'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="exact-match"> <xsd:annotation> <xsd:documentation>Indicates an exact match. An exact match occurs when a source text of a segment is exactly the same as the source text of a segment that was translated previously.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="fuzzy-match"> <xsd:annotation> <xsd:documentation>Indicates a fuzzy match. A fuzzy match occurs when a source text of a segment is very similar to the source text of a segment that was translated previously (e.g. when the difference is casing, a few changed words, white-space discripancy, etc.).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="id-match"> <xsd:annotation> <xsd:documentation>Indicates a match based on matching IDs (in addition to matching text).</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="leveraged-glossary"> <xsd:annotation> <xsd:documentation>Indicates a translation derived from a glossary.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="leveraged-inherited"> <xsd:annotation> <xsd:documentation>Indicates a translation derived from existing translation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="leveraged-mt"> <xsd:annotation> <xsd:documentation>Indicates a translation derived from machine translation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="leveraged-repository"> <xsd:annotation> <xsd:documentation>Indicates a translation derived from a translation repository.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="leveraged-tm"> <xsd:annotation> <xsd:documentation>Indicates a translation derived from a translation memory.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="mt-suggestion"> <xsd:annotation> <xsd:documentation>Indicates the translation is suggested by machine translation.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rejected-grammar"> <xsd:annotation> <xsd:documentation>Indicates that the item has been rejected because of incorrect grammar.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rejected-inaccurate"> <xsd:annotation> <xsd:documentation>Indicates that the item has been rejected because it is incorrect.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rejected-length"> <xsd:annotation> <xsd:documentation>Indicates that the item has been rejected because it is too long or too short.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rejected-spelling"> <xsd:annotation> <xsd:documentation>Indicates that the item has been rejected because of incorrect spelling.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="tm-suggestion"> <xsd:annotation> <xsd:documentation>Indicates the translation is suggested by translation memory.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="unitValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'unit'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="word"> <xsd:annotation> <xsd:documentation>Refers to words.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="page"> <xsd:annotation> <xsd:documentation>Refers to pages.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="trans-unit"> <xsd:annotation> <xsd:documentation>Refers to <trans-unit> elements.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="bin-unit"> <xsd:annotation> <xsd:documentation>Refers to <bin-unit> elements.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="glyph"> <xsd:annotation> <xsd:documentation>Refers to glyphs.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="item"> <xsd:annotation> <xsd:documentation>Refers to <trans-unit> and/or <bin-unit> elements.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="instance"> <xsd:annotation> <xsd:documentation>Refers to the occurrences of instances defined by the count-type value.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="character"> <xsd:annotation> <xsd:documentation>Refers to characters.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="line"> <xsd:annotation> <xsd:documentation>Refers to lines.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="sentence"> <xsd:annotation> <xsd:documentation>Refers to sentences.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="paragraph"> <xsd:annotation> <xsd:documentation>Refers to paragraphs.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="segment"> <xsd:annotation> <xsd:documentation>Refers to segments.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="placeable"> <xsd:annotation> <xsd:documentation>Refers to placeables (inline elements).</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="priorityValueList"> <xsd:annotation> <xsd:documentation>Values for the attribute 'priority'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:positiveInteger"> <xsd:enumeration value="1"> <xsd:annotation> <xsd:documentation>Highest priority.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="2"> <xsd:annotation> <xsd:documentation>High priority.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="3"> <xsd:annotation> <xsd:documentation>High priority, but not as important as 2.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="4"> <xsd:annotation> <xsd:documentation>High priority, but not as important as 3.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="5"> <xsd:annotation> <xsd:documentation>Medium priority, but more important than 6.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="6"> <xsd:annotation> <xsd:documentation>Medium priority, but less important than 5.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="7"> <xsd:annotation> <xsd:documentation>Low priority, but more important than 8.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="8"> <xsd:annotation> <xsd:documentation>Low priority, but more important than 9.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="9"> <xsd:annotation> <xsd:documentation>Low priority.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="10"> <xsd:annotation> <xsd:documentation>Lowest priority.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="reformatValueYesNo"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="yes"> <xsd:annotation> <xsd:documentation>This value indicates that all properties can be reformatted. This value must be used alone.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="no"> <xsd:annotation> <xsd:documentation>This value indicates that no properties should be reformatted. This value must be used alone.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="reformatValueList"> <xsd:list> <xsd:simpleType> <xsd:union memberTypes="xlf:XTend"> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="coord"> <xsd:annotation> <xsd:documentation>This value indicates that all information in the coord attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="coord-x"> <xsd:annotation> <xsd:documentation>This value indicates that the x information in the coord attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="coord-y"> <xsd:annotation> <xsd:documentation>This value indicates that the y information in the coord attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="coord-cx"> <xsd:annotation> <xsd:documentation>This value indicates that the cx information in the coord attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="coord-cy"> <xsd:annotation> <xsd:documentation>This value indicates that the cy information in the coord attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="font"> <xsd:annotation> <xsd:documentation>This value indicates that all the information in the font attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="font-name"> <xsd:annotation> <xsd:documentation>This value indicates that the name information in the font attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="font-size"> <xsd:annotation> <xsd:documentation>This value indicates that the size information in the font attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="font-weight"> <xsd:annotation> <xsd:documentation>This value indicates that the weight information in the font attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="css-style"> <xsd:annotation> <xsd:documentation>This value indicates that the information in the css-style attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="style"> <xsd:annotation> <xsd:documentation>This value indicates that the information in the style attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="ex-style"> <xsd:annotation> <xsd:documentation>This value indicates that the information in the exstyle attribute can be modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> </xsd:union> </xsd:simpleType> </xsd:list> </xsd:simpleType> <xsd:simpleType name="purposeValueList"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="information"> <xsd:annotation> <xsd:documentation>Indicates that the context is informational in nature, specifying for example, how a term should be translated. Thus, should be displayed to anyone editing the XLIFF document.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="location"> <xsd:annotation> <xsd:documentation>Indicates that the context-group is used to specify where the term was found in the translatable source. Thus, it is not displayed.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="match"> <xsd:annotation> <xsd:documentation>Indicates that the context information should be used during translation memory lookups. Thus, it is not displayed.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="alttranstypeValueList"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="proposal"> <xsd:annotation> <xsd:documentation>Represents a translation proposal from a translation memory or other resource.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="previous-version"> <xsd:annotation> <xsd:documentation>Represents a previous version of the target element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="rejected"> <xsd:annotation> <xsd:documentation>Represents a rejected version of the target element.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="reference"> <xsd:annotation> <xsd:documentation>Represents a translation to be used for reference purposes only, for example from a related product or a different language.</xsd:documentation> </xsd:annotation> </xsd:enumeration> <xsd:enumeration value="accepted"> <xsd:annotation> <xsd:documentation>Represents a proposed translation that was used for the translation of the trans-unit, possibly modified.</xsd:documentation> </xsd:annotation> </xsd:enumeration> </xsd:restriction> </xsd:simpleType> <!-- Other Types --> <xsd:complexType name="ElemType_ExternalReference"> <xsd:choice> <xsd:element ref="xlf:internal-file"/> <xsd:element ref="xlf:external-file"/> </xsd:choice> </xsd:complexType> <xsd:simpleType name="AttrType_purpose"> <xsd:list> <xsd:simpleType> <xsd:union memberTypes="xlf:purposeValueList xlf:XTend"/> </xsd:simpleType> </xsd:list> </xsd:simpleType> <xsd:simpleType name="AttrType_datatype"> <xsd:union memberTypes="xlf:datatypeValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_restype"> <xsd:union memberTypes="xlf:restypeValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_alttranstype"> <xsd:union memberTypes="xlf:alttranstypeValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_context-type"> <xsd:union memberTypes="xlf:context-typeValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_state"> <xsd:union memberTypes="xlf:stateValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_state-qualifier"> <xsd:union memberTypes="xlf:state-qualifierValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_count-type"> <xsd:union memberTypes="xlf:restypeValueList xlf:count-typeValueList xlf:datatypeValueList xlf:stateValueList xlf:state-qualifierValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_InlineDelimiters"> <xsd:union memberTypes="xlf:InlineDelimitersValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_InlinePlaceholders"> <xsd:union memberTypes="xlf:InlinePlaceholdersValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_size-unit"> <xsd:union memberTypes="xlf:size-unitValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_mtype"> <xsd:union memberTypes="xlf:mtypeValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_unit"> <xsd:union memberTypes="xlf:unitValueList xlf:XTend"/> </xsd:simpleType> <xsd:simpleType name="AttrType_priority"> <xsd:union memberTypes="xlf:priorityValueList"/> </xsd:simpleType> <xsd:simpleType name="AttrType_reformat"> <xsd:union memberTypes="xlf:reformatValueYesNo xlf:reformatValueList"/> </xsd:simpleType> <xsd:simpleType name="AttrType_YesNo"> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="yes"/> <xsd:enumeration value="no"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="AttrType_Position"> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="open"/> <xsd:enumeration value="close"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="AttrType_assoc"> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="preceding"/> <xsd:enumeration value="following"/> <xsd:enumeration value="both"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="AttrType_annotates"> <xsd:restriction base="xsd:NMTOKEN"> <xsd:enumeration value="source"/> <xsd:enumeration value="target"/> <xsd:enumeration value="general"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="AttrType_Coordinates"> <xsd:annotation> <xsd:documentation>Values for the attribute 'coord'.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:string"> <xsd:pattern value="(-?\d+|#);(-?\d+|#);(-?\d+|#);(-?\d+|#)"/> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="AttrType_Version"> <xsd:annotation> <xsd:documentation>Version values: 1.0 and 1.1 are allowed for backward compatibility.</xsd:documentation> </xsd:annotation> <xsd:restriction base="xsd:string"> <xsd:enumeration value="1.2"/> <xsd:enumeration value="1.1"/> <xsd:enumeration value="1.0"/> </xsd:restriction> </xsd:simpleType> <!-- Groups --> <xsd:group name="ElemGroup_TextContent"> <xsd:choice> <xsd:element ref="xlf:g"/> <xsd:element ref="xlf:bpt"/> <xsd:element ref="xlf:ept"/> <xsd:element ref="xlf:ph"/> <xsd:element ref="xlf:it"/> <xsd:element ref="xlf:mrk"/> <xsd:element ref="xlf:x"/> <xsd:element ref="xlf:bx"/> <xsd:element ref="xlf:ex"/> </xsd:choice> </xsd:group> <xsd:attributeGroup name="AttrGroup_TextContent"> <xsd:attribute name="id" type="xsd:string" use="required"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="xid" type="xsd:string" use="optional"/> <xsd:attribute name="equiv-text" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:attributeGroup> <!-- XLIFF Structure --> <xsd:element name="xliff"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded"> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> <xsd:element ref="xlf:file"/> </xsd:sequence> <xsd:attribute name="version" type="xlf:AttrType_Version" use="required"/> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <xsd:element name="file"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" ref="xlf:header"/> <xsd:element ref="xlf:body"/> </xsd:sequence> <xsd:attribute name="original" type="xsd:string" use="required"/> <xsd:attribute name="source-language" type="xsd:language" use="required"/> <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="required"/> <xsd:attribute name="tool-id" type="xsd:string" use="optional"/> <xsd:attribute default="manual" name="tool" type="xsd:string" use="optional"/> <xsd:attribute name="date" type="xsd:dateTime" use="optional"/> <xsd:attribute ref="xml:space" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="category" type="xsd:string" use="optional"/> <xsd:attribute name="target-language" type="xsd:language" use="optional"/> <xsd:attribute name="product-name" type="xsd:string" use="optional"/> <xsd:attribute name="product-version" type="xsd:string" use="optional"/> <xsd:attribute name="build-num" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_group_id"> <xsd:selector xpath=".//xlf:group"/> <xsd:field xpath="@id"/> </xsd:unique> <xsd:key name="K_unit_id"> <xsd:selector xpath=".//xlf:trans-unit|.//xlf:bin-unit"/> <xsd:field xpath="@id"/> </xsd:key> <xsd:keyref name="KR_unit_id" refer="xlf:K_unit_id"> <xsd:selector xpath=".//bpt|.//ept|.//it|.//ph|.//g|.//x|.//bx|.//ex|.//sub"/> <xsd:field xpath="@xid"/> </xsd:keyref> <xsd:key name="K_tool-id"> <xsd:selector xpath="xlf:header/xlf:tool"/> <xsd:field xpath="@tool-id"/> </xsd:key> <xsd:keyref name="KR_file_tool-id" refer="xlf:K_tool-id"> <xsd:selector xpath="."/> <xsd:field xpath="@tool-id"/> </xsd:keyref> <xsd:keyref name="KR_phase_tool-id" refer="xlf:K_tool-id"> <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/> <xsd:field xpath="@tool-id"/> </xsd:keyref> <xsd:keyref name="KR_alt-trans_tool-id" refer="xlf:K_tool-id"> <xsd:selector xpath=".//xlf:trans-unit/xlf:alt-trans"/> <xsd:field xpath="@tool-id"/> </xsd:keyref> <xsd:key name="K_count-group_name"> <xsd:selector xpath=".//xlf:count-group"/> <xsd:field xpath="@name"/> </xsd:key> <xsd:unique name="U_context-group_name"> <xsd:selector xpath=".//xlf:context-group"/> <xsd:field xpath="@name"/> </xsd:unique> <xsd:key name="K_phase-name"> <xsd:selector xpath="xlf:header/xlf:phase-group/xlf:phase"/> <xsd:field xpath="@phase-name"/> </xsd:key> <xsd:keyref name="KR_phase-name" refer="xlf:K_phase-name"> <xsd:selector xpath=".//xlf:count|.//xlf:trans-unit|.//xlf:target|.//bin-unit|.//bin-target"/> <xsd:field xpath="@phase-name"/> </xsd:keyref> <xsd:unique name="U_uid"> <xsd:selector xpath=".//xlf:external-file"/> <xsd:field xpath="@uid"/> </xsd:unique> </xsd:element> <xsd:element name="header"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" name="skl" type="xlf:ElemType_ExternalReference"/> <xsd:element minOccurs="0" ref="xlf:phase-group"/> <xsd:choice maxOccurs="unbounded" minOccurs="0"> <xsd:element name="glossary" type="xlf:ElemType_ExternalReference"/> <xsd:element name="reference" type="xlf:ElemType_ExternalReference"/> <xsd:element ref="xlf:count-group"/> <xsd:element ref="xlf:prop-group"/> <xsd:element ref="xlf:note"/> <xsd:element ref="xlf:tool"/> </xsd:choice> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="internal-file"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute name="form" type="xsd:string"/> <xsd:attribute name="crc" type="xsd:NMTOKEN"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element> <xsd:element name="external-file"> <xsd:complexType> <xsd:attribute name="href" type="xsd:string" use="required"/> <xsd:attribute name="crc" type="xsd:NMTOKEN"/> <xsd:attribute name="uid" type="xsd:NMTOKEN"/> </xsd:complexType> </xsd:element> <xsd:element name="note"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:attribute default="1" name="priority" type="xlf:AttrType_priority" use="optional"/> <xsd:attribute name="from" type="xsd:string" use="optional"/> <xsd:attribute default="general" name="annotates" type="xlf:AttrType_annotates" use="optional"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element> <xsd:element name="phase-group"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded"> <xsd:element ref="xlf:phase"/> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="phase"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:note"/> </xsd:sequence> <xsd:attribute name="phase-name" type="xsd:string" use="required"/> <xsd:attribute name="process-name" type="xsd:string" use="required"/> <xsd:attribute name="company-name" type="xsd:string" use="optional"/> <xsd:attribute name="tool-id" type="xsd:string" use="optional"/> <xsd:attribute name="tool" type="xsd:string" use="optional"/> <xsd:attribute name="date" type="xsd:dateTime" use="optional"/> <xsd:attribute name="job-id" type="xsd:string" use="optional"/> <xsd:attribute name="contact-name" type="xsd:string" use="optional"/> <xsd:attribute name="contact-email" type="xsd:string" use="optional"/> <xsd:attribute name="contact-phone" type="xsd:string" use="optional"/> </xsd:complexType> </xsd:element> <xsd:element name="count-group"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:count"/> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required"/> </xsd:complexType> </xsd:element> <xsd:element name="count"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute name="count-type" type="xlf:AttrType_count-type" use="optional"/> <xsd:attribute name="phase-name" type="xsd:string" use="optional"/> <xsd:attribute default="word" name="unit" type="xlf:AttrType_unit" use="optional"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element> <xsd:element name="context-group"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded"> <xsd:element ref="xlf:context"/> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="optional"/> <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="purpose" type="xlf:AttrType_purpose" use="optional"/> </xsd:complexType> </xsd:element> <xsd:element name="context"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute name="context-type" type="xlf:AttrType_context-type" use="required"/> <xsd:attribute default="no" name="match-mandatory" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element> <xsd:element name="prop-group"> <xsd:complexType> <xsd:sequence maxOccurs="unbounded"> <xsd:element ref="xlf:prop"/> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="optional"/> </xsd:complexType> </xsd:element> <xsd:element name="prop"> <xsd:complexType> <xsd:simpleContent> <xsd:extension base="xsd:string"> <xsd:attribute name="prop-type" type="xsd:string" use="required"/> <xsd:attribute ref="xml:lang" use="optional"/> </xsd:extension> </xsd:simpleContent> </xsd:complexType> </xsd:element> <xsd:element name="tool"> <xsd:complexType mixed="true"> <xsd:sequence> <xsd:any namespace="##any" processContents="skip" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="tool-id" type="xsd:string" use="required"/> <xsd:attribute name="tool-name" type="xsd:string" use="required"/> <xsd:attribute name="tool-version" type="xsd:string" use="optional"/> <xsd:attribute name="tool-company" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <xsd:element name="body"> <xsd:complexType> <xsd:choice maxOccurs="unbounded" minOccurs="0"> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/> </xsd:choice> </xsd:complexType> </xsd:element> <xsd:element name="group"> <xsd:complexType> <xsd:sequence> <xsd:sequence> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:count-group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:prop-group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> </xsd:sequence> <xsd:choice maxOccurs="unbounded"> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:trans-unit"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:bin-unit"/> </xsd:choice> </xsd:sequence> <xsd:attribute name="id" type="xsd:string" use="optional"/> <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/> <xsd:attribute default="default" ref="xml:space" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:attribute name="extradata" type="xsd:string" use="optional"/> <xsd:attribute name="extype" type="xsd:string" use="optional"/> <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="menu" type="xsd:string" use="optional"/> <xsd:attribute name="menu-option" type="xsd:string" use="optional"/> <xsd:attribute name="menu-name" type="xsd:string" use="optional"/> <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/> <xsd:attribute name="font" type="xsd:string" use="optional"/> <xsd:attribute name="css-style" type="xsd:string" use="optional"/> <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/> <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/> <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="charclass" type="xsd:string" use="optional"/> <xsd:attribute default="no" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <xsd:element name="trans-unit"> <xsd:complexType> <xsd:sequence> <xsd:element ref="xlf:source"/> <xsd:element minOccurs="0" ref="xlf:seg-source"/> <xsd:element minOccurs="0" ref="xlf:target"/> <xsd:choice maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:context-group"/> <xsd:element ref="xlf:count-group"/> <xsd:element ref="xlf:prop-group"/> <xsd:element ref="xlf:note"/> <xsd:element ref="xlf:alt-trans"/> </xsd:choice> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> </xsd:sequence> <xsd:attribute name="id" type="xsd:string" use="required"/> <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/> <xsd:attribute default="default" ref="xml:space" use="optional"/> <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="phase-name" type="xsd:string" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:attribute name="extradata" type="xsd:string" use="optional"/> <xsd:attribute name="extype" type="xsd:string" use="optional"/> <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="menu" type="xsd:string" use="optional"/> <xsd:attribute name="menu-option" type="xsd:string" use="optional"/> <xsd:attribute name="menu-name" type="xsd:string" use="optional"/> <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/> <xsd:attribute name="font" type="xsd:string" use="optional"/> <xsd:attribute name="css-style" type="xsd:string" use="optional"/> <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute default="pixel" name="size-unit" type="xlf:AttrType_size-unit" use="optional"/> <xsd:attribute name="maxwidth" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minwidth" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="maxheight" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minheight" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="maxbytes" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="minbytes" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="charclass" type="xsd:string" use="optional"/> <xsd:attribute default="yes" name="merged-trans" type="xlf:AttrType_YesNo" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_tu_segsrc_mid"> <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/> <xsd:field xpath="@mid"/> </xsd:unique> <xsd:keyref name="KR_tu_segsrc_mid" refer="xlf:U_tu_segsrc_mid"> <xsd:selector xpath="./xlf:target/xlf:mrk|./xlf:alt-trans"/> <xsd:field xpath="@mid"/> </xsd:keyref> </xsd:element> <xsd:element name="source"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_source_bpt_rid"> <xsd:selector xpath=".//xlf:bpt"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_source_ept_rid" refer="xlf:U_source_bpt_rid"> <xsd:selector xpath=".//xlf:ept"/> <xsd:field xpath="@rid"/> </xsd:keyref> <xsd:unique name="U_source_bx_rid"> <xsd:selector xpath=".//xlf:bx"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_source_ex_rid" refer="xlf:U_source_bx_rid"> <xsd:selector xpath=".//xlf:ex"/> <xsd:field xpath="@rid"/> </xsd:keyref> </xsd:element> <xsd:element name="seg-source"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_segsrc_bpt_rid"> <xsd:selector xpath=".//xlf:bpt"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_segsrc_ept_rid" refer="xlf:U_segsrc_bpt_rid"> <xsd:selector xpath=".//xlf:ept"/> <xsd:field xpath="@rid"/> </xsd:keyref> <xsd:unique name="U_segsrc_bx_rid"> <xsd:selector xpath=".//xlf:bx"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_segsrc_ex_rid" refer="xlf:U_segsrc_bx_rid"> <xsd:selector xpath=".//xlf:ex"/> <xsd:field xpath="@rid"/> </xsd:keyref> </xsd:element> <xsd:element name="target"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/> <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/> <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/> <xsd:attribute name="font" type="xsd:string" use="optional"/> <xsd:attribute name="css-style" type="xsd:string" use="optional"/> <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute default="yes" name="equiv-trans" type="xlf:AttrType_YesNo" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_target_bpt_rid"> <xsd:selector xpath=".//xlf:bpt"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_target_ept_rid" refer="xlf:U_target_bpt_rid"> <xsd:selector xpath=".//xlf:ept"/> <xsd:field xpath="@rid"/> </xsd:keyref> <xsd:unique name="U_target_bx_rid"> <xsd:selector xpath=".//xlf:bx"/> <xsd:field xpath="@rid"/> </xsd:unique> <xsd:keyref name="KR_target_ex_rid" refer="xlf:U_target_bx_rid"> <xsd:selector xpath=".//xlf:ex"/> <xsd:field xpath="@rid"/> </xsd:keyref> </xsd:element> <xsd:element name="alt-trans"> <xsd:complexType> <xsd:sequence> <xsd:element minOccurs="0" ref="xlf:source"/> <xsd:element minOccurs="0" ref="xlf:seg-source"/> <xsd:element maxOccurs="unbounded" ref="xlf:target"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:context-group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:prop-group"/> <xsd:element maxOccurs="unbounded" minOccurs="0" ref="xlf:note"/> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> </xsd:sequence> <xsd:attribute name="match-quality" type="xsd:string" use="optional"/> <xsd:attribute name="tool-id" type="xsd:string" use="optional"/> <xsd:attribute name="tool" type="xsd:string" use="optional"/> <xsd:attribute name="crc" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute ref="xml:lang" use="optional"/> <xsd:attribute name="origin" type="xsd:string" use="optional"/> <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/> <xsd:attribute default="default" ref="xml:space" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:attribute name="extradata" type="xsd:string" use="optional"/> <xsd:attribute name="extype" type="xsd:string" use="optional"/> <xsd:attribute name="help-id" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="menu" type="xsd:string" use="optional"/> <xsd:attribute name="menu-option" type="xsd:string" use="optional"/> <xsd:attribute name="menu-name" type="xsd:string" use="optional"/> <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="coord" type="xlf:AttrType_Coordinates" use="optional"/> <xsd:attribute name="font" type="xsd:string" use="optional"/> <xsd:attribute name="css-style" type="xsd:string" use="optional"/> <xsd:attribute name="style" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="exstyle" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute default="proposal" name="alttranstype" type="xlf:AttrType_alttranstype" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> <xsd:unique name="U_at_segsrc_mid"> <xsd:selector xpath="./xlf:seg-source/xlf:mrk"/> <xsd:field xpath="@mid"/> </xsd:unique> <xsd:keyref name="KR_at_segsrc_mid" refer="xlf:U_at_segsrc_mid"> <xsd:selector xpath="./xlf:target/xlf:mrk"/> <xsd:field xpath="@mid"/> </xsd:keyref> </xsd:element> <xsd:element name="bin-unit"> <xsd:complexType> <xsd:sequence> <xsd:element ref="xlf:bin-source"/> <xsd:element minOccurs="0" ref="xlf:bin-target"/> <xsd:choice maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:context-group"/> <xsd:element ref="xlf:count-group"/> <xsd:element ref="xlf:prop-group"/> <xsd:element ref="xlf:note"/> <xsd:element ref="xlf:trans-unit"/> </xsd:choice> <xsd:any maxOccurs="unbounded" minOccurs="0" namespace="##other" processContents="skip"/> </xsd:sequence> <xsd:attribute name="id" type="xsd:string" use="required"/> <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="required"/> <xsd:attribute name="approved" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute default="yes" name="translate" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attribute default="yes" name="reformat" type="xlf:AttrType_reformat" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:attribute name="phase-name" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <xsd:element name="bin-source"> <xsd:complexType> <xsd:choice> <xsd:element ref="xlf:internal-file"/> <xsd:element ref="xlf:external-file"/> </xsd:choice> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <xsd:element name="bin-target"> <xsd:complexType> <xsd:choice> <xsd:element ref="xlf:internal-file"/> <xsd:element ref="xlf:external-file"/> </xsd:choice> <xsd:attribute name="mime-type" type="xlf:mime-typeValueList" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:attribute name="state" type="xlf:AttrType_state" use="optional"/> <xsd:attribute name="state-qualifier" type="xlf:AttrType_state-qualifier" use="optional"/> <xsd:attribute name="phase-name" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="restype" type="xlf:AttrType_restype" use="optional"/> <xsd:attribute name="resname" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> <!-- Element for inline codes --> <xsd:element name="g"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/> <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="x"> <xsd:complexType> <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/> <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="bx"> <xsd:complexType> <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/> <xsd:attribute default="yes" name="clone" type="xlf:AttrType_YesNo" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="ex"> <xsd:complexType> <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="ph"> <xsd:complexType mixed="true"> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:sub"/> </xsd:sequence> <xsd:attribute name="ctype" type="xlf:AttrType_InlinePlaceholders" use="optional"/> <xsd:attribute name="crc" type="xsd:string" use="optional"/> <xsd:attribute name="assoc" type="xlf:AttrType_assoc" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="bpt"> <xsd:complexType mixed="true"> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:sub"/> </xsd:sequence> <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/> <xsd:attribute name="crc" type="xsd:string" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="ept"> <xsd:complexType mixed="true"> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:sub"/> </xsd:sequence> <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="crc" type="xsd:string" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="it"> <xsd:complexType mixed="true"> <xsd:sequence maxOccurs="unbounded" minOccurs="0"> <xsd:element ref="xlf:sub"/> </xsd:sequence> <xsd:attribute name="pos" type="xlf:AttrType_Position" use="required"/> <xsd:attribute name="rid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/> <xsd:attribute name="crc" type="xsd:string" use="optional"/> <xsd:attributeGroup ref="xlf:AttrGroup_TextContent"/> </xsd:complexType> </xsd:element> <xsd:element name="sub"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute name="datatype" type="xlf:AttrType_datatype" use="optional"/> <xsd:attribute name="ctype" type="xlf:AttrType_InlineDelimiters" use="optional"/> <xsd:attribute name="xid" type="xsd:string" use="optional"/> </xsd:complexType> </xsd:element> <xsd:element name="mrk"> <xsd:complexType mixed="true"> <xsd:group maxOccurs="unbounded" minOccurs="0" ref="xlf:ElemGroup_TextContent"/> <xsd:attribute name="mtype" type="xlf:AttrType_mtype" use="required"/> <xsd:attribute name="mid" type="xsd:NMTOKEN" use="optional"/> <xsd:attribute name="comment" type="xsd:string" use="optional"/> <xsd:attribute name="ts" type="xsd:string" use="optional"/> <xsd:anyAttribute namespace="##any" processContents="skip"/> </xsd:complexType> </xsd:element> </xsd:schema> Resources/schemas/xliff-core-2.0.xsd 0000644 00000040554 15111175536 0013231 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?> <!-- XLIFF Version 2.0 OASIS Standard 05 August 2014 Copyright (c) OASIS Open 2014. All rights reserved. Source: http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/schemas/ --> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" xmlns:xlf="urn:oasis:names:tc:xliff:document:2.0" targetNamespace="urn:oasis:names:tc:xliff:document:2.0"> <!-- Import --> <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="informativeCopiesOf3rdPartySchemas/w3c/xml.xsd"/> <!-- Element Group --> <xs:group name="inline"> <xs:choice> <xs:element ref="xlf:cp"/> <xs:element ref="xlf:ph"/> <xs:element ref="xlf:pc"/> <xs:element ref="xlf:sc"/> <xs:element ref="xlf:ec"/> <xs:element ref="xlf:mrk"/> <xs:element ref="xlf:sm"/> <xs:element ref="xlf:em"/> </xs:choice> </xs:group> <!-- Attribute Types --> <xs:simpleType name="yesNo"> <xs:restriction base="xs:string"> <xs:enumeration value="yes"/> <xs:enumeration value="no"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="yesNoFirstNo"> <xs:restriction base="xs:string"> <xs:enumeration value="yes"/> <xs:enumeration value="firstNo"/> <xs:enumeration value="no"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="dirValue"> <xs:restriction base="xs:string"> <xs:enumeration value="ltr"/> <xs:enumeration value="rtl"/> <xs:enumeration value="auto"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="appliesTo"> <xs:restriction base="xs:string"> <xs:enumeration value="source"/> <xs:enumeration value="target"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="userDefinedValue"> <xs:restriction base="xs:string"> <xs:pattern value="[^\s:]+:[^\s:]+"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="attrType_type"> <xs:restriction base="xs:string"> <xs:enumeration value="fmt"/> <xs:enumeration value="ui"/> <xs:enumeration value="quote"/> <xs:enumeration value="link"/> <xs:enumeration value="image"/> <xs:enumeration value="other"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="typeForMrkValues"> <xs:restriction base="xs:NMTOKEN"> <xs:enumeration value="generic"/> <xs:enumeration value="comment"/> <xs:enumeration value="term"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="attrType_typeForMrk"> <xs:union memberTypes="xlf:typeForMrkValues xlf:userDefinedValue"/> </xs:simpleType> <xs:simpleType name="priorityValue"> <xs:restriction base="xs:positiveInteger"> <xs:minInclusive value="1"/> <xs:maxInclusive value="10"/> </xs:restriction> </xs:simpleType> <xs:simpleType name="stateType"> <xs:restriction base="xs:string"> <xs:enumeration value="initial"/> <xs:enumeration value="translated"/> <xs:enumeration value="reviewed"/> <xs:enumeration value="final"/> </xs:restriction> </xs:simpleType> <!-- Structural Elements --> <xs:element name="xliff"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:file"/> </xs:sequence> <xs:attribute name="version" use="required"/> <xs:attribute name="srcLang" use="required"/> <xs:attribute name="trgLang" use="optional"/> <xs:attribute ref="xml:space" use="optional" default="default"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="file"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:skeleton"/> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/> <xs:choice minOccurs="1" maxOccurs="unbounded"> <xs:element ref="xlf:unit"/> <xs:element ref="xlf:group"/> </xs:choice> </xs:sequence> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="canResegment" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="original" use="optional"/> <xs:attribute name="translate" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="srcDir" use="optional" type="xlf:dirValue" default="auto"/> <xs:attribute name="trgDir" use="optional" type="xlf:dirValue" default="auto"/> <xs:attribute ref="xml:space" use="optional"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="skeleton"> <xs:complexType mixed="true"> <xs:sequence> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/> </xs:sequence> <xs:attribute name="href" use="optional"/> </xs:complexType> </xs:element> <xs:element name="group"> <xs:complexType mixed="false"> <xs:sequence> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element ref="xlf:unit"/> <xs:element ref="xlf:group"/> </xs:choice> </xs:sequence> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="name" use="optional"/> <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/> <xs:attribute name="translate" use="optional" type="xlf:yesNo"/> <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/> <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/> <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/> <xs:attribute ref="xml:space" use="optional"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="unit"> <xs:complexType mixed="false"> <xs:sequence> <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:notes"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:originalData"/> <xs:choice minOccurs="1" maxOccurs="unbounded"> <xs:element ref="xlf:segment"/> <xs:element ref="xlf:ignorable"/> </xs:choice> </xs:sequence> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="name" use="optional"/> <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/> <xs:attribute name="translate" use="optional" type="xlf:yesNo"/> <xs:attribute name="srcDir" use="optional" type="xlf:dirValue"/> <xs:attribute name="trgDir" use="optional" type="xlf:dirValue"/> <xs:attribute ref="xml:space" use="optional"/> <xs:attribute name="type" use="optional" type="xlf:userDefinedValue"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="segment"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/> </xs:sequence> <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="canResegment" use="optional" type="xlf:yesNo"/> <xs:attribute name="state" use="optional" type="xlf:stateType" default="initial"/> <xs:attribute name="subState" use="optional"/> </xs:complexType> </xs:element> <xs:element name="ignorable"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="1" ref="xlf:source"/> <xs:element minOccurs="0" maxOccurs="1" ref="xlf:target"/> </xs:sequence> <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/> </xs:complexType> </xs:element> <xs:element name="notes"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:note"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="note"> <xs:complexType mixed="true"> <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="appliesTo" use="optional" type="xlf:appliesTo"/> <xs:attribute name="category" use="optional"/> <xs:attribute name="priority" use="optional" type="xlf:priorityValue" default="1"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="originalData"> <xs:complexType mixed="false"> <xs:sequence> <xs:element minOccurs="1" maxOccurs="unbounded" ref="xlf:data"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="data"> <xs:complexType mixed="true"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="unbounded" ref="xlf:cp"/> </xs:sequence> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="dir" use="optional" type="xlf:dirValue" default="auto"/> <xs:attribute ref="xml:space" use="optional" fixed="preserve"/> </xs:complexType> </xs:element> <xs:element name="source"> <xs:complexType mixed="true"> <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/> <xs:attribute ref="xml:lang" use="optional"/> <xs:attribute ref="xml:space" use="optional"/> </xs:complexType> </xs:element> <xs:element name="target"> <xs:complexType mixed="true"> <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/> <xs:attribute ref="xml:lang" use="optional"/> <xs:attribute ref="xml:space" use="optional"/> <xs:attribute name="order" use="optional" type="xs:positiveInteger"/> </xs:complexType> </xs:element> <!-- Inline Elements --> <xs:element name="cp"> <!-- Code Point --> <xs:complexType mixed="false"> <xs:attribute name="hex" use="required" type="xs:hexBinary"/> </xs:complexType> </xs:element> <xs:element name="ph"> <!-- Placeholder --> <xs:complexType mixed="false"> <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/> <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="disp" use="optional"/> <xs:attribute name="equiv" use="optional"/> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/> <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/> <xs:attribute name="type" use="optional" type="xlf:attrType_type"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="pc"> <!-- Paired Code --> <xs:complexType mixed="true"> <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/> <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo"/> <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/> <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dispEnd" use="optional"/> <xs:attribute name="dispStart" use="optional"/> <xs:attribute name="equivEnd" use="optional"/> <xs:attribute name="equivStart" use="optional"/> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="dataRefEnd" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dataRefStart" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="subFlowsEnd" use="optional" type="xs:NMTOKENS"/> <xs:attribute name="subFlowsStart" use="optional" type="xs:NMTOKENS"/> <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/> <xs:attribute name="type" use="optional" type="xlf:attrType_type"/> <xs:attribute name="dir" use="optional" type="xlf:dirValue"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="sc"> <!-- Start Code --> <xs:complexType mixed="false"> <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/> <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dir" use="optional" type="xlf:dirValue"/> <xs:attribute name="disp" use="optional"/> <xs:attribute name="equiv" use="optional"/> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/> <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/> <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/> <xs:attribute name="type" use="optional" type="xlf:attrType_type"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="ec"> <!-- End Code --> <xs:complexType mixed="false"> <xs:attribute name="canCopy" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canDelete" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canOverlap" use="optional" type="xlf:yesNo" default="yes"/> <xs:attribute name="canReorder" use="optional" type="xlf:yesNoFirstNo" default="yes"/> <xs:attribute name="copyOf" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dataRef" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="dir" use="optional" type="xlf:dirValue"/> <xs:attribute name="disp" use="optional"/> <xs:attribute name="equiv" use="optional"/> <xs:attribute name="id" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="isolated" use="optional" type="xlf:yesNo" default="no"/> <xs:attribute name="startRef" use="optional" type="xs:NMTOKEN"/> <xs:attribute name="subFlows" use="optional" type="xs:NMTOKENS"/> <xs:attribute name="subType" use="optional" type="xlf:userDefinedValue"/> <xs:attribute name="type" use="optional" type="xlf:attrType_type"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="mrk"> <!-- Annotation Marker --> <xs:complexType mixed="true"> <xs:group ref="xlf:inline" minOccurs="0" maxOccurs="unbounded"/> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="translate" use="optional" type="xlf:yesNo"/> <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/> <xs:attribute name="ref" use="optional" type="xs:anyURI"/> <xs:attribute name="value" use="optional"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="sm"> <!-- Start Annotation Marker --> <xs:complexType mixed="false"> <xs:attribute name="id" use="required" type="xs:NMTOKEN"/> <xs:attribute name="translate" use="optional" type="xlf:yesNo"/> <xs:attribute name="type" use="optional" type="xlf:attrType_typeForMrk"/> <xs:attribute name="ref" use="optional" type="xs:anyURI"/> <xs:attribute name="value" use="optional"/> <xs:anyAttribute namespace="##other" processContents="lax"/> </xs:complexType> </xs:element> <xs:element name="em"> <!-- End Annotation Marker --> <xs:complexType mixed="false"> <xs:attribute name="startRef" use="required" type="xs:NMTOKEN"/> </xs:complexType> </xs:element> </xs:schema> Resources/bin/translation-status.php 0000644 00000021221 15111175536 0013661 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if ('cli' !== \PHP_SAPI) { throw new Exception('This script must be run from the command line.'); } $usageInstructions = <<<END Usage instructions ------------------------------------------------------------------------------- $ cd symfony-code-root-directory/ # show the translation status of all locales $ php translation-status.php # only show the translation status of incomplete or erroneous locales $ php translation-status.php --incomplete # show the translation status of all locales, all their missing translations and mismatches between trans-unit id and source $ php translation-status.php -v # show the status of a single locale $ php translation-status.php fr # show the status of a single locale, missing translations and mismatches between trans-unit id and source $ php translation-status.php fr -v END; $config = [ // if TRUE, the full list of missing translations is displayed 'verbose_output' => false, // NULL = analyze all locales 'locale_to_analyze' => null, // append --incomplete to only show incomplete languages 'include_completed_languages' => true, // the reference files all the other translations are compared to 'original_files' => [ 'src/Symfony/Component/Form/Resources/translations/validators.en.xlf', 'src/Symfony/Component/Security/Core/Resources/translations/security.en.xlf', 'src/Symfony/Component/Validator/Resources/translations/validators.en.xlf', ], ]; $argc = $_SERVER['argc']; $argv = $_SERVER['argv']; if ($argc > 4) { echo str_replace('translation-status.php', $argv[0], $usageInstructions); exit(1); } foreach (array_slice($argv, 1) as $argumentOrOption) { if ('--incomplete' === $argumentOrOption) { $config['include_completed_languages'] = false; continue; } if (str_starts_with($argumentOrOption, '-')) { $config['verbose_output'] = true; } else { $config['locale_to_analyze'] = $argumentOrOption; } } foreach ($config['original_files'] as $originalFilePath) { if (!file_exists($originalFilePath)) { echo sprintf('The following file does not exist. Make sure that you execute this command at the root dir of the Symfony code repository.%s %s', \PHP_EOL, $originalFilePath); exit(1); } } $totalMissingTranslations = 0; $totalTranslationMismatches = 0; foreach ($config['original_files'] as $originalFilePath) { $translationFilePaths = findTranslationFiles($originalFilePath, $config['locale_to_analyze']); $translationStatus = calculateTranslationStatus($originalFilePath, $translationFilePaths); $totalMissingTranslations += array_sum(array_map(fn ($translation) => count($translation['missingKeys']), array_values($translationStatus))); $totalTranslationMismatches += array_sum(array_map(fn ($translation) => count($translation['mismatches']), array_values($translationStatus))); printTranslationStatus($originalFilePath, $translationStatus, $config['verbose_output'], $config['include_completed_languages']); } exit($totalTranslationMismatches > 0 ? 1 : 0); function findTranslationFiles($originalFilePath, $localeToAnalyze): array { $translations = []; $translationsDir = dirname($originalFilePath); $originalFileName = basename($originalFilePath); $translationFileNamePattern = str_replace('.en.', '.*.', $originalFileName); $translationFiles = glob($translationsDir.'/'.$translationFileNamePattern, \GLOB_NOSORT); sort($translationFiles); foreach ($translationFiles as $filePath) { $locale = extractLocaleFromFilePath($filePath); if (null !== $localeToAnalyze && $locale !== $localeToAnalyze) { continue; } $translations[$locale] = $filePath; } return $translations; } function calculateTranslationStatus($originalFilePath, $translationFilePaths): array { $translationStatus = []; $allTranslationKeys = extractTranslationKeys($originalFilePath); foreach ($translationFilePaths as $locale => $translationPath) { $translatedKeys = extractTranslationKeys($translationPath); $missingKeys = array_diff_key($allTranslationKeys, $translatedKeys); $mismatches = findTransUnitMismatches($allTranslationKeys, $translatedKeys); $translationStatus[$locale] = [ 'total' => count($allTranslationKeys), 'translated' => count($translatedKeys), 'missingKeys' => $missingKeys, 'mismatches' => $mismatches, ]; $translationStatus[$locale]['is_completed'] = isTranslationCompleted($translationStatus[$locale]); } return $translationStatus; } function isTranslationCompleted(array $translationStatus): bool { return $translationStatus['total'] === $translationStatus['translated'] && 0 === count($translationStatus['mismatches']); } function printTranslationStatus($originalFilePath, $translationStatus, $verboseOutput, $includeCompletedLanguages) { printTitle($originalFilePath); printTable($translationStatus, $verboseOutput, $includeCompletedLanguages); echo \PHP_EOL.\PHP_EOL; } function extractLocaleFromFilePath($filePath) { $parts = explode('.', $filePath); return $parts[count($parts) - 2]; } function extractTranslationKeys($filePath): array { $translationKeys = []; $contents = new \SimpleXMLElement(file_get_contents($filePath)); foreach ($contents->file->body->{'trans-unit'} as $translationKey) { $translationId = (string) $translationKey['id']; $translationKey = (string) $translationKey->source; $translationKeys[$translationId] = $translationKey; } return $translationKeys; } /** * Check whether the trans-unit id and source match with the base translation. */ function findTransUnitMismatches(array $baseTranslationKeys, array $translatedKeys): array { $mismatches = []; foreach ($baseTranslationKeys as $translationId => $translationKey) { if (!isset($translatedKeys[$translationId])) { continue; } if ($translatedKeys[$translationId] !== $translationKey) { $mismatches[$translationId] = [ 'found' => $translatedKeys[$translationId], 'expected' => $translationKey, ]; } } return $mismatches; } function printTitle($title) { echo $title.\PHP_EOL; echo str_repeat('=', strlen($title)).\PHP_EOL.\PHP_EOL; } function printTable($translations, $verboseOutput, bool $includeCompletedLanguages) { if (0 === count($translations)) { echo 'No translations found'; return; } $longestLocaleNameLength = max(array_map('strlen', array_keys($translations))); foreach ($translations as $locale => $translation) { if (!$includeCompletedLanguages && $translation['is_completed']) { continue; } if ($translation['translated'] > $translation['total']) { textColorRed(); } elseif (count($translation['mismatches']) > 0) { textColorRed(); } elseif ($translation['is_completed']) { textColorGreen(); } echo sprintf( '| Locale: %-'.$longestLocaleNameLength.'s | Translated: %2d/%2d | Mismatches: %d |', $locale, $translation['translated'], $translation['total'], count($translation['mismatches']) ).\PHP_EOL; textColorNormal(); $shouldBeClosed = false; if (true === $verboseOutput && count($translation['missingKeys']) > 0) { echo '| Missing Translations:'.\PHP_EOL; foreach ($translation['missingKeys'] as $id => $content) { echo sprintf('| (id=%s) %s', $id, $content).\PHP_EOL; } $shouldBeClosed = true; } if (true === $verboseOutput && count($translation['mismatches']) > 0) { echo '| Mismatches between trans-unit id and source:'.\PHP_EOL; foreach ($translation['mismatches'] as $id => $content) { echo sprintf('| (id=%s) Expected: %s', $id, $content['expected']).\PHP_EOL; echo sprintf('| Found: %s', $content['found']).\PHP_EOL; } $shouldBeClosed = true; } if ($shouldBeClosed) { echo str_repeat('-', 80).\PHP_EOL; } } } function textColorGreen() { echo "\033[32m"; } function textColorRed() { echo "\033[31m"; } function textColorNormal() { echo "\033[0m"; } Resources/functions.php 0000644 00000001062 15111175536 0011243 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; if (!\function_exists(t::class)) { /** * @author Nate Wiebe <nate@northern.co> */ function t(string $message, array $parameters = [], string $domain = null): TranslatableMessage { return new TranslatableMessage($message, $parameters, $domain); } } Resources/data/parents.json 0000644 00000006212 15111175536 0012004 0 ustar 00 { "az_Cyrl": "root", "bs_Cyrl": "root", "en_150": "en_001", "en_AG": "en_001", "en_AI": "en_001", "en_AT": "en_150", "en_AU": "en_001", "en_BB": "en_001", "en_BE": "en_150", "en_BM": "en_001", "en_BS": "en_001", "en_BW": "en_001", "en_BZ": "en_001", "en_CC": "en_001", "en_CH": "en_150", "en_CK": "en_001", "en_CM": "en_001", "en_CX": "en_001", "en_CY": "en_001", "en_DE": "en_150", "en_DG": "en_001", "en_DK": "en_150", "en_DM": "en_001", "en_ER": "en_001", "en_FI": "en_150", "en_FJ": "en_001", "en_FK": "en_001", "en_FM": "en_001", "en_GB": "en_001", "en_GD": "en_001", "en_GG": "en_001", "en_GH": "en_001", "en_GI": "en_001", "en_GM": "en_001", "en_GY": "en_001", "en_HK": "en_001", "en_ID": "en_001", "en_IE": "en_001", "en_IL": "en_001", "en_IM": "en_001", "en_IN": "en_001", "en_IO": "en_001", "en_JE": "en_001", "en_JM": "en_001", "en_KE": "en_001", "en_KI": "en_001", "en_KN": "en_001", "en_KY": "en_001", "en_LC": "en_001", "en_LR": "en_001", "en_LS": "en_001", "en_MG": "en_001", "en_MO": "en_001", "en_MS": "en_001", "en_MT": "en_001", "en_MU": "en_001", "en_MV": "en_001", "en_MW": "en_001", "en_MY": "en_001", "en_NA": "en_001", "en_NF": "en_001", "en_NG": "en_001", "en_NL": "en_150", "en_NR": "en_001", "en_NU": "en_001", "en_NZ": "en_001", "en_PG": "en_001", "en_PK": "en_001", "en_PN": "en_001", "en_PW": "en_001", "en_RW": "en_001", "en_SB": "en_001", "en_SC": "en_001", "en_SD": "en_001", "en_SE": "en_150", "en_SG": "en_001", "en_SH": "en_001", "en_SI": "en_150", "en_SL": "en_001", "en_SS": "en_001", "en_SX": "en_001", "en_SZ": "en_001", "en_TC": "en_001", "en_TK": "en_001", "en_TO": "en_001", "en_TT": "en_001", "en_TV": "en_001", "en_TZ": "en_001", "en_UG": "en_001", "en_VC": "en_001", "en_VG": "en_001", "en_VU": "en_001", "en_WS": "en_001", "en_ZA": "en_001", "en_ZM": "en_001", "en_ZW": "en_001", "es_AR": "es_419", "es_BO": "es_419", "es_BR": "es_419", "es_BZ": "es_419", "es_CL": "es_419", "es_CO": "es_419", "es_CR": "es_419", "es_CU": "es_419", "es_DO": "es_419", "es_EC": "es_419", "es_GT": "es_419", "es_HN": "es_419", "es_MX": "es_419", "es_NI": "es_419", "es_PA": "es_419", "es_PE": "es_419", "es_PR": "es_419", "es_PY": "es_419", "es_SV": "es_419", "es_US": "es_419", "es_UY": "es_419", "es_VE": "es_419", "ff_Adlm": "root", "hi_Latn": "en_IN", "ks_Deva": "root", "nb": "no", "nn": "no", "pa_Arab": "root", "pt_AO": "pt_PT", "pt_CH": "pt_PT", "pt_CV": "pt_PT", "pt_GQ": "pt_PT", "pt_GW": "pt_PT", "pt_LU": "pt_PT", "pt_MO": "pt_PT", "pt_MZ": "pt_PT", "pt_ST": "pt_PT", "pt_TL": "pt_PT", "sd_Deva": "root", "sr_Latn": "root", "uz_Arab": "root", "uz_Cyrl": "root", "zh_Hant": "root", "zh_Hant_MO": "zh_Hant_HK" } README.md 0000644 00000002357 15111175536 0006037 0 ustar 00 Translation Component ===================== The Translation component provides tools to internationalize your application. Getting Started --------------- ``` $ composer require symfony/translation ``` ```php use Symfony\Component\Translation\Translator; use Symfony\Component\Translation\Loader\ArrayLoader; $translator = new Translator('fr_FR'); $translator->addLoader('array', new ArrayLoader()); $translator->addResource('array', [ 'Hello World!' => 'Bonjour !', ], 'fr_FR'); echo $translator->trans('Hello World!'); // outputs « Bonjour ! » ``` Sponsor ------- The Translation component for Symfony 6.3 is [backed][1] by: * [Crowdin][2], a cloud-based localization management software helping teams to go global and stay agile. Help Symfony by [sponsoring][3] its development! Resources --------- * [Documentation](https://symfony.com/doc/current/translation.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) [1]: https://symfony.com/backers [2]: https://crowdin.com [3]: https://symfony.com/sponsor Loader/FileLoader.php 0000644 00000003275 15111175536 0010505 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ abstract class FileLoader extends ArrayLoader { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } $messages = $this->loadResource($resource); // empty resource $messages ??= []; // not an array if (!\is_array($messages)) { throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } $catalogue = parent::load($messages, $locale, $domain); if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } /** * @throws InvalidResourceException if stream content has an invalid format */ abstract protected function loadResource(string $resource): array; } Loader/YamlFileLoader.php 0000644 00000003114 15111175536 0011320 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; use Symfony\Component\Yaml\Yaml; /** * YamlFileLoader loads translations from Yaml files. * * @author Fabien Potencier <fabien@symfony.com> */ class YamlFileLoader extends FileLoader { private $yamlParser; protected function loadResource(string $resource): array { if (null === $this->yamlParser) { if (!class_exists(\Symfony\Component\Yaml\Parser::class)) { throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.'); } $this->yamlParser = new YamlParser(); } try { $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT); } catch (ParseException $e) { throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e); } if (null !== $messages && !\is_array($messages)) { throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource)); } return $messages ?: []; } } Loader/IcuResFileLoader.php 0000644 00000005345 15111175536 0011620 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. * * @author stealth35 */ class IcuResFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!is_dir($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception) { $rb = null; } if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); if (class_exists(DirectoryResource::class)) { $catalogue->addResource(new DirectoryResource($resource)); } return $catalogue; } /** * Flattens an ResourceBundle. * * The scheme used is: * key { key2 { key3 { "value" } } } * Becomes: * 'key.key2.key3' => 'value' * * This function takes an array by reference and will modify it * * @param \ResourceBundle $rb The ResourceBundle that will be flattened * @param array $messages Used internally for recursive calls * @param string|null $path Current path being parsed, used internally for recursive calls */ protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null): array { foreach ($rb as $key => $value) { $nodePath = $path ? $path.'.'.$key : $key; if ($value instanceof \ResourceBundle) { $this->flatten($value, $messages, $nodePath); } else { $messages[$nodePath] = $value; } } return $messages; } } Loader/MoFileLoader.php 0000644 00000010253 15111175537 0010774 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/) */ class MoFileLoader extends FileLoader { /** * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was little endian. */ public const MO_LITTLE_ENDIAN_MAGIC = 0x950412DE; /** * Magic used for validating the format of an MO file as well as * detecting if the machine used to create that file was big endian. */ public const MO_BIG_ENDIAN_MAGIC = 0xDE120495; /** * The size of the header of an MO file in bytes. */ public const MO_HEADER_SIZE = 28; /** * Parses machine object (MO) format, independent of the machine's endian it * was created on. Both 32bit and 64bit systems are supported. */ protected function loadResource(string $resource): array { $stream = fopen($resource, 'r'); $stat = fstat($stream); if ($stat['size'] < self::MO_HEADER_SIZE) { throw new InvalidResourceException('MO stream content has an invalid format.'); } $magic = unpack('V1', fread($stream, 4)); $magic = hexdec(substr(dechex(current($magic)), -8)); if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) { $isBigEndian = false; } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) { $isBigEndian = true; } else { throw new InvalidResourceException('MO stream content has an invalid format.'); } // formatRevision $this->readLong($stream, $isBigEndian); $count = $this->readLong($stream, $isBigEndian); $offsetId = $this->readLong($stream, $isBigEndian); $offsetTranslated = $this->readLong($stream, $isBigEndian); // sizeHashes $this->readLong($stream, $isBigEndian); // offsetHashes $this->readLong($stream, $isBigEndian); $messages = []; for ($i = 0; $i < $count; ++$i) { $pluralId = null; $translated = null; fseek($stream, $offsetId + $i * 8); $length = $this->readLong($stream, $isBigEndian); $offset = $this->readLong($stream, $isBigEndian); if ($length < 1) { continue; } fseek($stream, $offset); $singularId = fread($stream, $length); if (str_contains($singularId, "\000")) { [$singularId, $pluralId] = explode("\000", $singularId); } fseek($stream, $offsetTranslated + $i * 8); $length = $this->readLong($stream, $isBigEndian); $offset = $this->readLong($stream, $isBigEndian); if ($length < 1) { continue; } fseek($stream, $offset); $translated = fread($stream, $length); if (str_contains($translated, "\000")) { $translated = explode("\000", $translated); } $ids = ['singular' => $singularId, 'plural' => $pluralId]; $item = compact('ids', 'translated'); if (!empty($item['ids']['singular'])) { $id = $item['ids']['singular']; if (isset($item['ids']['plural'])) { $id .= '|'.$item['ids']['plural']; } $messages[$id] = stripcslashes(implode('|', (array) $item['translated'])); } } fclose($stream); return array_filter($messages); } /** * Reads an unsigned long from stream respecting endianness. * * @param resource $stream */ private function readLong($stream, bool $isBigEndian): int { $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4)); $result = current($result); return (int) substr($result, -8); } } Loader/PhpFileLoader.php 0000644 00000002025 15111175537 0011146 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * PhpFileLoader loads translations from PHP files returning an array of translations. * * @author Fabien Potencier <fabien@symfony.com> */ class PhpFileLoader extends FileLoader { private static ?array $cache = []; protected function loadResource(string $resource): array { if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOL) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOL))) { self::$cache = null; } if (null === self::$cache) { return require $resource; } return self::$cache[$resource] ??= require $resource; } } Loader/ArrayLoader.php 0000644 00000002717 15111175537 0010705 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\MessageCatalogue; /** * ArrayLoader loads translations from a PHP array. * * @author Fabien Potencier <fabien@symfony.com> */ class ArrayLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { $resource = $this->flatten($resource); $catalogue = new MessageCatalogue($locale); $catalogue->add($resource, $domain); return $catalogue; } /** * Flattens an nested array of translations. * * The scheme used is: * 'key' => ['key2' => ['key3' => 'value']] * Becomes: * 'key.key2.key3' => 'value' */ private function flatten(array $messages): array { $result = []; foreach ($messages as $key => $value) { if (\is_array($value)) { foreach ($this->flatten($value) as $k => $v) { if (null !== $v) { $result[$key.'.'.$k] = $v; } } } elseif (null !== $value) { $result[$key] = $value; } } return $result; } } Loader/QtFileLoader.php 0000644 00000005326 15111175537 0011012 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; /** * QtFileLoader loads translations from QT Translations XML files. * * @author Benjamin Eberlei <kontakt@beberlei.de> */ class QtFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!class_exists(XmlUtils::class)) { throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.'); } if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $dom = XmlUtils::loadFile($resource); } catch (\InvalidArgumentException $e) { throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e); } $internalErrors = libxml_use_internal_errors(true); libxml_clear_errors(); $xpath = new \DOMXPath($dom); $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]'); $catalogue = new MessageCatalogue($locale); if (1 == $nodes->length) { $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message'); foreach ($translations as $translation) { $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue; if (!empty($translationValue)) { $catalogue->set( (string) $translation->getElementsByTagName('source')->item(0)->nodeValue, $translationValue, $domain ); } } if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } } libxml_use_internal_errors($internalErrors); return $catalogue; } } Loader/IcuDatFileLoader.php 0000644 00000003444 15111175537 0011576 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * IcuResFileLoader loads translations from a resource bundle. * * @author stealth35 */ class IcuDatFileLoader extends IcuResFileLoader { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!stream_is_local($resource.'.dat')) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource.'.dat')) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } try { $rb = new \ResourceBundle($locale, $resource); } catch (\Exception) { $rb = null; } if (!$rb) { throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource)); } elseif (intl_is_failure($rb->getErrorCode())) { throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode()); } $messages = $this->flatten($rb); $catalogue = new MessageCatalogue($locale); $catalogue->add($messages, $domain); if (class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource.'.dat')); } return $catalogue; } } Loader/CsvFileLoader.php 0000644 00000003336 15111175537 0011160 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\NotFoundResourceException; /** * CsvFileLoader loads translations from CSV files. * * @author Saša Stamenković <umpirsky@gmail.com> */ class CsvFileLoader extends FileLoader { private string $delimiter = ';'; private string $enclosure = '"'; private string $escape = '\\'; protected function loadResource(string $resource): array { $messages = []; try { $file = new \SplFileObject($resource, 'rb'); } catch (\RuntimeException $e) { throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e); } $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY); $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape); foreach ($file as $data) { if (false === $data) { continue; } if (!str_starts_with($data[0], '#') && isset($data[1]) && 2 === \count($data)) { $messages[$data[0]] = $data[1]; } } return $messages; } /** * Sets the delimiter, enclosure, and escape character for CSV. * * @return void */ public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; $this->escape = $escape; } } Loader/XliffFileLoader.php 0000644 00000021214 15111175537 0011470 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\Exception\InvalidXmlException; use Symfony\Component\Config\Util\Exception\XmlParsingException; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Util\XliffUtils; /** * XliffFileLoader loads translations from XLIFF files. * * @author Fabien Potencier <fabien@symfony.com> */ class XliffFileLoader implements LoaderInterface { public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue { if (!class_exists(XmlUtils::class)) { throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.'); } if (!$this->isXmlString($resource)) { if (!stream_is_local($resource)) { throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource)); } if (!file_exists($resource)) { throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource)); } if (!is_file($resource)) { throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource)); } } try { if ($this->isXmlString($resource)) { $dom = XmlUtils::parse($resource); } else { $dom = XmlUtils::loadFile($resource); } } catch (\InvalidArgumentException|XmlParsingException|InvalidXmlException $e) { throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e); } if ($errors = XliffUtils::validateSchema($dom)) { throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors)); } $catalogue = new MessageCatalogue($locale); $this->extract($dom, $catalogue, $domain); if (is_file($resource) && class_exists(FileResource::class)) { $catalogue->addResource(new FileResource($resource)); } return $catalogue; } private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xliffVersion = XliffUtils::getVersionNumber($dom); if ('1.2' === $xliffVersion) { $this->extractXliff1($dom, $catalogue, $domain); } if ('2.0' === $xliffVersion) { $this->extractXliff2($dom, $catalogue, $domain); } } /** * Extract messages and metadata from DOMDocument into a MessageCatalogue. */ private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xml = simplexml_import_dom($dom); $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; $namespace = 'urn:oasis:names:tc:xliff:document:1.2'; $xml->registerXPathNamespace('xliff', $namespace); foreach ($xml->xpath('//xliff:file') as $file) { $fileAttributes = $file->attributes(); $file->registerXPathNamespace('xliff', $namespace); foreach ($file->xpath('.//xliff:prop') as $prop) { $catalogue->setCatalogueMetadata($prop->attributes()['prop-type'], (string) $prop, $domain); } foreach ($file->xpath('.//xliff:trans-unit') as $translation) { $attributes = $translation->attributes(); if (!(isset($attributes['resname']) || isset($translation->source))) { continue; } $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source; // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding); $catalogue->set((string) $source, $target, $domain); $metadata = [ 'source' => (string) $translation->source, 'file' => [ 'original' => (string) $fileAttributes['original'], ], ]; if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) { $metadata['notes'] = $notes; } if (isset($translation->target) && $translation->target->attributes()) { $metadata['target-attributes'] = []; foreach ($translation->target->attributes() as $key => $value) { $metadata['target-attributes'][$key] = (string) $value; } } if (isset($attributes['id'])) { $metadata['id'] = (string) $attributes['id']; } $catalogue->setMetadata((string) $source, $metadata, $domain); } } } private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain): void { $xml = simplexml_import_dom($dom); $encoding = $dom->encoding ? strtoupper($dom->encoding) : null; $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0'); foreach ($xml->xpath('//xliff:unit') as $unit) { foreach ($unit->segment as $segment) { $attributes = $unit->attributes(); $source = $attributes['name'] ?? $segment->source; // If the xlf file has another encoding specified, try to convert it because // simple_xml will always return utf-8 encoded values $target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding); $catalogue->set((string) $source, $target, $domain); $metadata = []; if (isset($segment->target) && $segment->target->attributes()) { $metadata['target-attributes'] = []; foreach ($segment->target->attributes() as $key => $value) { $metadata['target-attributes'][$key] = (string) $value; } } if (isset($unit->notes)) { $metadata['notes'] = []; foreach ($unit->notes->note as $noteNode) { $note = []; foreach ($noteNode->attributes() as $key => $value) { $note[$key] = (string) $value; } $note['content'] = (string) $noteNode; $metadata['notes'][] = $note; } } $catalogue->setMetadata((string) $source, $metadata, $domain); } } } /** * Convert a UTF8 string to the specified encoding. */ private function utf8ToCharset(string $content, string $encoding = null): string { if ('UTF-8' !== $encoding && !empty($encoding)) { return mb_convert_encoding($content, $encoding, 'UTF-8'); } return $content; } private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array { $notes = []; if (null === $noteElement) { return $notes; } /** @var \SimpleXMLElement $xmlNote */ foreach ($noteElement as $xmlNote) { $noteAttributes = $xmlNote->attributes(); $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)]; if (isset($noteAttributes['priority'])) { $note['priority'] = (int) $noteAttributes['priority']; } if (isset($noteAttributes['from'])) { $note['from'] = (string) $noteAttributes['from']; } $notes[] = $note; } return $notes; } private function isXmlString(string $resource): bool { return str_starts_with($resource, '<?xml'); } } Loader/IniFileLoader.php 0000644 00000001031 15111175537 0011132 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * IniFileLoader loads translations from an ini file. * * @author stealth35 */ class IniFileLoader extends FileLoader { protected function loadResource(string $resource): array { return parse_ini_file($resource, true); } } Loader/error_log 0000644 00000013114 15111175540 0007667 0 ustar 00 [20-Nov-2025 04:02:02 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/JsonFileLoader.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/JsonFileLoader.php on line 21 [20-Nov-2025 04:10:54 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\IcuResFileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IcuDatFileLoader.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IcuDatFileLoader.php on line 24 [20-Nov-2025 04:14:45 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/XliffFileLoader.php:29 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/XliffFileLoader.php on line 29 [20-Nov-2025 04:17:56 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IcuResFileLoader.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IcuResFileLoader.php on line 24 [20-Nov-2025 06:08:39 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/MoFileLoader.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/MoFileLoader.php on line 19 [20-Nov-2025 06:10:45 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/ArrayLoader.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/ArrayLoader.php on line 21 [20-Nov-2025 07:27:39 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/XliffFileLoader.php:29 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/XliffFileLoader.php on line 29 [20-Nov-2025 07:37:48 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IniFileLoader.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/IniFileLoader.php on line 19 [20-Nov-2025 07:38:40 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/CsvFileLoader.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/CsvFileLoader.php on line 21 [20-Nov-2025 07:43:45 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/YamlFileLoader.php:25 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/YamlFileLoader.php on line 25 [20-Nov-2025 07:45:40 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/PhpFileLoader.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/PhpFileLoader.php on line 19 [20-Nov-2025 10:52:50 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/ArrayLoader.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/ArrayLoader.php on line 21 [20-Nov-2025 12:27:51 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\ArrayLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/FileLoader.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/FileLoader.php on line 22 [20-Nov-2025 15:26:10 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Loader\LoaderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/QtFileLoader.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/QtFileLoader.php on line 26 [20-Nov-2025 15:30:14 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\FileLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/PoFileLoader.php:18 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/PoFileLoader.php on line 18 [20-Nov-2025 15:43:17 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Loader\ArrayLoader" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/FileLoader.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Loader/FileLoader.php on line 22 Loader/JsonFileLoader.php 0000644 00000002771 15111175540 0011332 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * JsonFileLoader loads translations from an json file. * * @author singles */ class JsonFileLoader extends FileLoader { protected function loadResource(string $resource): array { $messages = []; if ($data = file_get_contents($resource)) { $messages = json_decode($data, true); if (0 < $errorCode = json_last_error()) { throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode)); } } return $messages; } /** * Translates JSON_ERROR_* constant into meaningful message. */ private function getJSONErrorMessage(int $errorCode): string { return match ($errorCode) { \JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', \JSON_ERROR_STATE_MISMATCH => 'Underflow or the modes mismatch', \JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', \JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', \JSON_ERROR_UTF8 => 'Malformed UTF-8 characters, possibly incorrectly encoded', default => 'Unknown error', }; } } Loader/PoFileLoader.php 0000644 00000011756 15111175540 0011002 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; /** * @copyright Copyright (c) 2010, Union of RAD https://github.com/UnionOfRAD/lithium * @copyright Copyright (c) 2012, Clemens Tolboom */ class PoFileLoader extends FileLoader { /** * Parses portable object (PO) format. * * From https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files * we should be able to parse files having: * * white-space * # translator-comments * #. extracted-comments * #: reference... * #, flag... * #| msgid previous-untranslated-string * msgid untranslated-string * msgstr translated-string * * extra or different lines are: * * #| msgctxt previous-context * #| msgid previous-untranslated-string * msgctxt context * * #| msgid previous-untranslated-string-singular * #| msgid_plural previous-untranslated-string-plural * msgid untranslated-string-singular * msgid_plural untranslated-string-plural * msgstr[0] translated-string-case-0 * ... * msgstr[N] translated-string-case-n * * The definition states: * - white-space and comments are optional. * - msgid "" that an empty singleline defines a header. * * This parser sacrifices some features of the reference implementation the * differences to that implementation are as follows. * - No support for comments spanning multiple lines. * - Translator and extracted comments are treated as being the same type. * - Message IDs are allowed to have other encodings as just US-ASCII. * * Items with an empty id are ignored. */ protected function loadResource(string $resource): array { $stream = fopen($resource, 'r'); $defaults = [ 'ids' => [], 'translated' => null, ]; $messages = []; $item = $defaults; $flags = []; while ($line = fgets($stream)) { $line = trim($line); if ('' === $line) { // Whitespace indicated current item is done if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } $item = $defaults; $flags = []; } elseif (str_starts_with($line, '#,')) { $flags = array_map('trim', explode(',', substr($line, 2))); } elseif (str_starts_with($line, 'msgid "')) { // We start a new msg so save previous // TODO: this fails when comments or contexts are added $this->addMessage($messages, $item); $item = $defaults; $item['ids']['singular'] = substr($line, 7, -1); } elseif (str_starts_with($line, 'msgstr "')) { $item['translated'] = substr($line, 8, -1); } elseif ('"' === $line[0]) { $continues = isset($item['translated']) ? 'translated' : 'ids'; if (\is_array($item[$continues])) { end($item[$continues]); $item[$continues][key($item[$continues])] .= substr($line, 1, -1); } else { $item[$continues] .= substr($line, 1, -1); } } elseif (str_starts_with($line, 'msgid_plural "')) { $item['ids']['plural'] = substr($line, 14, -1); } elseif (str_starts_with($line, 'msgstr[')) { $size = strpos($line, ']'); $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1); } } // save last item if (!\in_array('fuzzy', $flags)) { $this->addMessage($messages, $item); } fclose($stream); return $messages; } /** * Save a translation item to the messages. * * A .po file could contain by error missing plural indexes. We need to * fix these before saving them. */ private function addMessage(array &$messages, array $item): void { if (!empty($item['ids']['singular'])) { $id = stripcslashes($item['ids']['singular']); if (isset($item['ids']['plural'])) { $id .= '|'.stripcslashes($item['ids']['plural']); } $translated = (array) $item['translated']; // PO are by definition indexed so sort by index. ksort($translated); // Make sure every index is filled. end($translated); $count = key($translated); // Fill missing spots with '-'. $empties = array_fill(0, $count + 1, '-'); $translated += $empties; ksort($translated); $messages[$id] = stripcslashes(implode('|', $translated)); } } } Loader/LoaderInterface.php 0000644 00000001666 15111175540 0011523 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Loader; use Symfony\Component\Translation\Exception\InvalidResourceException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\MessageCatalogue; /** * LoaderInterface is the interface implemented by all translation loaders. * * @author Fabien Potencier <fabien@symfony.com> */ interface LoaderInterface { /** * Loads a locale. * * @throws NotFoundResourceException when the resource cannot be found * @throws InvalidResourceException when the resource cannot be loaded */ public function load(mixed $resource, string $locale, string $domain = 'messages'): MessageCatalogue; } LICENSE 0000644 00000002054 15111175540 0005552 0 ustar 00 Copyright (c) 2004-present Fabien Potencier 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. Reader/TranslationReaderInterface.php 0000644 00000001275 15111175540 0013726 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Reader; use Symfony\Component\Translation\MessageCatalogue; /** * TranslationReader reads translation messages from translation files. * * @author Tobias Nyholm <tobias.nyholm@gmail.com> */ interface TranslationReaderInterface { /** * Reads translation messages from a directory to the catalogue. * * @return void */ public function read(string $directory, MessageCatalogue $catalogue); } Reader/TranslationReader.php 0000644 00000003371 15111175540 0012104 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Reader; use Symfony\Component\Finder\Finder; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\MessageCatalogue; /** * TranslationReader reads translation messages from translation files. * * @author Michel Salib <michelsalib@hotmail.com> */ class TranslationReader implements TranslationReaderInterface { /** * Loaders used for import. * * @var array<string, LoaderInterface> */ private array $loaders = []; /** * Adds a loader to the translation extractor. * * @param string $format The format of the loader * * @return void */ public function addLoader(string $format, LoaderInterface $loader) { $this->loaders[$format] = $loader; } /** * @return void */ public function read(string $directory, MessageCatalogue $catalogue) { if (!is_dir($directory)) { return; } foreach ($this->loaders as $format => $loader) { // load any existing translation files $finder = new Finder(); $extension = $catalogue->getLocale().'.'.$format; $files = $finder->files()->name('*.'.$extension)->in($directory); foreach ($files as $file) { $domain = substr($file->getFilename(), 0, -1 * \strlen($extension) - 1); $catalogue->addCatalogue($loader->load($file->getPathname(), $catalogue->getLocale(), $domain)); } } } } TranslatorBag.php 0000644 00000006066 15111175540 0010030 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Translation\Catalogue\AbstractOperation; use Symfony\Component\Translation\Catalogue\TargetOperation; final class TranslatorBag implements TranslatorBagInterface { /** @var MessageCatalogue[] */ private array $catalogues = []; public function addCatalogue(MessageCatalogue $catalogue): void { if (null !== $existingCatalogue = $this->getCatalogue($catalogue->getLocale())) { $catalogue->addCatalogue($existingCatalogue); } $this->catalogues[$catalogue->getLocale()] = $catalogue; } public function addBag(TranslatorBagInterface $bag): void { foreach ($bag->getCatalogues() as $catalogue) { $this->addCatalogue($catalogue); } } public function getCatalogue(string $locale = null): MessageCatalogueInterface { if (null === $locale || !isset($this->catalogues[$locale])) { $this->catalogues[$locale] = new MessageCatalogue($locale); } return $this->catalogues[$locale]; } public function getCatalogues(): array { return array_values($this->catalogues); } public function diff(TranslatorBagInterface $diffBag): self { $diff = new self(); foreach ($this->catalogues as $locale => $catalogue) { if (null === $diffCatalogue = $diffBag->getCatalogue($locale)) { $diff->addCatalogue($catalogue); continue; } $operation = new TargetOperation($diffCatalogue, $catalogue); $operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::NEW_BATCH); $newCatalogue = new MessageCatalogue($locale); foreach ($catalogue->getDomains() as $domain) { $newCatalogue->add($operation->getNewMessages($domain), $domain); } $diff->addCatalogue($newCatalogue); } return $diff; } public function intersect(TranslatorBagInterface $intersectBag): self { $diff = new self(); foreach ($this->catalogues as $locale => $catalogue) { if (null === $intersectCatalogue = $intersectBag->getCatalogue($locale)) { continue; } $operation = new TargetOperation($catalogue, $intersectCatalogue); $operation->moveMessagesToIntlDomainsIfPossible(AbstractOperation::OBSOLETE_BATCH); $obsoleteCatalogue = new MessageCatalogue($locale); foreach ($operation->getDomains() as $domain) { $obsoleteCatalogue->add( array_diff($operation->getMessages($domain), $operation->getNewMessages($domain)), $domain ); } $diff->addCatalogue($obsoleteCatalogue); } return $diff; } } MessageCatalogueInterface.php 0000644 00000006625 15111175540 0012320 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Config\Resource\ResourceInterface; /** * MessageCatalogueInterface. * * @author Fabien Potencier <fabien@symfony.com> */ interface MessageCatalogueInterface { public const INTL_DOMAIN_SUFFIX = '+intl-icu'; /** * Gets the catalogue locale. */ public function getLocale(): string; /** * Gets the domains. */ public function getDomains(): array; /** * Gets the messages within a given domain. * * If $domain is null, it returns all messages. */ public function all(string $domain = null): array; /** * Sets a message translation. * * @param string $id The message id * @param string $translation The messages translation * @param string $domain The domain name * * @return void */ public function set(string $id, string $translation, string $domain = 'messages'); /** * Checks if a message has a translation. * * @param string $id The message id * @param string $domain The domain name */ public function has(string $id, string $domain = 'messages'): bool; /** * Checks if a message has a translation (it does not take into account the fallback mechanism). * * @param string $id The message id * @param string $domain The domain name */ public function defines(string $id, string $domain = 'messages'): bool; /** * Gets a message translation. * * @param string $id The message id * @param string $domain The domain name */ public function get(string $id, string $domain = 'messages'): string; /** * Sets translations for a given domain. * * @param array $messages An array of translations * @param string $domain The domain name * * @return void */ public function replace(array $messages, string $domain = 'messages'); /** * Adds translations for a given domain. * * @param array $messages An array of translations * @param string $domain The domain name * * @return void */ public function add(array $messages, string $domain = 'messages'); /** * Merges translations from the given Catalogue into the current one. * * The two catalogues must have the same locale. * * @return void */ public function addCatalogue(self $catalogue); /** * Merges translations from the given Catalogue into the current one * only when the translation does not exist. * * This is used to provide default translations when they do not exist for the current locale. * * @return void */ public function addFallbackCatalogue(self $catalogue); /** * Gets the fallback catalogue. */ public function getFallbackCatalogue(): ?self; /** * Returns an array of resources loaded to build this collection. * * @return ResourceInterface[] */ public function getResources(): array; /** * Adds a resource for this collection. * * @return void */ public function addResource(ResourceInterface $resource); } Util/XliffUtils.php 0000644 00000014473 15111175540 0010274 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Util; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\InvalidResourceException; /** * Provides some utility methods for XLIFF translation files, such as validating * their contents according to the XSD schema. * * @author Fabien Potencier <fabien@symfony.com> */ class XliffUtils { /** * Gets xliff file version based on the root "version" attribute. * * Defaults to 1.2 for backwards compatibility. * * @throws InvalidArgumentException */ public static function getVersionNumber(\DOMDocument $dom): string { /** @var \DOMNode $xliff */ foreach ($dom->getElementsByTagName('xliff') as $xliff) { $version = $xliff->attributes->getNamedItem('version'); if ($version) { return $version->nodeValue; } $namespace = $xliff->attributes->getNamedItem('xmlns'); if ($namespace) { if (0 !== substr_compare('urn:oasis:names:tc:xliff:document:', $namespace->nodeValue, 0, 34)) { throw new InvalidArgumentException(sprintf('Not a valid XLIFF namespace "%s".', $namespace)); } return substr($namespace, 34); } } // Falls back to v1.2 return '1.2'; } /** * Validates and parses the given file into a DOMDocument. * * @throws InvalidResourceException */ public static function validateSchema(\DOMDocument $dom): array { $xliffVersion = static::getVersionNumber($dom); $internalErrors = libxml_use_internal_errors(true); if ($shouldEnable = self::shouldEnableEntityLoader()) { $disableEntities = libxml_disable_entity_loader(false); } try { $isValid = @$dom->schemaValidateSource(self::getSchema($xliffVersion)); if (!$isValid) { return self::getXmlErrors($internalErrors); } } finally { if ($shouldEnable) { libxml_disable_entity_loader($disableEntities); } } $dom->normalizeDocument(); libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return []; } private static function shouldEnableEntityLoader(): bool { static $dom, $schema; if (null === $dom) { $dom = new \DOMDocument(); $dom->loadXML('<?xml version="1.0"?><test/>'); $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); register_shutdown_function(static function () use ($tmpfile) { @unlink($tmpfile); }); $schema = '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:include schemaLocation="file:///'.str_replace('\\', '/', $tmpfile).'" /> </xsd:schema>'; file_put_contents($tmpfile, '<?xml version="1.0" encoding="utf-8"?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:element name="test" type="testType" /> <xsd:complexType name="testType"/> </xsd:schema>'); } return !@$dom->schemaValidateSource($schema); } public static function getErrorsAsString(array $xmlErrors): string { $errorsAsString = ''; foreach ($xmlErrors as $error) { $errorsAsString .= sprintf("[%s %s] %s (in %s - line %d, column %d)\n", \LIBXML_ERR_WARNING === $error['level'] ? 'WARNING' : 'ERROR', $error['code'], $error['message'], $error['file'], $error['line'], $error['column'] ); } return $errorsAsString; } private static function getSchema(string $xliffVersion): string { if ('1.2' === $xliffVersion) { $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-1.2-transitional.xsd'); $xmlUri = 'http://www.w3.org/2001/xml.xsd'; } elseif ('2.0' === $xliffVersion) { $schemaSource = file_get_contents(__DIR__.'/../Resources/schemas/xliff-core-2.0.xsd'); $xmlUri = 'informativeCopiesOf3rdPartySchemas/w3c/xml.xsd'; } else { throw new InvalidArgumentException(sprintf('No support implemented for loading XLIFF version "%s".', $xliffVersion)); } return self::fixXmlLocation($schemaSource, $xmlUri); } /** * Internally changes the URI of a dependent xsd to be loaded locally. */ private static function fixXmlLocation(string $schemaSource, string $xmlUri): string { $newPath = str_replace('\\', '/', __DIR__).'/../Resources/schemas/xml.xsd'; $parts = explode('/', $newPath); $locationstart = 'file:///'; if (0 === stripos($newPath, 'phar://')) { $tmpfile = tempnam(sys_get_temp_dir(), 'symfony'); if ($tmpfile) { copy($newPath, $tmpfile); $parts = explode('/', str_replace('\\', '/', $tmpfile)); } else { array_shift($parts); $locationstart = 'phar:///'; } } $drive = '\\' === \DIRECTORY_SEPARATOR ? array_shift($parts).'/' : ''; $newPath = $locationstart.$drive.implode('/', array_map('rawurlencode', $parts)); return str_replace($xmlUri, $newPath, $schemaSource); } /** * Returns the XML errors of the internal XML parser. */ private static function getXmlErrors(bool $internalErrors): array { $errors = []; foreach (libxml_get_errors() as $error) { $errors[] = [ 'level' => \LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', 'code' => $error->code, 'message' => trim($error->message), 'file' => $error->file ?: 'n/a', 'line' => $error->line, 'column' => $error->column, ]; } libxml_clear_errors(); libxml_use_internal_errors($internalErrors); return $errors; } } Util/ArrayConverter.php 0000644 00000007271 15111175541 0011150 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Util; /** * ArrayConverter generates tree like structure from a message catalogue. * e.g. this * 'foo.bar1' => 'test1', * 'foo.bar2' => 'test2' * converts to follows: * foo: * bar1: test1 * bar2: test2. * * @author Gennady Telegin <gtelegin@gmail.com> */ class ArrayConverter { /** * Converts linear messages array to tree-like array. * For example this array('foo.bar' => 'value') will be converted to ['foo' => ['bar' => 'value']]. * * @param array $messages Linear messages array */ public static function expandToTree(array $messages): array { $tree = []; foreach ($messages as $id => $value) { $referenceToElement = &self::getElementByPath($tree, self::getKeyParts($id)); $referenceToElement = $value; unset($referenceToElement); } return $tree; } private static function &getElementByPath(array &$tree, array $parts): mixed { $elem = &$tree; $parentOfElem = null; foreach ($parts as $i => $part) { if (isset($elem[$part]) && \is_string($elem[$part])) { /* Process next case: * 'foo': 'test1', * 'foo.bar': 'test2' * * $tree['foo'] was string before we found array {bar: test2}. * Treat new element as string too, e.g. add $tree['foo.bar'] = 'test2'; */ $elem = &$elem[implode('.', \array_slice($parts, $i))]; break; } $parentOfElem = &$elem; $elem = &$elem[$part]; } if ($elem && \is_array($elem) && $parentOfElem) { /* Process next case: * 'foo.bar': 'test1' * 'foo': 'test2' * * $tree['foo'] was array = {bar: 'test1'} before we found string constant `foo`. * Cancel treating $tree['foo'] as array and cancel back it expansion, * e.g. make it $tree['foo.bar'] = 'test1' again. */ self::cancelExpand($parentOfElem, $part, $elem); } return $elem; } private static function cancelExpand(array &$tree, string $prefix, array $node): void { $prefix .= '.'; foreach ($node as $id => $value) { if (\is_string($value)) { $tree[$prefix.$id] = $value; } else { self::cancelExpand($tree, $prefix.$id, $value); } } } /** * @return string[] */ private static function getKeyParts(string $key): array { $parts = explode('.', $key); $partsCount = \count($parts); $result = []; $buffer = ''; foreach ($parts as $index => $part) { if (0 === $index && '' === $part) { $buffer = '.'; continue; } if ($index === $partsCount - 1 && '' === $part) { $buffer .= '.'; $result[] = $buffer; continue; } if (isset($parts[$index + 1]) && '' === $parts[$index + 1]) { $buffer .= $part; continue; } if ($buffer) { $result[] = $buffer.$part; $buffer = ''; continue; } $result[] = $part; } return $result; } } TranslatorBagInterface.php 0000644 00000001624 15111175541 0011645 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Translation\Exception\InvalidArgumentException; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ interface TranslatorBagInterface { /** * Gets the catalogue by locale. * * @param string|null $locale The locale or null to use the default * * @throws InvalidArgumentException If the locale contains invalid characters */ public function getCatalogue(string $locale = null): MessageCatalogueInterface; /** * Returns all catalogues of the instance. * * @return MessageCatalogueInterface[] */ public function getCatalogues(): array; } Command/XliffLintCommand.php 0000644 00000025125 15111175541 0012037 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\CI\GithubActionReporter; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Util\XliffUtils; /** * Validates XLIFF files syntax and outputs encountered errors. * * @author Grégoire Pineau <lyrixx@lyrixx.info> * @author Robin Chalas <robin.chalas@gmail.com> * @author Javier Eguiluz <javier.eguiluz@gmail.com> */ #[AsCommand(name: 'lint:xliff', description: 'Lint an XLIFF file and outputs encountered errors')] class XliffLintCommand extends Command { private string $format; private bool $displayCorrectFiles; private ?\Closure $directoryIteratorProvider; private ?\Closure $isReadableProvider; private bool $requireStrictFileNames; public function __construct(string $name = null, callable $directoryIteratorProvider = null, callable $isReadableProvider = null, bool $requireStrictFileNames = true) { parent::__construct($name); $this->directoryIteratorProvider = null === $directoryIteratorProvider ? null : $directoryIteratorProvider(...); $this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...); $this->requireStrictFileNames = $requireStrictFileNames; } /** * @return void */ protected function configure() { $this ->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN') ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions()))) ->setHelp(<<<EOF The <info>%command.name%</info> command lints an XLIFF file and outputs to STDOUT the first encountered syntax error. You can validates XLIFF contents passed from STDIN: <info>cat filename | php %command.full_name% -</info> You can also validate the syntax of a file: <info>php %command.full_name% filename</info> Or of a whole directory: <info>php %command.full_name% dirname</info> <info>php %command.full_name% dirname --format=json</info> EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $filenames = (array) $input->getArgument('filename'); $this->format = $input->getOption('format') ?? (GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt'); $this->displayCorrectFiles = $output->isVerbose(); if (['-'] === $filenames) { return $this->display($io, [$this->validate(file_get_contents('php://stdin'))]); } if (!$filenames) { throw new RuntimeException('Please provide a filename or pipe file content to STDIN.'); } $filesInfo = []; foreach ($filenames as $filename) { if (!$this->isReadable($filename)) { throw new RuntimeException(sprintf('File or directory "%s" is not readable.', $filename)); } foreach ($this->getFiles($filename) as $file) { $filesInfo[] = $this->validate(file_get_contents($file), $file); } } return $this->display($io, $filesInfo); } private function validate(string $content, string $file = null): array { $errors = []; // Avoid: Warning DOMDocument::loadXML(): Empty string supplied as input if ('' === trim($content)) { return ['file' => $file, 'valid' => true]; } $internal = libxml_use_internal_errors(true); $document = new \DOMDocument(); $document->loadXML($content); if (null !== $targetLanguage = $this->getTargetLanguageFromFile($document)) { $normalizedLocalePattern = sprintf('(%s|%s)', preg_quote($targetLanguage, '/'), preg_quote(str_replace('-', '_', $targetLanguage), '/')); // strict file names require translation files to be named '____.locale.xlf' // otherwise, both '____.locale.xlf' and 'locale.____.xlf' are allowed // also, the regexp matching must be case-insensitive, as defined for 'target-language' values // http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target-language $expectedFilenamePattern = $this->requireStrictFileNames ? sprintf('/^.*\.(?i:%s)\.(?:xlf|xliff)/', $normalizedLocalePattern) : sprintf('/^(?:.*\.(?i:%s)|(?i:%s)\..*)\.(?:xlf|xliff)/', $normalizedLocalePattern, $normalizedLocalePattern); if (0 === preg_match($expectedFilenamePattern, basename($file))) { $errors[] = [ 'line' => -1, 'column' => -1, 'message' => sprintf('There is a mismatch between the language included in the file name ("%s") and the "%s" value used in the "target-language" attribute of the file.', basename($file), $targetLanguage), ]; } } foreach (XliffUtils::validateSchema($document) as $xmlError) { $errors[] = [ 'line' => $xmlError['line'], 'column' => $xmlError['column'], 'message' => $xmlError['message'], ]; } libxml_clear_errors(); libxml_use_internal_errors($internal); return ['file' => $file, 'valid' => 0 === \count($errors), 'messages' => $errors]; } private function display(SymfonyStyle $io, array $files): int { return match ($this->format) { 'txt' => $this->displayTxt($io, $files), 'json' => $this->displayJson($io, $files), 'github' => $this->displayTxt($io, $files, true), default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))), }; } private function displayTxt(SymfonyStyle $io, array $filesInfo, bool $errorAsGithubAnnotations = false): int { $countFiles = \count($filesInfo); $erroredFiles = 0; $githubReporter = $errorAsGithubAnnotations ? new GithubActionReporter($io) : null; foreach ($filesInfo as $info) { if ($info['valid'] && $this->displayCorrectFiles) { $io->comment('<info>OK</info>'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); } elseif (!$info['valid']) { ++$erroredFiles; $io->text('<error> ERROR </error>'.($info['file'] ? sprintf(' in %s', $info['file']) : '')); $io->listing(array_map(function ($error) use ($info, $githubReporter) { // general document errors have a '-1' line number $line = -1 === $error['line'] ? null : $error['line']; $githubReporter?->error($error['message'], $info['file'], $line, null !== $line ? $error['column'] : null); return null === $line ? $error['message'] : sprintf('Line %d, Column %d: %s', $line, $error['column'], $error['message']); }, $info['messages'])); } } if (0 === $erroredFiles) { $io->success(sprintf('All %d XLIFF files contain valid syntax.', $countFiles)); } else { $io->warning(sprintf('%d XLIFF files have valid syntax and %d contain errors.', $countFiles - $erroredFiles, $erroredFiles)); } return min($erroredFiles, 1); } private function displayJson(SymfonyStyle $io, array $filesInfo): int { $errors = 0; array_walk($filesInfo, function (&$v) use (&$errors) { $v['file'] = (string) $v['file']; if (!$v['valid']) { ++$errors; } }); $io->writeln(json_encode($filesInfo, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES)); return min($errors, 1); } /** * @return iterable<\SplFileInfo> */ private function getFiles(string $fileOrDirectory): iterable { if (is_file($fileOrDirectory)) { yield new \SplFileInfo($fileOrDirectory); return; } foreach ($this->getDirectoryIterator($fileOrDirectory) as $file) { if (!\in_array($file->getExtension(), ['xlf', 'xliff'])) { continue; } yield $file; } } /** * @return iterable<\SplFileInfo> */ private function getDirectoryIterator(string $directory): iterable { $default = fn ($directory) => new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), \RecursiveIteratorIterator::LEAVES_ONLY ); if (null !== $this->directoryIteratorProvider) { return ($this->directoryIteratorProvider)($directory, $default); } return $default($directory); } private function isReadable(string $fileOrDirectory): bool { $default = fn ($fileOrDirectory) => is_readable($fileOrDirectory); if (null !== $this->isReadableProvider) { return ($this->isReadableProvider)($fileOrDirectory, $default); } return $default($fileOrDirectory); } private function getTargetLanguageFromFile(\DOMDocument $xliffContents): ?string { foreach ($xliffContents->getElementsByTagName('file')[0]->attributes ?? [] as $attribute) { if ('target-language' === $attribute->nodeName) { return $attribute->nodeValue; } } return null; } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues($this->getAvailableFormatOptions()); } } private function getAvailableFormatOptions(): array { return ['txt', 'json', 'github']; } } Command/TranslationTrait.php 0000644 00000004525 15111175541 0012144 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Command; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogueInterface; use Symfony\Component\Translation\TranslatorBag; /** * @internal */ trait TranslationTrait { private function readLocalTranslations(array $locales, array $domains, array $transPaths): TranslatorBag { $bag = new TranslatorBag(); foreach ($locales as $locale) { $catalogue = new MessageCatalogue($locale); foreach ($transPaths as $path) { $this->reader->read($path, $catalogue); } if ($domains) { foreach ($domains as $domain) { $bag->addCatalogue($this->filterCatalogue($catalogue, $domain)); } } else { $bag->addCatalogue($catalogue); } } return $bag; } private function filterCatalogue(MessageCatalogue $catalogue, string $domain): MessageCatalogue { $filteredCatalogue = new MessageCatalogue($catalogue->getLocale()); // extract intl-icu messages only $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; if ($intlMessages = $catalogue->all($intlDomain)) { $filteredCatalogue->add($intlMessages, $intlDomain); } // extract all messages and subtract intl-icu messages if ($messages = array_diff($catalogue->all($domain), $intlMessages)) { $filteredCatalogue->add($messages, $domain); } foreach ($catalogue->getResources() as $resource) { $filteredCatalogue->addResource($resource); } if ($metadata = $catalogue->getMetadata('', $intlDomain)) { foreach ($metadata as $k => $v) { $filteredCatalogue->setMetadata($k, $v, $intlDomain); } } if ($metadata = $catalogue->getMetadata('', $domain)) { foreach ($metadata as $k => $v) { $filteredCatalogue->setMetadata($k, $v, $domain); } } return $filteredCatalogue; } } Command/TranslationPushCommand.php 0000644 00000016763 15111175541 0013306 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Translation\Provider\FilteringProvider; use Symfony\Component\Translation\Provider\TranslationProviderCollection; use Symfony\Component\Translation\Reader\TranslationReaderInterface; use Symfony\Component\Translation\TranslatorBag; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ #[AsCommand(name: 'translation:push', description: 'Push translations to a given provider.')] final class TranslationPushCommand extends Command { use TranslationTrait; private TranslationProviderCollection $providers; private TranslationReaderInterface $reader; private array $transPaths; private array $enabledLocales; public function __construct(TranslationProviderCollection $providers, TranslationReaderInterface $reader, array $transPaths = [], array $enabledLocales = []) { $this->providers = $providers; $this->reader = $reader; $this->transPaths = $transPaths; $this->enabledLocales = $enabledLocales; parent::__construct(); } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('provider')) { $suggestions->suggestValues($this->providers->keys()); return; } if ($input->mustSuggestOptionValuesFor('domains')) { $provider = $this->providers->get($input->getArgument('provider')); if ($provider && method_exists($provider, 'getDomains')) { $domains = $provider->getDomains(); $suggestions->suggestValues($domains); } return; } if ($input->mustSuggestOptionValuesFor('locales')) { $suggestions->suggestValues($this->enabledLocales); } } protected function configure(): void { $keys = $this->providers->keys(); $defaultProvider = 1 === \count($keys) ? $keys[0] : null; $this ->setDefinition([ new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to push translations to.', $defaultProvider), new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with local ones (it will delete not synchronized messages).'), new InputOption('delete-missing', null, InputOption::VALUE_NONE, 'Delete translations available on provider but not locally.'), new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to push.'), new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to push.', $this->enabledLocales), ]) ->setHelp(<<<'EOF' The <info>%command.name%</> command pushes translations to the given provider. Only new translations are pushed, existing ones are not overwritten. You can overwrite existing translations by using the <comment>--force</> flag: <info>php %command.full_name% --force provider</> You can delete provider translations which are not present locally by using the <comment>--delete-missing</> flag: <info>php %command.full_name% --delete-missing provider</> Full example: <info>php %command.full_name% provider --force --delete-missing --domains=messages --domains=validators --locales=en</> This command pushes all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale. Provider translations for the specified domains and locale are deleted if they're not present locally and overwritten if it's the case. Provider translations for others domains and locales are ignored. EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $provider = $this->providers->get($input->getArgument('provider')); if (!$this->enabledLocales) { throw new InvalidArgumentException(sprintf('You must define "framework.enabled_locales" or "framework.translator.providers.%s.locales" config key in order to work with translation providers.', parse_url($provider, \PHP_URL_SCHEME))); } $io = new SymfonyStyle($input, $output); $domains = $input->getOption('domains'); $locales = $input->getOption('locales'); $force = $input->getOption('force'); $deleteMissing = $input->getOption('delete-missing'); if (!$domains && $provider instanceof FilteringProvider) { $domains = $provider->getDomains(); } // Reading local translations must be done after retrieving the domains from the provider // in order to manage only translations from configured domains $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); if (!$domains) { $domains = $this->getDomainsFromTranslatorBag($localTranslations); } if (!$deleteMissing && $force) { $provider->write($localTranslations); $io->success(sprintf('All local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); return 0; } $providerTranslations = $provider->read($domains, $locales); if ($deleteMissing) { $provider->delete($providerTranslations->diff($localTranslations)); $io->success(sprintf('Missing translations on "%s" has been deleted (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); // Read provider translations again, after missing translations deletion, // to avoid push freshly deleted translations. $providerTranslations = $provider->read($domains, $locales); } $translationsToWrite = $localTranslations->diff($providerTranslations); if ($force) { $translationsToWrite->addBag($localTranslations->intersect($providerTranslations)); } $provider->write($translationsToWrite); $io->success(sprintf('%s local translations has been sent to "%s" (for "%s" locale(s), and "%s" domain(s)).', $force ? 'All' : 'New', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); return 0; } private function getDomainsFromTranslatorBag(TranslatorBag $translatorBag): array { $domains = []; foreach ($translatorBag->getCatalogues() as $catalogue) { $domains += $catalogue->getDomains(); } return array_unique($domains); } } Command/error_log 0000644 00000004202 15111175541 0010036 0 ustar 00 [20-Nov-2025 02:26:54 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPullCommand.php:33 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPullCommand.php on line 33 [20-Nov-2025 02:31:17 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/XliffLintCommand.php:36 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/XliffLintCommand.php on line 36 [20-Nov-2025 02:33:50 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPushCommand.php:33 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPushCommand.php on line 33 [20-Nov-2025 06:13:19 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/XliffLintCommand.php:36 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/XliffLintCommand.php on line 36 [20-Nov-2025 06:28:18 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPushCommand.php:33 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPushCommand.php on line 33 [20-Nov-2025 06:30:11 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Console\Command\Command" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPullCommand.php:33 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Command/TranslationPullCommand.php on line 33 Command/TranslationPullCommand.php 0000644 00000016701 15111175541 0013273 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Command; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Completion\CompletionInput; use Symfony\Component\Console\Completion\CompletionSuggestions; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Translation\Catalogue\TargetOperation; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Provider\TranslationProviderCollection; use Symfony\Component\Translation\Reader\TranslationReaderInterface; use Symfony\Component\Translation\Writer\TranslationWriterInterface; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ #[AsCommand(name: 'translation:pull', description: 'Pull translations from a given provider.')] final class TranslationPullCommand extends Command { use TranslationTrait; private TranslationProviderCollection $providerCollection; private TranslationWriterInterface $writer; private TranslationReaderInterface $reader; private string $defaultLocale; private array $transPaths; private array $enabledLocales; public function __construct(TranslationProviderCollection $providerCollection, TranslationWriterInterface $writer, TranslationReaderInterface $reader, string $defaultLocale, array $transPaths = [], array $enabledLocales = []) { $this->providerCollection = $providerCollection; $this->writer = $writer; $this->reader = $reader; $this->defaultLocale = $defaultLocale; $this->transPaths = $transPaths; $this->enabledLocales = $enabledLocales; parent::__construct(); } public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void { if ($input->mustSuggestArgumentValuesFor('provider')) { $suggestions->suggestValues($this->providerCollection->keys()); return; } if ($input->mustSuggestOptionValuesFor('domains')) { $provider = $this->providerCollection->get($input->getArgument('provider')); if (method_exists($provider, 'getDomains')) { $suggestions->suggestValues($provider->getDomains()); } return; } if ($input->mustSuggestOptionValuesFor('locales')) { $suggestions->suggestValues($this->enabledLocales); return; } if ($input->mustSuggestOptionValuesFor('format')) { $suggestions->suggestValues(['php', 'xlf', 'xlf12', 'xlf20', 'po', 'mo', 'yml', 'yaml', 'ts', 'csv', 'json', 'ini', 'res']); } } protected function configure(): void { $keys = $this->providerCollection->keys(); $defaultProvider = 1 === \count($keys) ? $keys[0] : null; $this ->setDefinition([ new InputArgument('provider', null !== $defaultProvider ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'The provider to pull translations from.', $defaultProvider), new InputOption('force', null, InputOption::VALUE_NONE, 'Override existing translations with provider ones (it will delete not synchronized messages).'), new InputOption('intl-icu', null, InputOption::VALUE_NONE, 'Associated to --force option, it will write messages in "%domain%+intl-icu.%locale%.xlf" files.'), new InputOption('domains', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the domains to pull.'), new InputOption('locales', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Specify the locales to pull.'), new InputOption('format', null, InputOption::VALUE_OPTIONAL, 'Override the default output format.', 'xlf12'), ]) ->setHelp(<<<'EOF' The <info>%command.name%</> command pulls translations from the given provider. Only new translations are pulled, existing ones are not overwritten. You can overwrite existing translations (and remove the missing ones on local side) by using the <comment>--force</> flag: <info>php %command.full_name% --force provider</> Full example: <info>php %command.full_name% provider --force --domains=messages --domains=validators --locales=en</> This command pulls all translations associated with the <comment>messages</> and <comment>validators</> domains for the <comment>en</> locale. Local translations for the specified domains and locale are deleted if they're not present on the provider and overwritten if it's the case. Local translations for others domains and locales are ignored. EOF ) ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $provider = $this->providerCollection->get($input->getArgument('provider')); $force = $input->getOption('force'); $intlIcu = $input->getOption('intl-icu'); $locales = $input->getOption('locales') ?: $this->enabledLocales; $domains = $input->getOption('domains'); $format = $input->getOption('format'); $xliffVersion = '1.2'; if ($intlIcu && !$force) { $io->note('--intl-icu option only has an effect when used with --force. Here, it will be ignored.'); } switch ($format) { case 'xlf20': $xliffVersion = '2.0'; // no break case 'xlf12': $format = 'xlf'; } $writeOptions = [ 'path' => end($this->transPaths), 'xliff_version' => $xliffVersion, 'default_locale' => $this->defaultLocale, ]; if (!$domains) { $domains = $provider->getDomains(); } $providerTranslations = $provider->read($domains, $locales); if ($force) { foreach ($providerTranslations->getCatalogues() as $catalogue) { $operation = new TargetOperation(new MessageCatalogue($catalogue->getLocale()), $catalogue); if ($intlIcu) { $operation->moveMessagesToIntlDomainsIfPossible(); } $this->writer->write($operation->getResult(), $format, $writeOptions); } $io->success(sprintf('Local translations has been updated from "%s" (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); return 0; } $localTranslations = $this->readLocalTranslations($locales, $domains, $this->transPaths); // Append pulled translations to local ones. $localTranslations->addBag($providerTranslations->diff($localTranslations)); foreach ($localTranslations->getCatalogues() as $catalogue) { $this->writer->write($catalogue, $format, $writeOptions); } $io->success(sprintf('New translations from "%s" has been written locally (for "%s" locale(s), and "%s" domain(s)).', parse_url($provider, \PHP_URL_SCHEME), implode(', ', $locales), implode(', ', $domains))); return 0; } } DataCollectorTranslator.php 0000644 00000010734 15111175541 0012055 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\HttpKernel\CacheWarmer\WarmableInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class DataCollectorTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface, WarmableInterface { public const MESSAGE_DEFINED = 0; public const MESSAGE_MISSING = 1; public const MESSAGE_EQUALS_FALLBACK = 2; private TranslatorInterface $translator; private array $messages = []; /** * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator */ public function __construct(TranslatorInterface $translator) { if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); } $this->translator = $translator; } public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string { $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->collectMessage($locale, $domain, $id, $trans, $parameters); return $trans; } /** * @return void */ public function setLocale(string $locale) { $this->translator->setLocale($locale); } public function getLocale(): string { return $this->translator->getLocale(); } public function getCatalogue(string $locale = null): MessageCatalogueInterface { return $this->translator->getCatalogue($locale); } public function getCatalogues(): array { return $this->translator->getCatalogues(); } /** * @return string[] */ public function warmUp(string $cacheDir): array { if ($this->translator instanceof WarmableInterface) { return (array) $this->translator->warmUp($cacheDir); } return []; } /** * Gets the fallback locales. */ public function getFallbackLocales(): array { if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { return $this->translator->getFallbackLocales(); } return []; } /** * Passes through all unknown calls onto the translator object. */ public function __call(string $method, array $args) { return $this->translator->{$method}(...$args); } public function getCollectedMessages(): array { return $this->messages; } private function collectMessage(?string $locale, ?string $domain, string $id, string $translation, ?array $parameters = []): void { $domain ??= 'messages'; $catalogue = $this->translator->getCatalogue($locale); $locale = $catalogue->getLocale(); $fallbackLocale = null; if ($catalogue->defines($id, $domain)) { $state = self::MESSAGE_DEFINED; } elseif ($catalogue->has($id, $domain)) { $state = self::MESSAGE_EQUALS_FALLBACK; $fallbackCatalogue = $catalogue->getFallbackCatalogue(); while ($fallbackCatalogue) { if ($fallbackCatalogue->defines($id, $domain)) { $fallbackLocale = $fallbackCatalogue->getLocale(); break; } $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); } } else { $state = self::MESSAGE_MISSING; } $this->messages[] = [ 'locale' => $locale, 'fallbackLocale' => $fallbackLocale, 'domain' => $domain, 'id' => $id, 'translation' => $translation, 'parameters' => $parameters, 'state' => $state, 'transChoiceNumber' => isset($parameters['%count%']) && is_numeric($parameters['%count%']) ? $parameters['%count%'] : null, ]; } } Writer/TranslationWriterInterface.php 0000644 00000001710 15111175541 0014045 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Writer; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\MessageCatalogue; /** * TranslationWriter writes translation messages. * * @author Michel Salib <michelsalib@hotmail.com> */ interface TranslationWriterInterface { /** * Writes translation from the catalogue according to the selected format. * * @param string $format The format to use to dump the messages * @param array $options Options that are passed to the dumper * * @return void * * @throws InvalidArgumentException */ public function write(MessageCatalogue $catalogue, string $format, array $options = []); } Writer/TranslationWriter.php 0000644 00000004172 15111175542 0012232 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Writer; use Symfony\Component\Translation\Dumper\DumperInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; /** * TranslationWriter writes translation messages. * * @author Michel Salib <michelsalib@hotmail.com> */ class TranslationWriter implements TranslationWriterInterface { /** * @var array<string, DumperInterface> */ private array $dumpers = []; /** * Adds a dumper to the writer. * * @return void */ public function addDumper(string $format, DumperInterface $dumper) { $this->dumpers[$format] = $dumper; } /** * Obtains the list of supported formats. */ public function getFormats(): array { return array_keys($this->dumpers); } /** * Writes translation from the catalogue according to the selected format. * * @param string $format The format to use to dump the messages * @param array $options Options that are passed to the dumper * * @return void * * @throws InvalidArgumentException */ public function write(MessageCatalogue $catalogue, string $format, array $options = []) { if (!isset($this->dumpers[$format])) { throw new InvalidArgumentException(sprintf('There is no dumper associated with format "%s".', $format)); } // get the right dumper $dumper = $this->dumpers[$format]; if (isset($options['path']) && !is_dir($options['path']) && !@mkdir($options['path'], 0777, true) && !is_dir($options['path'])) { throw new RuntimeException(sprintf('Translation Writer was not able to create directory "%s".', $options['path'])); } // save $dumper->dump($catalogue, $options); } } Writer/error_log 0000644 00000001372 15111175542 0007742 0 ustar 00 [20-Nov-2025 04:01:02 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Writer\TranslationWriterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Writer/TranslationWriter.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Writer/TranslationWriter.php on line 24 [20-Nov-2025 08:52:44 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Writer\TranslationWriterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Writer/TranslationWriter.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Writer/TranslationWriter.php on line 24 Extractor/PhpStringTokenParser.php 0000644 00000010546 15111175542 0013334 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated.', PhpStringTokenParser::class); /* * The following is derived from code at http://github.com/nikic/PHP-Parser * * Copyright (c) 2011 by Nikita Popov * * Some rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * * The names of the contributors may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @deprecated since Symfony 6.2 */ class PhpStringTokenParser { protected static $replacements = [ '\\' => '\\', '$' => '$', 'n' => "\n", 'r' => "\r", 't' => "\t", 'f' => "\f", 'v' => "\v", 'e' => "\x1B", ]; /** * Parses a string token. * * @param string $str String token content */ public static function parse(string $str): string { $bLength = 0; if ('b' === $str[0]) { $bLength = 1; } if ('\'' === $str[$bLength]) { return str_replace( ['\\\\', '\\\''], ['\\', '\''], substr($str, $bLength + 1, -1) ); } else { return self::parseEscapeSequences(substr($str, $bLength + 1, -1), '"'); } } /** * Parses escape sequences in strings (all string types apart from single quoted). * * @param string $str String without quotes * @param string|null $quote Quote type */ public static function parseEscapeSequences(string $str, string $quote = null): string { if (null !== $quote) { $str = str_replace('\\'.$quote, $quote, $str); } return preg_replace_callback( '~\\\\([\\\\$nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3})~', [__CLASS__, 'parseCallback'], $str ); } private static function parseCallback(array $matches): string { $str = $matches[1]; if (isset(self::$replacements[$str])) { return self::$replacements[$str]; } elseif ('x' === $str[0] || 'X' === $str[0]) { return \chr(hexdec($str)); } else { return \chr(octdec($str)); } } /** * Parses a constant doc string. * * @param string $startToken Doc string start token content (<<<SMTHG) * @param string $str String token content */ public static function parseDocString(string $startToken, string $str): string { // strip last newline (thanks tokenizer for sticking it into the string!) $str = preg_replace('~(\r\n|\n|\r)$~', '', $str); // nowdoc string if (str_contains($startToken, '\'')) { return $str; } return self::parseEscapeSequences($str, null); } } Extractor/ChainExtractor.php 0000644 00000002465 15111175542 0012157 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; use Symfony\Component\Translation\MessageCatalogue; /** * ChainExtractor extracts translation messages from template files. * * @author Michel Salib <michelsalib@hotmail.com> */ class ChainExtractor implements ExtractorInterface { /** * The extractors. * * @var ExtractorInterface[] */ private array $extractors = []; /** * Adds a loader to the translation extractor. * * @return void */ public function addExtractor(string $format, ExtractorInterface $extractor) { $this->extractors[$format] = $extractor; } /** * @return void */ public function setPrefix(string $prefix) { foreach ($this->extractors as $extractor) { $extractor->setPrefix($prefix); } } /** * @return void */ public function extract(string|iterable $directory, MessageCatalogue $catalogue) { foreach ($this->extractors as $extractor) { $extractor->extract($directory, $catalogue); } } } Extractor/ExtractorInterface.php 0000644 00000002023 15111175542 0013023 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; use Symfony\Component\Translation\MessageCatalogue; /** * Extracts translation messages from a directory or files to the catalogue. * New found messages are injected to the catalogue using the prefix. * * @author Michel Salib <michelsalib@hotmail.com> */ interface ExtractorInterface { /** * Extracts translation messages from files, a file or a directory to the catalogue. * * @param string|iterable<string> $resource Files, a file or a directory * * @return void */ public function extract(string|iterable $resource, MessageCatalogue $catalogue); /** * Sets the prefix that should be used for new found messages. * * @return void */ public function setPrefix(string $prefix); } Extractor/Visitor/AbstractVisitor.php 0000644 00000007740 15111175542 0014024 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor\Visitor; use PhpParser\Node; use Symfony\Component\Translation\MessageCatalogue; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ abstract class AbstractVisitor { private MessageCatalogue $catalogue; private \SplFileInfo $file; private string $messagePrefix; public function initialize(MessageCatalogue $catalogue, \SplFileInfo $file, string $messagePrefix): void { $this->catalogue = $catalogue; $this->file = $file; $this->messagePrefix = $messagePrefix; } protected function addMessageToCatalogue(string $message, ?string $domain, int $line): void { $domain ??= 'messages'; $this->catalogue->set($message, $this->messagePrefix.$message, $domain); $metadata = $this->catalogue->getMetadata($message, $domain) ?? []; $normalizedFilename = preg_replace('{[\\\\/]+}', '/', $this->file); $metadata['sources'][] = $normalizedFilename.':'.$line; $this->catalogue->setMetadata($message, $metadata, $domain); } protected function getStringArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node, int|string $index, bool $indexIsRegex = false): array { if (\is_string($index)) { return $this->getStringNamedArguments($node, $index, $indexIsRegex); } $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; if (!($arg = $args[$index] ?? null) instanceof Node\Arg) { return []; } return (array) $this->getStringValue($arg->value); } protected function hasNodeNamedArguments(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): bool { $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; foreach ($args as $arg) { if ($arg instanceof Node\Arg && null !== $arg->name) { return true; } } return false; } protected function nodeFirstNamedArgumentIndex(Node\Expr\CallLike|Node\Attribute|Node\Expr\New_ $node): int { $args = $node instanceof Node\Expr\CallLike ? $node->getRawArgs() : $node->args; foreach ($args as $i => $arg) { if ($arg instanceof Node\Arg && null !== $arg->name) { return $i; } } return \PHP_INT_MAX; } private function getStringNamedArguments(Node\Expr\CallLike|Node\Attribute $node, string $argumentName = null, bool $isArgumentNamePattern = false): array { $args = $node instanceof Node\Expr\CallLike ? $node->getArgs() : $node->args; $argumentValues = []; foreach ($args as $arg) { if (!$isArgumentNamePattern && $arg->name?->toString() === $argumentName) { $argumentValues[] = $this->getStringValue($arg->value); } elseif ($isArgumentNamePattern && preg_match($argumentName, $arg->name?->toString() ?? '') > 0) { $argumentValues[] = $this->getStringValue($arg->value); } } return array_filter($argumentValues); } private function getStringValue(Node $node): ?string { if ($node instanceof Node\Scalar\String_) { return $node->value; } if ($node instanceof Node\Expr\BinaryOp\Concat) { if (null === $left = $this->getStringValue($node->left)) { return null; } if (null === $right = $this->getStringValue($node->right)) { return null; } return $left.$right; } if ($node instanceof Node\Expr\Assign && $node->expr instanceof Node\Scalar\String_) { return $node->expr->value; } return null; } } Extractor/Visitor/ConstraintVisitor.php 0000644 00000005553 15111175542 0014405 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor\Visitor; use PhpParser\Node; use PhpParser\NodeVisitor; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> * * Code mostly comes from https://github.com/php-translation/extractor/blob/master/src/Visitor/Php/Symfony/Constraint.php */ final class ConstraintVisitor extends AbstractVisitor implements NodeVisitor { public function __construct( private readonly array $constraintClassNames = [] ) { } public function beforeTraverse(array $nodes): ?Node { return null; } public function enterNode(Node $node): ?Node { if (!$node instanceof Node\Expr\New_ && !$node instanceof Node\Attribute) { return null; } $className = $node instanceof Node\Attribute ? $node->name : $node->class; if (!$className instanceof Node\Name) { return null; } $parts = $className->parts; $isConstraintClass = false; foreach ($parts as $part) { if (\in_array($part, $this->constraintClassNames, true)) { $isConstraintClass = true; break; } } if (!$isConstraintClass) { return null; } $arg = $node->args[0] ?? null; if (!$arg instanceof Node\Arg) { return null; } if ($this->hasNodeNamedArguments($node)) { $messages = $this->getStringArguments($node, '/message/i', true); } else { if (!$arg->value instanceof Node\Expr\Array_) { // There is no way to guess which argument is a message to be translated. return null; } $messages = []; $options = $arg->value; /** @var Node\Expr\ArrayItem $item */ foreach ($options->items as $item) { if (!$item->key instanceof Node\Scalar\String_) { continue; } if (false === stripos($item->key->value ?? '', 'message')) { continue; } if (!$item->value instanceof Node\Scalar\String_) { continue; } $messages[] = $item->value->value; break; } } foreach ($messages as $message) { $this->addMessageToCatalogue($message, 'validators', $node->getStartLine()); } return null; } public function leaveNode(Node $node): ?Node { return null; } public function afterTraverse(array $nodes): ?Node { return null; } } Extractor/Visitor/TransMethodVisitor.php 0000644 00000003300 15111175542 0014475 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor\Visitor; use PhpParser\Node; use PhpParser\NodeVisitor; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ final class TransMethodVisitor extends AbstractVisitor implements NodeVisitor { public function beforeTraverse(array $nodes): ?Node { return null; } public function enterNode(Node $node): ?Node { if (!$node instanceof Node\Expr\MethodCall && !$node instanceof Node\Expr\FuncCall) { return null; } if (!\is_string($node->name) && !$node->name instanceof Node\Identifier && !$node->name instanceof Node\Name) { return null; } $name = (string) $node->name; if ('trans' === $name || 't' === $name) { $firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node); if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) { return null; } $domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null; foreach ($messages as $message) { $this->addMessageToCatalogue($message, $domain, $node->getStartLine()); } } return null; } public function leaveNode(Node $node): ?Node { return null; } public function afterTraverse(array $nodes): ?Node { return null; } } Extractor/Visitor/TranslatableMessageVisitor.php 0000644 00000003116 15111175542 0016173 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor\Visitor; use PhpParser\Node; use PhpParser\NodeVisitor; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ final class TranslatableMessageVisitor extends AbstractVisitor implements NodeVisitor { public function beforeTraverse(array $nodes): ?Node { return null; } public function enterNode(Node $node): ?Node { if (!$node instanceof Node\Expr\New_) { return null; } if (!($className = $node->class) instanceof Node\Name) { return null; } if (!\in_array('TranslatableMessage', $className->parts, true)) { return null; } $firstNamedArgumentIndex = $this->nodeFirstNamedArgumentIndex($node); if (!$messages = $this->getStringArguments($node, 0 < $firstNamedArgumentIndex ? 0 : 'message')) { return null; } $domain = $this->getStringArguments($node, 2 < $firstNamedArgumentIndex ? 2 : 'domain')[0] ?? null; foreach ($messages as $message) { $this->addMessageToCatalogue($message, $domain, $node->getStartLine()); } return null; } public function leaveNode(Node $node): ?Node { return null; } public function afterTraverse(array $nodes): ?Node { return null; } } Extractor/AbstractFileExtractor.php 0000644 00000003316 15111175543 0013475 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; use Symfony\Component\Translation\Exception\InvalidArgumentException; /** * Base class used by classes that extract translation messages from files. * * @author Marcos D. Sánchez <marcosdsanchez@gmail.com> */ abstract class AbstractFileExtractor { protected function extractFiles(string|iterable $resource): iterable { if (is_iterable($resource)) { $files = []; foreach ($resource as $file) { if ($this->canBeExtracted($file)) { $files[] = $this->toSplFileInfo($file); } } } elseif (is_file($resource)) { $files = $this->canBeExtracted($resource) ? [$this->toSplFileInfo($resource)] : []; } else { $files = $this->extractFromDirectory($resource); } return $files; } private function toSplFileInfo(string $file): \SplFileInfo { return new \SplFileInfo($file); } /** * @throws InvalidArgumentException */ protected function isFile(string $file): bool { if (!is_file($file)) { throw new InvalidArgumentException(sprintf('The "%s" file does not exist.', $file)); } return true; } /** * @return bool */ abstract protected function canBeExtracted(string $file); /** * @return iterable */ abstract protected function extractFromDirectory(string|array $resource); } Extractor/error_log 0000644 00000005762 15111175543 0010451 0 ustar 00 [19-Nov-2025 01:14:48 UTC] PHP Fatal error: Uncaught Error: Call to undefined function Symfony\Component\Translation\Extractor\trigger_deprecation() in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpStringTokenParser.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpStringTokenParser.php on line 14 [19-Nov-2025 01:15:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Extractor\AbstractFileExtractor" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpAstExtractor.php:27 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpAstExtractor.php on line 27 [19-Nov-2025 01:16:02 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Extractor\ExtractorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/ChainExtractor.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/ChainExtractor.php on line 21 [19-Nov-2025 01:17:30 UTC] PHP Fatal error: Uncaught Error: Call to undefined function Symfony\Component\Translation\Extractor\trigger_deprecation() in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpExtractor.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpExtractor.php on line 14 [19-Nov-2025 09:05:32 UTC] PHP Fatal error: Uncaught Error: Call to undefined function Symfony\Component\Translation\Extractor\trigger_deprecation() in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpStringTokenParser.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpStringTokenParser.php on line 14 [19-Nov-2025 09:06:51 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Extractor\AbstractFileExtractor" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpAstExtractor.php:27 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpAstExtractor.php on line 27 [19-Nov-2025 09:08:16 UTC] PHP Fatal error: Uncaught Error: Call to undefined function Symfony\Component\Translation\Extractor\trigger_deprecation() in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpExtractor.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/PhpExtractor.php on line 14 [19-Nov-2025 09:08:25 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Extractor\ExtractorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/ChainExtractor.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Extractor/ChainExtractor.php on line 21 Extractor/PhpAstExtractor.php 0000644 00000005314 15111175543 0012331 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; use PhpParser\NodeTraverser; use PhpParser\NodeVisitor; use PhpParser\Parser; use PhpParser\ParserFactory; use Symfony\Component\Finder\Finder; use Symfony\Component\Translation\Extractor\Visitor\AbstractVisitor; use Symfony\Component\Translation\MessageCatalogue; /** * PhpAstExtractor extracts translation messages from a PHP AST. * * @author Mathieu Santostefano <msantostefano@protonmail.com> */ final class PhpAstExtractor extends AbstractFileExtractor implements ExtractorInterface { private Parser $parser; public function __construct( /** * @param iterable<AbstractVisitor&NodeVisitor> $visitors */ private readonly iterable $visitors, private string $prefix = '', ) { if (!class_exists(ParserFactory::class)) { throw new \LogicException(sprintf('You cannot use "%s" as the "nikic/php-parser" package is not installed. Try running "composer require nikic/php-parser".', static::class)); } $this->parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); } public function extract(iterable|string $resource, MessageCatalogue $catalogue): void { foreach ($this->extractFiles($resource) as $file) { $traverser = new NodeTraverser(); /** @var AbstractVisitor&NodeVisitor $visitor */ foreach ($this->visitors as $visitor) { $visitor->initialize($catalogue, $file, $this->prefix); $traverser->addVisitor($visitor); } $nodes = $this->parser->parse(file_get_contents($file)); $traverser->traverse($nodes); } } public function setPrefix(string $prefix): void { $this->prefix = $prefix; } protected function canBeExtracted(string $file): bool { return 'php' === pathinfo($file, \PATHINFO_EXTENSION) && $this->isFile($file) && preg_match('/\bt\(|->trans\(|TranslatableMessage|Symfony\\\\Component\\\\Validator\\\\Constraints/i', file_get_contents($file)); } protected function extractFromDirectory(array|string $resource): iterable|Finder { if (!class_exists(Finder::class)) { throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class)); } return (new Finder())->files()->name('*.php')->in($resource); } } Extractor/PhpExtractor.php 0000644 00000022474 15111175543 0011667 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Extractor; trigger_deprecation('symfony/translation', '6.2', '"%s" is deprecated, use "%s" instead.', PhpExtractor::class, PhpAstExtractor::class); use Symfony\Component\Finder\Finder; use Symfony\Component\Translation\MessageCatalogue; /** * PhpExtractor extracts translation messages from a PHP template. * * @author Michel Salib <michelsalib@hotmail.com> * * @deprecated since Symfony 6.2, use the PhpAstExtractor instead */ class PhpExtractor extends AbstractFileExtractor implements ExtractorInterface { public const MESSAGE_TOKEN = 300; public const METHOD_ARGUMENTS_TOKEN = 1000; public const DOMAIN_TOKEN = 1001; /** * Prefix for new found message. */ private string $prefix = ''; /** * The sequence that captures translation messages. */ protected $sequences = [ [ '->', 'trans', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN, ], [ '->', 'trans', '(', self::MESSAGE_TOKEN, ], [ 'new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN, ], [ 'new', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ], [ 'new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN, ], [ 'new', '\Symfony\Component\Translation\TranslatableMessage', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN, ], [ 'new', '\\', 'Symfony', '\\', 'Component', '\\', 'Translation', '\\', 'TranslatableMessage', '(', self::MESSAGE_TOKEN, ], [ 'new', '\Symfony\Component\Translation\TranslatableMessage', '(', self::MESSAGE_TOKEN, ], [ 't', '(', self::MESSAGE_TOKEN, ',', self::METHOD_ARGUMENTS_TOKEN, ',', self::DOMAIN_TOKEN, ], [ 't', '(', self::MESSAGE_TOKEN, ], ]; /** * @return void */ public function extract(string|iterable $resource, MessageCatalogue $catalog) { $files = $this->extractFiles($resource); foreach ($files as $file) { $this->parseTokens(token_get_all(file_get_contents($file)), $catalog, $file); gc_mem_caches(); } } /** * @return void */ public function setPrefix(string $prefix) { $this->prefix = $prefix; } /** * Normalizes a token. */ protected function normalizeToken(mixed $token): ?string { if (isset($token[1]) && 'b"' !== $token) { return $token[1]; } return $token; } /** * Seeks to a non-whitespace token. */ private function seekToNextRelevantToken(\Iterator $tokenIterator): void { for (; $tokenIterator->valid(); $tokenIterator->next()) { $t = $tokenIterator->current(); if (\T_WHITESPACE !== $t[0]) { break; } } } private function skipMethodArgument(\Iterator $tokenIterator): void { $openBraces = 0; for (; $tokenIterator->valid(); $tokenIterator->next()) { $t = $tokenIterator->current(); if ('[' === $t[0] || '(' === $t[0]) { ++$openBraces; } if (']' === $t[0] || ')' === $t[0]) { --$openBraces; } if ((0 === $openBraces && ',' === $t[0]) || (-1 === $openBraces && ')' === $t[0])) { break; } } } /** * Extracts the message from the iterator while the tokens * match allowed message tokens. */ private function getValue(\Iterator $tokenIterator): string { $message = ''; $docToken = ''; $docPart = ''; for (; $tokenIterator->valid(); $tokenIterator->next()) { $t = $tokenIterator->current(); if ('.' === $t) { // Concatenate with next token continue; } if (!isset($t[1])) { break; } switch ($t[0]) { case \T_START_HEREDOC: $docToken = $t[1]; break; case \T_ENCAPSED_AND_WHITESPACE: case \T_CONSTANT_ENCAPSED_STRING: if ('' === $docToken) { $message .= PhpStringTokenParser::parse($t[1]); } else { $docPart = $t[1]; } break; case \T_END_HEREDOC: if ($indentation = strspn($t[1], ' ')) { $docPartWithLineBreaks = $docPart; $docPart = ''; foreach (preg_split('~(\r\n|\n|\r)~', $docPartWithLineBreaks, -1, \PREG_SPLIT_DELIM_CAPTURE) as $str) { if (\in_array($str, ["\r\n", "\n", "\r"], true)) { $docPart .= $str; } else { $docPart .= substr($str, $indentation); } } } $message .= PhpStringTokenParser::parseDocString($docToken, $docPart); $docToken = ''; $docPart = ''; break; case \T_WHITESPACE: break; default: break 2; } } return $message; } /** * Extracts trans message from PHP tokens. * * @return void */ protected function parseTokens(array $tokens, MessageCatalogue $catalog, string $filename) { $tokenIterator = new \ArrayIterator($tokens); for ($key = 0; $key < $tokenIterator->count(); ++$key) { foreach ($this->sequences as $sequence) { $message = ''; $domain = 'messages'; $tokenIterator->seek($key); foreach ($sequence as $sequenceKey => $item) { $this->seekToNextRelevantToken($tokenIterator); if ($this->normalizeToken($tokenIterator->current()) === $item) { $tokenIterator->next(); continue; } elseif (self::MESSAGE_TOKEN === $item) { $message = $this->getValue($tokenIterator); if (\count($sequence) === ($sequenceKey + 1)) { break; } } elseif (self::METHOD_ARGUMENTS_TOKEN === $item) { $this->skipMethodArgument($tokenIterator); } elseif (self::DOMAIN_TOKEN === $item) { $domainToken = $this->getValue($tokenIterator); if ('' !== $domainToken) { $domain = $domainToken; } break; } else { break; } } if ($message) { $catalog->set($message, $this->prefix.$message, $domain); $metadata = $catalog->getMetadata($message, $domain) ?? []; $normalizedFilename = preg_replace('{[\\\\/]+}', '/', $filename); $metadata['sources'][] = $normalizedFilename.':'.$tokens[$key][2]; $catalog->setMetadata($message, $metadata, $domain); break; } } } } /** * @throws \InvalidArgumentException */ protected function canBeExtracted(string $file): bool { return $this->isFile($file) && 'php' === pathinfo($file, \PATHINFO_EXTENSION); } protected function extractFromDirectory(string|array $directory): iterable { if (!class_exists(Finder::class)) { throw new \LogicException(sprintf('You cannot use "%s" as the "symfony/finder" package is not installed. Try running "composer require symfony/finder".', static::class)); } $finder = new Finder(); return $finder->files()->name('*.php')->in($directory); } } PseudoLocalizationTranslator.php 0000644 00000025546 15111175543 0013156 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Contracts\Translation\TranslatorInterface; /** * This translator should only be used in a development environment. */ final class PseudoLocalizationTranslator implements TranslatorInterface { private const EXPANSION_CHARACTER = '~'; private TranslatorInterface $translator; private bool $accents; private float $expansionFactor; private bool $brackets; private bool $parseHTML; /** * @var string[] */ private array $localizableHTMLAttributes; /** * Available options: * * accents: * type: boolean * default: true * description: replace ASCII characters of the translated string with accented versions or similar characters * example: if true, "foo" => "ƒöö". * * * expansion_factor: * type: float * default: 1 * validation: it must be greater than or equal to 1 * description: expand the translated string by the given factor with spaces and tildes * example: if 2, "foo" => "~foo ~" * * * brackets: * type: boolean * default: true * description: wrap the translated string with brackets * example: if true, "foo" => "[foo]" * * * parse_html: * type: boolean * default: false * description: parse the translated string as HTML - looking for HTML tags has a performance impact but allows to preserve them from alterations - it also allows to compute the visible translated string length which is useful to correctly expand ot when it contains HTML * warning: unclosed tags are unsupported, they will be fixed (closed) by the parser - eg, "foo <div>bar" => "foo <div>bar</div>" * * * localizable_html_attributes: * type: string[] * default: [] * description: the list of HTML attributes whose values can be altered - it is only useful when the "parse_html" option is set to true * example: if ["title"], and with the "accents" option set to true, "<a href="#" title="Go to your profile">Profile</a>" => "<a href="#" title="Ĝö ţö ýöûŕ þŕöƒîļé">Þŕöƒîļé</a>" - if "title" was not in the "localizable_html_attributes" list, the title attribute data would be left unchanged. */ public function __construct(TranslatorInterface $translator, array $options = []) { $this->translator = $translator; $this->accents = $options['accents'] ?? true; if (1.0 > ($this->expansionFactor = $options['expansion_factor'] ?? 1.0)) { throw new \InvalidArgumentException('The expansion factor must be greater than or equal to 1.'); } $this->brackets = $options['brackets'] ?? true; $this->parseHTML = $options['parse_html'] ?? false; if ($this->parseHTML && !$this->accents && 1.0 === $this->expansionFactor) { $this->parseHTML = false; } $this->localizableHTMLAttributes = $options['localizable_html_attributes'] ?? []; } public function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string { $trans = ''; $visibleText = ''; foreach ($this->getParts($this->translator->trans($id, $parameters, $domain, $locale)) as [$visible, $localizable, $text]) { if ($visible) { $visibleText .= $text; } if (!$localizable) { $trans .= $text; continue; } $this->addAccents($trans, $text); } $this->expand($trans, $visibleText); $this->addBrackets($trans); return $trans; } public function getLocale(): string { return $this->translator->getLocale(); } private function getParts(string $originalTrans): array { if (!$this->parseHTML) { return [[true, true, $originalTrans]]; } $html = mb_encode_numericentity($originalTrans, [0x80, 0xFFFF, 0, 0xFFFF], mb_detect_encoding($originalTrans, null, true) ?: 'UTF-8'); $useInternalErrors = libxml_use_internal_errors(true); $dom = new \DOMDocument(); $dom->loadHTML('<trans>'.$html.'</trans>'); libxml_clear_errors(); libxml_use_internal_errors($useInternalErrors); return $this->parseNode($dom->childNodes->item(1)->childNodes->item(0)->childNodes->item(0)); } private function parseNode(\DOMNode $node): array { $parts = []; foreach ($node->childNodes as $childNode) { if (!$childNode instanceof \DOMElement) { $parts[] = [true, true, $childNode->nodeValue]; continue; } $parts[] = [false, false, '<'.$childNode->tagName]; /** @var \DOMAttr $attribute */ foreach ($childNode->attributes as $attribute) { $parts[] = [false, false, ' '.$attribute->nodeName.'="']; $localizableAttribute = \in_array($attribute->nodeName, $this->localizableHTMLAttributes, true); foreach (preg_split('/(&(?:amp|quot|#039|lt|gt);+)/', htmlspecialchars($attribute->nodeValue, \ENT_QUOTES, 'UTF-8'), -1, \PREG_SPLIT_DELIM_CAPTURE) as $i => $match) { if ('' === $match) { continue; } $parts[] = [false, $localizableAttribute && 0 === $i % 2, $match]; } $parts[] = [false, false, '"']; } $parts[] = [false, false, '>']; $parts = array_merge($parts, $this->parseNode($childNode, $parts)); $parts[] = [false, false, '</'.$childNode->tagName.'>']; } return $parts; } private function addAccents(string &$trans, string $text): void { $trans .= $this->accents ? strtr($text, [ ' ' => ' ', '!' => '¡', '"' => '″', '#' => '♯', '$' => '€', '%' => '‰', '&' => '⅋', '\'' => '´', '(' => '{', ')' => '}', '*' => '⁎', '+' => '⁺', ',' => '،', '-' => '‐', '.' => '·', '/' => '⁄', '0' => '⓪', '1' => '①', '2' => '②', '3' => '③', '4' => '④', '5' => '⑤', '6' => '⑥', '7' => '⑦', '8' => '⑧', '9' => '⑨', ':' => '∶', ';' => '⁏', '<' => '≤', '=' => '≂', '>' => '≥', '?' => '¿', '@' => '՞', 'A' => 'Å', 'B' => 'Ɓ', 'C' => 'Ç', 'D' => 'Ð', 'E' => 'É', 'F' => 'Ƒ', 'G' => 'Ĝ', 'H' => 'Ĥ', 'I' => 'Î', 'J' => 'Ĵ', 'K' => 'Ķ', 'L' => 'Ļ', 'M' => 'Ṁ', 'N' => 'Ñ', 'O' => 'Ö', 'P' => 'Þ', 'Q' => 'Ǫ', 'R' => 'Ŕ', 'S' => 'Š', 'T' => 'Ţ', 'U' => 'Û', 'V' => 'Ṽ', 'W' => 'Ŵ', 'X' => 'Ẋ', 'Y' => 'Ý', 'Z' => 'Ž', '[' => '⁅', '\\' => '∖', ']' => '⁆', '^' => '˄', '_' => '‿', '`' => '‵', 'a' => 'å', 'b' => 'ƀ', 'c' => 'ç', 'd' => 'ð', 'e' => 'é', 'f' => 'ƒ', 'g' => 'ĝ', 'h' => 'ĥ', 'i' => 'î', 'j' => 'ĵ', 'k' => 'ķ', 'l' => 'ļ', 'm' => 'ɱ', 'n' => 'ñ', 'o' => 'ö', 'p' => 'þ', 'q' => 'ǫ', 'r' => 'ŕ', 's' => 'š', 't' => 'ţ', 'u' => 'û', 'v' => 'ṽ', 'w' => 'ŵ', 'x' => 'ẋ', 'y' => 'ý', 'z' => 'ž', '{' => '(', '|' => '¦', '}' => ')', '~' => '˞', ]) : $text; } private function expand(string &$trans, string $visibleText): void { if (1.0 >= $this->expansionFactor) { return; } $visibleLength = $this->strlen($visibleText); $missingLength = (int) ceil($visibleLength * $this->expansionFactor) - $visibleLength; if ($this->brackets) { $missingLength -= 2; } if (0 >= $missingLength) { return; } $words = []; $wordsCount = 0; foreach (preg_split('/ +/', $visibleText, -1, \PREG_SPLIT_NO_EMPTY) as $word) { $wordLength = $this->strlen($word); if ($wordLength >= $missingLength) { continue; } if (!isset($words[$wordLength])) { $words[$wordLength] = 0; } ++$words[$wordLength]; ++$wordsCount; } if (!$words) { $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); return; } arsort($words, \SORT_NUMERIC); $longestWordLength = max(array_keys($words)); while (true) { $r = mt_rand(1, $wordsCount); foreach ($words as $length => $count) { $r -= $count; if ($r <= 0) { break; } } $trans .= ' '.str_repeat(self::EXPANSION_CHARACTER, $length); $missingLength -= $length + 1; if (0 === $missingLength) { return; } while ($longestWordLength >= $missingLength) { $wordsCount -= $words[$longestWordLength]; unset($words[$longestWordLength]); if (!$words) { $trans .= 1 === $missingLength ? self::EXPANSION_CHARACTER : ' '.str_repeat(self::EXPANSION_CHARACTER, $missingLength - 1); return; } $longestWordLength = max(array_keys($words)); } } } private function addBrackets(string &$trans): void { if (!$this->brackets) { return; } $trans = '['.$trans.']'; } private function strlen(string $s): int { return false === ($encoding = mb_detect_encoding($s, null, true)) ? \strlen($s) : mb_strlen($s, $encoding); } } Catalogue/TargetOperation.php 0000644 00000007233 15111175543 0012300 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Target operation between two catalogues: * intersection = source ∩ target = {x: x ∈ source ∧ x ∈ target} * all = intersection ∪ (target ∖ intersection) = target * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} * obsolete = source ∖ all = source ∖ target = {x: x ∈ source ∧ x ∉ target} * Basically, the result contains messages from the target catalogue. * * @author Michael Lee <michael.lee@zerustech.com> */ class TargetOperation extends AbstractOperation { /** * @return void */ protected function processDomain(string $domain) { $this->messages[$domain] = [ 'all' => [], 'new' => [], 'obsolete' => [], ]; $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $domain)) { $this->result->setCatalogueMetadata($key, $value, $domain); } } foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) { $this->result->setCatalogueMetadata($key, $value, $intlDomain); } } // For 'all' messages, the code can't be simplified as ``$this->messages[$domain]['all'] = $target->all($domain);``, // because doing so will drop messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} // // For 'new' messages, the code can't be simplified as ``array_diff_assoc($this->target->all($domain), $this->source->all($domain));`` // because doing so will not exclude messages like {x: x ∈ target ∧ x ∉ source.all ∧ x ∈ source.fallback} // // For 'obsolete' messages, the code can't be simplified as ``array_diff_assoc($this->source->all($domain), $this->target->all($domain))`` // because doing so will not exclude messages like {x: x ∈ source ∧ x ∉ target.all ∧ x ∈ target.fallback} foreach ($this->source->all($domain) as $id => $message) { if ($this->target->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } else { $this->messages[$domain]['obsolete'][$id] = $message; } } foreach ($this->target->all($domain) as $id => $message) { if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } } } } Catalogue/MergeOperation.php 0000644 00000005206 15111175543 0012107 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Merge operation between two catalogues as follows: * all = source ∪ target = {x: x ∈ source ∨ x ∈ target} * new = all ∖ source = {x: x ∈ target ∧ x ∉ source} * obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ source ∧ x ∉ target} = ∅ * Basically, the result contains messages from both catalogues. * * @author Jean-François Simon <contact@jfsimon.fr> */ class MergeOperation extends AbstractOperation { /** * @return void */ protected function processDomain(string $domain) { $this->messages[$domain] = [ 'all' => [], 'new' => [], 'obsolete' => [], ]; $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; foreach ($this->target->getCatalogueMetadata('', $domain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $domain)) { $this->result->setCatalogueMetadata($key, $value, $domain); } } foreach ($this->target->getCatalogueMetadata('', $intlDomain) ?? [] as $key => $value) { if (null === $this->result->getCatalogueMetadata($key, $intlDomain)) { $this->result->setCatalogueMetadata($key, $value, $intlDomain); } } foreach ($this->source->all($domain) as $id => $message) { $this->messages[$domain]['all'][$id] = $message; $d = $this->source->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->source->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } foreach ($this->target->all($domain) as $id => $message) { if (!$this->source->has($id, $domain)) { $this->messages[$domain]['all'][$id] = $message; $this->messages[$domain]['new'][$id] = $message; $d = $this->target->defines($id, $intlDomain) ? $intlDomain : $domain; $this->result->add([$id => $message], $d); if (null !== $keyMetadata = $this->target->getMetadata($id, $d)) { $this->result->setMetadata($id, $keyMetadata, $d); } } } } } Catalogue/OperationInterface.php 0000644 00000003501 15111175543 0012744 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Represents an operation on catalogue(s). * * An instance of this interface performs an operation on one or more catalogues and * stores intermediate and final results of the operation. * * The first catalogue in its argument(s) is called the 'source catalogue' or 'source' and * the following results are stored: * * Messages: also called 'all', are valid messages for the given domain after the operation is performed. * * New Messages: also called 'new' (new = all ∖ source = {x: x ∈ all ∧ x ∉ source}). * * Obsolete Messages: also called 'obsolete' (obsolete = source ∖ all = {x: x ∈ source ∧ x ∉ all}). * * Result: also called 'result', is the resulting catalogue for the given domain that holds the same messages as 'all'. * * @author Jean-François Simon <jeanfrancois.simon@sensiolabs.com> */ interface OperationInterface { /** * Returns domains affected by operation. */ public function getDomains(): array; /** * Returns all valid messages ('all') after operation. */ public function getMessages(string $domain): array; /** * Returns new messages ('new') after operation. */ public function getNewMessages(string $domain): array; /** * Returns obsolete messages ('obsolete') after operation. */ public function getObsoleteMessages(string $domain): array; /** * Returns resulting catalogue ('result'). */ public function getResult(): MessageCatalogueInterface; } Catalogue/error_log 0000644 00000006462 15111175543 0010400 0 ustar 00 [19-Nov-2025 04:19:43 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php:25 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php on line 25 [19-Nov-2025 04:20:19 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php on line 26 [19-Nov-2025 04:27:29 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Catalogue\OperationInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php:27 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php on line 27 [19-Nov-2025 11:10:55 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php:25 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php on line 25 [19-Nov-2025 11:21:34 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Catalogue\OperationInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php:27 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php on line 27 [19-Nov-2025 11:22:16 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php on line 26 [19-Nov-2025 17:29:41 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php:25 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/MergeOperation.php on line 25 [19-Nov-2025 17:29:44 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Catalogue\AbstractOperation" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/TargetOperation.php on line 26 [19-Nov-2025 17:30:22 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Catalogue\OperationInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php:27 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Catalogue/AbstractOperation.php on line 27 Catalogue/AbstractOperation.php 0000644 00000013662 15111175543 0012620 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Catalogue; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\MessageCatalogueInterface; /** * Base catalogues binary operation class. * * A catalogue binary operation performs operation on * source (the left argument) and target (the right argument) catalogues. * * @author Jean-François Simon <contact@jfsimon.fr> */ abstract class AbstractOperation implements OperationInterface { public const OBSOLETE_BATCH = 'obsolete'; public const NEW_BATCH = 'new'; public const ALL_BATCH = 'all'; protected $source; protected $target; protected $result; /** * @var array|null The domains affected by this operation */ private $domains; /** * This array stores 'all', 'new' and 'obsolete' messages for all valid domains. * * The data structure of this array is as follows: * * [ * 'domain 1' => [ * 'all' => [...], * 'new' => [...], * 'obsolete' => [...] * ], * 'domain 2' => [ * 'all' => [...], * 'new' => [...], * 'obsolete' => [...] * ], * ... * ] * * @var array The array that stores 'all', 'new' and 'obsolete' messages */ protected $messages; /** * @throws LogicException */ public function __construct(MessageCatalogueInterface $source, MessageCatalogueInterface $target) { if ($source->getLocale() !== $target->getLocale()) { throw new LogicException('Operated catalogues must belong to the same locale.'); } $this->source = $source; $this->target = $target; $this->result = new MessageCatalogue($source->getLocale()); $this->messages = []; } public function getDomains(): array { if (null === $this->domains) { $domains = []; foreach ([$this->source, $this->target] as $catalogue) { foreach ($catalogue->getDomains() as $domain) { $domains[$domain] = $domain; if ($catalogue->all($domainIcu = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX)) { $domains[$domainIcu] = $domainIcu; } } } $this->domains = array_values($domains); } return $this->domains; } public function getMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::ALL_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::ALL_BATCH]; } public function getNewMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::NEW_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::NEW_BATCH]; } public function getObsoleteMessages(string $domain): array { if (!\in_array($domain, $this->getDomains())) { throw new InvalidArgumentException(sprintf('Invalid domain: "%s".', $domain)); } if (!isset($this->messages[$domain][self::OBSOLETE_BATCH])) { $this->processDomain($domain); } return $this->messages[$domain][self::OBSOLETE_BATCH]; } public function getResult(): MessageCatalogueInterface { foreach ($this->getDomains() as $domain) { if (!isset($this->messages[$domain])) { $this->processDomain($domain); } } return $this->result; } /** * @param self::*_BATCH $batch */ public function moveMessagesToIntlDomainsIfPossible(string $batch = self::ALL_BATCH): void { // If MessageFormatter class does not exists, intl domains are not supported. if (!class_exists(\MessageFormatter::class)) { return; } foreach ($this->getDomains() as $domain) { $intlDomain = $domain.MessageCatalogueInterface::INTL_DOMAIN_SUFFIX; $messages = match ($batch) { self::OBSOLETE_BATCH => $this->getObsoleteMessages($domain), self::NEW_BATCH => $this->getNewMessages($domain), self::ALL_BATCH => $this->getMessages($domain), default => throw new \InvalidArgumentException(sprintf('$batch argument must be one of ["%s", "%s", "%s"].', self::ALL_BATCH, self::NEW_BATCH, self::OBSOLETE_BATCH)), }; if (!$messages || (!$this->source->all($intlDomain) && $this->source->all($domain))) { continue; } $result = $this->getResult(); $allIntlMessages = $result->all($intlDomain); $currentMessages = array_diff_key($messages, $result->all($domain)); $result->replace($currentMessages, $domain); $result->replace($allIntlMessages + $messages, $intlDomain); } } /** * Performs operation on source and target catalogues for the given domain and * stores the results. * * @param string $domain The domain which the operation will be performed for * * @return void */ abstract protected function processDomain(string $domain); } Provider/TranslationProviderCollectionFactory.php 0000644 00000003261 15111175543 0016431 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\UnsupportedSchemeException; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ class TranslationProviderCollectionFactory { private iterable $factories; private array $enabledLocales; /** * @param iterable<mixed, ProviderFactoryInterface> $factories */ public function __construct(iterable $factories, array $enabledLocales) { $this->factories = $factories; $this->enabledLocales = $enabledLocales; } public function fromConfig(array $config): TranslationProviderCollection { $providers = []; foreach ($config as $name => $currentConfig) { $providers[$name] = $this->fromDsnObject( new Dsn($currentConfig['dsn']), !$currentConfig['locales'] ? $this->enabledLocales : $currentConfig['locales'], !$currentConfig['domains'] ? [] : $currentConfig['domains'] ); } return new TranslationProviderCollection($providers); } public function fromDsnObject(Dsn $dsn, array $locales, array $domains = []): ProviderInterface { foreach ($this->factories as $factory) { if ($factory->supports($dsn)) { return new FilteringProvider($factory->create($dsn), $locales, $domains); } } throw new UnsupportedSchemeException($dsn); } } Provider/ProviderFactoryInterface.php 0000644 00000001235 15111175544 0014017 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\IncompleteDsnException; use Symfony\Component\Translation\Exception\UnsupportedSchemeException; interface ProviderFactoryInterface { /** * @throws UnsupportedSchemeException * @throws IncompleteDsnException */ public function create(Dsn $dsn): ProviderInterface; public function supports(Dsn $dsn): bool; } Provider/NullProviderFactory.php 0000644 00000001513 15111175544 0013030 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\UnsupportedSchemeException; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ final class NullProviderFactory extends AbstractProviderFactory { public function create(Dsn $dsn): ProviderInterface { if ('null' === $dsn->getScheme()) { return new NullProvider(); } throw new UnsupportedSchemeException($dsn, 'null', $this->getSupportedSchemes()); } protected function getSupportedSchemes(): array { return ['null']; } } Provider/NullProvider.php 0000644 00000001616 15111175544 0011504 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\TranslatorBag; use Symfony\Component\Translation\TranslatorBagInterface; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ class NullProvider implements ProviderInterface { public function __toString(): string { return 'null'; } public function write(TranslatorBagInterface $translatorBag, bool $override = false): void { } public function read(array $domains, array $locales): TranslatorBag { return new TranslatorBag(); } public function delete(TranslatorBagInterface $translatorBag): void { } } Provider/Dsn.php 0000644 00000005614 15111175544 0007605 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\MissingRequiredOptionException; /** * @author Fabien Potencier <fabien@symfony.com> * @author Oskar Stark <oskarstark@googlemail.com> */ final class Dsn { private ?string $scheme; private ?string $host; private ?string $user; private ?string $password; private ?int $port; private ?string $path; private array $options = []; private string $originalDsn; public function __construct(#[\SensitiveParameter] string $dsn) { $this->originalDsn = $dsn; if (false === $parsedDsn = parse_url($dsn)) { throw new InvalidArgumentException('The translation provider DSN is invalid.'); } if (!isset($parsedDsn['scheme'])) { throw new InvalidArgumentException('The translation provider DSN must contain a scheme.'); } $this->scheme = $parsedDsn['scheme']; if (!isset($parsedDsn['host'])) { throw new InvalidArgumentException('The translation provider DSN must contain a host (use "default" by default).'); } $this->host = $parsedDsn['host']; $this->user = '' !== ($parsedDsn['user'] ?? '') ? urldecode($parsedDsn['user']) : null; $this->password = '' !== ($parsedDsn['pass'] ?? '') ? urldecode($parsedDsn['pass']) : null; $this->port = $parsedDsn['port'] ?? null; $this->path = $parsedDsn['path'] ?? null; parse_str($parsedDsn['query'] ?? '', $this->options); } public function getScheme(): string { return $this->scheme; } public function getHost(): string { return $this->host; } public function getUser(): ?string { return $this->user; } public function getPassword(): ?string { return $this->password; } public function getPort(int $default = null): ?int { return $this->port ?? $default; } public function getOption(string $key, mixed $default = null): mixed { return $this->options[$key] ?? $default; } public function getRequiredOption(string $key): mixed { if (!\array_key_exists($key, $this->options) || '' === trim($this->options[$key])) { throw new MissingRequiredOptionException($key); } return $this->options[$key]; } public function getOptions(): array { return $this->options; } public function getPath(): ?string { return $this->path; } public function getOriginalDsn(): string { return $this->originalDsn; } } Provider/error_log 0000644 00000005756 15111175544 0010274 0 ustar 00 [20-Nov-2025 05:31:55 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Provider\AbstractProviderFactory" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProviderFactory.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProviderFactory.php on line 19 [20-Nov-2025 05:33:50 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProvider.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProvider.php on line 20 [20-Nov-2025 05:34:32 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderFactoryInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/AbstractProviderFactory.php:16 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/AbstractProviderFactory.php on line 16 [20-Nov-2025 05:36:39 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/FilteringProvider.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/FilteringProvider.php on line 22 [20-Nov-2025 10:06:25 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProvider.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProvider.php on line 20 [20-Nov-2025 10:10:09 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/FilteringProvider.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/FilteringProvider.php on line 22 [20-Nov-2025 10:12:56 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Provider\ProviderFactoryInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/AbstractProviderFactory.php:16 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/AbstractProviderFactory.php on line 16 [20-Nov-2025 10:19:48 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Provider\AbstractProviderFactory" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProviderFactory.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Provider/NullProviderFactory.php on line 19 Provider/ProviderInterface.php 0000644 00000001666 15111175544 0012477 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\TranslatorBag; use Symfony\Component\Translation\TranslatorBagInterface; interface ProviderInterface { public function __toString(): string; /** * Translations available in the TranslatorBag only must be created. * Translations available in both the TranslatorBag and on the provider * must be overwritten. * Translations available on the provider only must be kept. */ public function write(TranslatorBagInterface $translatorBag): void; public function read(array $domains, array $locales): TranslatorBag; public function delete(TranslatorBagInterface $translatorBag): void; } Provider/AbstractProviderFactory.php 0000644 00000002037 15111175544 0013663 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\IncompleteDsnException; abstract class AbstractProviderFactory implements ProviderFactoryInterface { public function supports(Dsn $dsn): bool { return \in_array($dsn->getScheme(), $this->getSupportedSchemes(), true); } /** * @return string[] */ abstract protected function getSupportedSchemes(): array; protected function getUser(Dsn $dsn): string { return $dsn->getUser() ?? throw new IncompleteDsnException('User is not set.', $dsn->getScheme().'://'.$dsn->getHost()); } protected function getPassword(Dsn $dsn): string { return $dsn->getPassword() ?? throw new IncompleteDsnException('Password is not set.', $dsn->getOriginalDsn()); } } Provider/FilteringProvider.php 0000644 00000003232 15111175544 0012511 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\TranslatorBag; use Symfony\Component\Translation\TranslatorBagInterface; /** * Filters domains and locales between the Translator config values and those specific to each provider. * * @author Mathieu Santostefano <msantostefano@protonmail.com> */ class FilteringProvider implements ProviderInterface { private ProviderInterface $provider; private array $locales; private array $domains; public function __construct(ProviderInterface $provider, array $locales, array $domains = []) { $this->provider = $provider; $this->locales = $locales; $this->domains = $domains; } public function __toString(): string { return (string) $this->provider; } public function write(TranslatorBagInterface $translatorBag): void { $this->provider->write($translatorBag); } public function read(array $domains, array $locales): TranslatorBag { $domains = !$this->domains ? $domains : array_intersect($this->domains, $domains); $locales = array_intersect($this->locales, $locales); return $this->provider->read($domains, $locales); } public function delete(TranslatorBagInterface $translatorBag): void { $this->provider->delete($translatorBag); } public function getDomains(): array { return $this->domains; } } Provider/TranslationProviderCollection.php 0000644 00000002562 15111175544 0015105 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Provider; use Symfony\Component\Translation\Exception\InvalidArgumentException; /** * @author Mathieu Santostefano <msantostefano@protonmail.com> */ final class TranslationProviderCollection { /** * @var array<string, ProviderInterface> */ private $providers; /** * @param array<string, ProviderInterface> $providers */ public function __construct(iterable $providers) { $this->providers = \is_array($providers) ? $providers : iterator_to_array($providers); } public function __toString(): string { return '['.implode(',', array_keys($this->providers)).']'; } public function has(string $name): bool { return isset($this->providers[$name]); } public function get(string $name): ProviderInterface { if (!$this->has($name)) { throw new InvalidArgumentException(sprintf('Provider "%s" not found. Available: "%s".', $name, (string) $this)); } return $this->providers[$name]; } public function keys(): array { return array_keys($this->providers); } } LoggingTranslator.php 0000644 00000007034 15111175544 0010725 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Psr\Log\LoggerInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class LoggingTranslator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface { private TranslatorInterface $translator; private LoggerInterface $logger; /** * @param TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator The translator must implement TranslatorBagInterface */ public function __construct(TranslatorInterface $translator, LoggerInterface $logger) { if (!$translator instanceof TranslatorBagInterface || !$translator instanceof LocaleAwareInterface) { throw new InvalidArgumentException(sprintf('The Translator "%s" must implement TranslatorInterface, TranslatorBagInterface and LocaleAwareInterface.', get_debug_type($translator))); } $this->translator = $translator; $this->logger = $logger; } public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string { $trans = $this->translator->trans($id = (string) $id, $parameters, $domain, $locale); $this->log($id, $domain, $locale); return $trans; } /** * @return void */ public function setLocale(string $locale) { $prev = $this->translator->getLocale(); $this->translator->setLocale($locale); if ($prev === $locale) { return; } $this->logger->debug(sprintf('The locale of the translator has changed from "%s" to "%s".', $prev, $locale)); } public function getLocale(): string { return $this->translator->getLocale(); } public function getCatalogue(string $locale = null): MessageCatalogueInterface { return $this->translator->getCatalogue($locale); } public function getCatalogues(): array { return $this->translator->getCatalogues(); } /** * Gets the fallback locales. */ public function getFallbackLocales(): array { if ($this->translator instanceof Translator || method_exists($this->translator, 'getFallbackLocales')) { return $this->translator->getFallbackLocales(); } return []; } /** * Passes through all unknown calls onto the translator object. */ public function __call(string $method, array $args) { return $this->translator->{$method}(...$args); } /** * Logs for missing translations. */ private function log(string $id, ?string $domain, ?string $locale): void { $domain ??= 'messages'; $catalogue = $this->translator->getCatalogue($locale); if ($catalogue->defines($id, $domain)) { return; } if ($catalogue->has($id, $domain)) { $this->logger->debug('Translation use fallback catalogue.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]); } else { $this->logger->warning('Translation not found.', ['id' => $id, 'domain' => $domain, 'locale' => $catalogue->getLocale()]); } } } Exception/InvalidResourceException.php 0000644 00000000755 15111175544 0014203 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Thrown when a resource cannot be loaded. * * @author Fabien Potencier <fabien@symfony.com> */ class InvalidResourceException extends \InvalidArgumentException implements ExceptionInterface { } Exception/MissingRequiredOptionException.php 0000644 00000001234 15111175545 0015402 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * @author Oskar Stark <oskarstark@googlemail.com> */ class MissingRequiredOptionException extends IncompleteDsnException { public function __construct(string $option, string $dsn = null, \Throwable $previous = null) { $message = sprintf('The option "%s" is required but missing.', $option); parent::__construct($message, $dsn, $previous); } } Exception/ExceptionInterface.php 0000644 00000000725 15111175545 0013003 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Exception interface for all exceptions thrown by the component. * * @author Fabien Potencier <fabien@symfony.com> */ interface ExceptionInterface extends \Throwable { } Exception/ProviderException.php 0000644 00000002002 15111175545 0012663 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Fabien Potencier <fabien@symfony.com> */ class ProviderException extends RuntimeException implements ProviderExceptionInterface { private ResponseInterface $response; private string $debug; public function __construct(string $message, ResponseInterface $response, int $code = 0, \Exception $previous = null) { $this->response = $response; $this->debug = $response->getInfo('debug') ?? ''; parent::__construct($message, $code, $previous); } public function getResponse(): ResponseInterface { return $this->response; } public function getDebug(): string { return $this->debug; } } Exception/LogicException.php 0000644 00000000747 15111175545 0012144 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Base LogicException for Translation component. * * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class LogicException extends \LogicException implements ExceptionInterface { } Exception/ProviderExceptionInterface.php 0000644 00000001057 15111175545 0014515 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * @author Fabien Potencier <fabien@symfony.com> */ interface ProviderExceptionInterface extends ExceptionInterface { /* * Returns debug info coming from the Symfony\Contracts\HttpClient\ResponseInterface */ public function getDebug(): string; } Exception/RuntimeException.php 0000644 00000000761 15111175545 0012526 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Base RuntimeException for the Translation component. * * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class RuntimeException extends \RuntimeException implements ExceptionInterface { } Exception/NotFoundResourceException.php 0000644 00000000754 15111175545 0014351 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Thrown when a resource does not exist. * * @author Fabien Potencier <fabien@symfony.com> */ class NotFoundResourceException extends \InvalidArgumentException implements ExceptionInterface { } Exception/error_log 0000644 00000017230 15111175545 0010427 0 ustar 00 [19-Nov-2025 06:40:31 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/LogicException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/LogicException.php on line 19 [19-Nov-2025 06:40:46 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\IncompleteDsnException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/MissingRequiredOptionException.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/MissingRequiredOptionException.php on line 17 [19-Nov-2025 06:44:19 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/RuntimeException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/RuntimeException.php on line 19 [19-Nov-2025 06:54:40 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/NotFoundResourceException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/NotFoundResourceException.php on line 19 [19-Nov-2025 06:56:41 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\LogicException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/UnsupportedSchemeException.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/UnsupportedSchemeException.php on line 17 [19-Nov-2025 06:57:12 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidResourceException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidResourceException.php on line 19 [19-Nov-2025 06:57:19 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderExceptionInterface.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderExceptionInterface.php on line 17 [19-Nov-2025 06:59:43 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\InvalidArgumentException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/IncompleteDsnException.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/IncompleteDsnException.php on line 14 [19-Nov-2025 07:01:40 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidArgumentException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidArgumentException.php on line 19 [19-Nov-2025 07:11:25 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\RuntimeException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderException.php on line 19 [19-Nov-2025 13:26:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\IncompleteDsnException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/MissingRequiredOptionException.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/MissingRequiredOptionException.php on line 17 [19-Nov-2025 13:32:00 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/LogicException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/LogicException.php on line 19 [19-Nov-2025 13:32:56 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/RuntimeException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/RuntimeException.php on line 19 [19-Nov-2025 13:39:16 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/NotFoundResourceException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/NotFoundResourceException.php on line 19 [19-Nov-2025 13:39:45 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidResourceException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidResourceException.php on line 19 [19-Nov-2025 13:40:47 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\LogicException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/UnsupportedSchemeException.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/UnsupportedSchemeException.php on line 17 [19-Nov-2025 13:47:18 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidArgumentException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/InvalidArgumentException.php on line 19 [19-Nov-2025 13:47:50 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Exception\ExceptionInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderExceptionInterface.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderExceptionInterface.php on line 17 [19-Nov-2025 13:52:25 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\InvalidArgumentException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/IncompleteDsnException.php:14 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/IncompleteDsnException.php on line 14 [19-Nov-2025 13:59:07 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Exception\RuntimeException" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderException.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Exception/ProviderException.php on line 19 Exception/IncompleteDsnException.php 0000644 00000001164 15111175545 0013645 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; class IncompleteDsnException extends InvalidArgumentException { public function __construct(string $message, string $dsn = null, \Throwable $previous = null) { if ($dsn) { $message = sprintf('Invalid "%s" provider DSN: ', $dsn).$message; } parent::__construct($message, 0, $previous); } } Exception/UnsupportedSchemeException.php 0000644 00000003550 15111175545 0014557 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; use Symfony\Component\Translation\Bridge; use Symfony\Component\Translation\Provider\Dsn; class UnsupportedSchemeException extends LogicException { private const SCHEME_TO_PACKAGE_MAP = [ 'crowdin' => [ 'class' => Bridge\Crowdin\CrowdinProviderFactory::class, 'package' => 'symfony/crowdin-translation-provider', ], 'loco' => [ 'class' => Bridge\Loco\LocoProviderFactory::class, 'package' => 'symfony/loco-translation-provider', ], 'lokalise' => [ 'class' => Bridge\Lokalise\LokaliseProviderFactory::class, 'package' => 'symfony/lokalise-translation-provider', ], ]; public function __construct(Dsn $dsn, string $name = null, array $supported = []) { $provider = $dsn->getScheme(); if (false !== $pos = strpos($provider, '+')) { $provider = substr($provider, 0, $pos); } $package = self::SCHEME_TO_PACKAGE_MAP[$provider] ?? null; if ($package && !class_exists($package['class'])) { parent::__construct(sprintf('Unable to synchronize translations via "%s" as the provider is not installed. Try running "composer require %s".', $provider, $package['package'])); return; } $message = sprintf('The "%s" scheme is not supported', $dsn->getScheme()); if ($name && $supported) { $message .= sprintf('; supported schemes for translation provider "%s" are: "%s"', $name, implode('", "', $supported)); } parent::__construct($message.'.'); } } Exception/InvalidArgumentException.php 0000644 00000001011 15111175545 0014161 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Exception; /** * Base InvalidArgumentException for the Translation component. * * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } LocaleSwitcher.php 0000644 00000003474 15111175545 0010202 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Routing\RequestContext; use Symfony\Contracts\Translation\LocaleAwareInterface; /** * @author Kevin Bond <kevinbond@gmail.com> */ class LocaleSwitcher implements LocaleAwareInterface { private string $defaultLocale; /** * @param LocaleAwareInterface[] $localeAwareServices */ public function __construct( private string $locale, private iterable $localeAwareServices, private ?RequestContext $requestContext = null, ) { $this->defaultLocale = $locale; } public function setLocale(string $locale): void { if (class_exists(\Locale::class)) { \Locale::setDefault($locale); } $this->locale = $locale; $this->requestContext?->setParameter('_locale', $locale); foreach ($this->localeAwareServices as $service) { $service->setLocale($locale); } } public function getLocale(): string { return $this->locale; } /** * Switch to a new locale, execute a callback, then switch back to the original. * * @template T * * @param callable():T $callback * * @return T */ public function runWithLocale(string $locale, callable $callback): mixed { $original = $this->getLocale(); $this->setLocale($locale); try { return $callback(); } finally { $this->setLocale($original); } } public function reset(): void { $this->setLocale($this->defaultLocale); } } CatalogueMetadataAwareInterface.php 0000644 00000002730 15111175546 0013433 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; /** * This interface is used to get, set, and delete metadata about the Catalogue. * * @author Hugo Alliaume <hugo@alliau.me> */ interface CatalogueMetadataAwareInterface { /** * Gets catalogue metadata for the given domain and key. * * Passing an empty domain will return an array with all catalogue metadata indexed by * domain and then by key. Passing an empty key will return an array with all * catalogue metadata for the given domain. * * @return mixed The value that was set or an array with the domains/keys or null */ public function getCatalogueMetadata(string $key = '', string $domain = 'messages'): mixed; /** * Adds catalogue metadata to a message domain. * * @return void */ public function setCatalogueMetadata(string $key, mixed $value, string $domain = 'messages'); /** * Deletes catalogue metadata for the given key and domain. * * Passing an empty domain will delete all catalogue metadata. Passing an empty key will * delete all metadata for the given domain. * * @return void */ public function deleteCatalogueMetadata(string $key = '', string $domain = 'messages'); } composer.json 0000644 00000003544 15111175546 0007302 0 ustar 00 { "name": "symfony/translation", "type": "library", "description": "Provides tools to internationalize your application", "keywords": [], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/translation-contracts": "^2.5|^3.0" }, "require-dev": { "nikic/php-parser": "^4.13", "symfony/config": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", "symfony/dependency-injection": "^5.4|^6.0", "symfony/http-client-contracts": "^2.5|^3.0", "symfony/http-kernel": "^5.4|^6.0", "symfony/intl": "^5.4|^6.0", "symfony/polyfill-intl-icu": "^1.21", "symfony/routing": "^5.4|^6.0", "symfony/service-contracts": "^2.5|^3", "symfony/yaml": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", "psr/log": "^1|^2|^3" }, "conflict": { "symfony/config": "<5.4", "symfony/dependency-injection": "<5.4", "symfony/http-client-contracts": "<2.5", "symfony/http-kernel": "<5.4", "symfony/service-contracts": "<2.5", "symfony/twig-bundle": "<5.4", "symfony/yaml": "<5.4", "symfony/console": "<5.4" }, "provide": { "symfony/translation-implementation": "2.3|3.0" }, "autoload": { "files": [ "Resources/functions.php" ], "psr-4": { "Symfony\\Component\\Translation\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" } DataCollector/TranslationDataCollector.php 0000644 00000010411 15111175546 0014737 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\DataCollector; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface; use Symfony\Component\Translation\DataCollectorTranslator; use Symfony\Component\VarDumper\Cloner\Data; /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> * * @final */ class TranslationDataCollector extends DataCollector implements LateDataCollectorInterface { private DataCollectorTranslator $translator; public function __construct(DataCollectorTranslator $translator) { $this->translator = $translator; } public function lateCollect(): void { $messages = $this->sanitizeCollectedMessages($this->translator->getCollectedMessages()); $this->data += $this->computeCount($messages); $this->data['messages'] = $messages; $this->data = $this->cloneVar($this->data); } public function collect(Request $request, Response $response, \Throwable $exception = null): void { $this->data['locale'] = $this->translator->getLocale(); $this->data['fallback_locales'] = $this->translator->getFallbackLocales(); } public function reset(): void { $this->data = []; } public function getMessages(): array|Data { return $this->data['messages'] ?? []; } public function getCountMissings(): int { return $this->data[DataCollectorTranslator::MESSAGE_MISSING] ?? 0; } public function getCountFallbacks(): int { return $this->data[DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK] ?? 0; } public function getCountDefines(): int { return $this->data[DataCollectorTranslator::MESSAGE_DEFINED] ?? 0; } public function getLocale() { return !empty($this->data['locale']) ? $this->data['locale'] : null; } /** * @internal */ public function getFallbackLocales() { return (isset($this->data['fallback_locales']) && \count($this->data['fallback_locales']) > 0) ? $this->data['fallback_locales'] : []; } public function getName(): string { return 'translation'; } private function sanitizeCollectedMessages(array $messages): array { $result = []; foreach ($messages as $key => $message) { $messageId = $message['locale'].$message['domain'].$message['id']; if (!isset($result[$messageId])) { $message['count'] = 1; $message['parameters'] = !empty($message['parameters']) ? [$message['parameters']] : []; $messages[$key]['translation'] = $this->sanitizeString($message['translation']); $result[$messageId] = $message; } else { if (!empty($message['parameters'])) { $result[$messageId]['parameters'][] = $message['parameters']; } ++$result[$messageId]['count']; } unset($messages[$key]); } return $result; } private function computeCount(array $messages): array { $count = [ DataCollectorTranslator::MESSAGE_DEFINED => 0, DataCollectorTranslator::MESSAGE_MISSING => 0, DataCollectorTranslator::MESSAGE_EQUALS_FALLBACK => 0, ]; foreach ($messages as $message) { ++$count[$message['state']]; } return $count; } private function sanitizeString(string $string, int $length = 80): string { $string = trim(preg_replace('/\s+/', ' ', $string)); if (false !== $encoding = mb_detect_encoding($string, null, true)) { if (mb_strlen($string, $encoding) > $length) { return mb_substr($string, 0, $length - 3, $encoding).'...'; } } elseif (\strlen($string) > $length) { return substr($string, 0, $length - 3).'...'; } return $string; } } DataCollector/error_log 0000644 00000001434 15111175546 0011211 0 ustar 00 [19-Nov-2025 03:04:06 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\HttpKernel\DataCollector\DataCollector" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollector/TranslationDataCollector.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollector/TranslationDataCollector.php on line 26 [19-Nov-2025 09:48:03 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\HttpKernel\DataCollector\DataCollector" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollector/TranslationDataCollector.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollector/TranslationDataCollector.php on line 26 Formatter/MessageFormatter.php 0000644 00000003144 15111175546 0012500 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; use Symfony\Component\Translation\IdentityTranslator; use Symfony\Contracts\Translation\TranslatorInterface; // Help opcache.preload discover always-needed symbols class_exists(IntlFormatter::class); /** * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class MessageFormatter implements MessageFormatterInterface, IntlFormatterInterface { private TranslatorInterface $translator; private IntlFormatterInterface $intlFormatter; /** * @param TranslatorInterface|null $translator An identity translator to use as selector for pluralization */ public function __construct(TranslatorInterface $translator = null, IntlFormatterInterface $intlFormatter = null) { $this->translator = $translator ?? new IdentityTranslator(); $this->intlFormatter = $intlFormatter ?? new IntlFormatter(); } public function format(string $message, string $locale, array $parameters = []): string { if ($this->translator instanceof TranslatorInterface) { return $this->translator->trans($message, $parameters, null, $locale); } return strtr($message, $parameters); } public function formatIntl(string $message, string $locale, array $parameters = []): string { return $this->intlFormatter->formatIntl($message, $locale, $parameters); } } Formatter/IntlFormatter.php 0000644 00000004102 15111175546 0012015 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\LogicException; /** * @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ class IntlFormatter implements IntlFormatterInterface { private $hasMessageFormatter; private $cache = []; public function formatIntl(string $message, string $locale, array $parameters = []): string { // MessageFormatter constructor throws an exception if the message is empty if ('' === $message) { return ''; } if (!$formatter = $this->cache[$locale][$message] ?? null) { if (!$this->hasMessageFormatter ??= class_exists(\MessageFormatter::class)) { throw new LogicException('Cannot parse message translation: please install the "intl" PHP extension or the "symfony/polyfill-intl-messageformatter" package.'); } try { $this->cache[$locale][$message] = $formatter = new \MessageFormatter($locale, $message); } catch (\IntlException $e) { throw new InvalidArgumentException(sprintf('Invalid message format (error #%d): ', intl_get_error_code()).intl_get_error_message(), 0, $e); } } foreach ($parameters as $key => $value) { if (\in_array($key[0] ?? null, ['%', '{'], true)) { unset($parameters[$key]); $parameters[trim($key, '%{ }')] = $value; } } if (false === $message = $formatter->format($parameters)) { throw new InvalidArgumentException(sprintf('Unable to format message (error #%s): ', $formatter->getErrorCode()).$formatter->getErrorMessage()); } return $message; } } Formatter/error_log 0000644 00000002772 15111175546 0010442 0 ustar 00 [19-Nov-2025 04:16:08 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Formatter\MessageFormatterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/MessageFormatter.php:23 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/MessageFormatter.php on line 23 [19-Nov-2025 04:16:10 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Formatter\IntlFormatterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/IntlFormatter.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/IntlFormatter.php on line 21 [19-Nov-2025 11:05:13 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Formatter\MessageFormatterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/MessageFormatter.php:23 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/MessageFormatter.php on line 23 [19-Nov-2025 11:05:17 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Formatter\IntlFormatterInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/IntlFormatter.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Formatter/IntlFormatter.php on line 21 Formatter/IntlFormatterInterface.php 0000644 00000001262 15111175546 0013642 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; /** * Formats ICU message patterns. * * @author Nicolas Grekas <p@tchwork.com> */ interface IntlFormatterInterface { /** * Formats a localized message using rules defined by ICU MessageFormat. * * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html#details */ public function formatIntl(string $message, string $locale, array $parameters = []): string; } Formatter/MessageFormatterInterface.php 0000644 00000001526 15111175546 0014323 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Formatter; /** * @author Guilherme Blanco <guilhermeblanco@hotmail.com> * @author Abdellatif Ait boudad <a.aitboudad@gmail.com> */ interface MessageFormatterInterface { /** * Formats a localized message pattern with given arguments. * * @param string $message The message (may also be an object that can be cast to string) * @param string $locale The message locale * @param array $parameters An array of parameters for the message */ public function format(string $message, string $locale, array $parameters = []): string; } error_log 0000644 00000020607 15111175546 0006474 0 ustar 00 [18-Nov-2025 05:15:11 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php on line 19 [18-Nov-2025 10:33:07 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\MessageCatalogueInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php on line 20 [18-Nov-2025 11:54:38 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollectorTranslator.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollectorTranslator.php on line 22 [18-Nov-2025 14:46:11 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\LocaleAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php on line 20 [18-Nov-2025 15:40:01 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php on line 19 [18-Nov-2025 19:59:18 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\MessageCatalogueInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php on line 20 [18-Nov-2025 23:18:27 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatableInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php on line 20 [18-Nov-2025 23:21:12 UTC] PHP Fatal error: Trait "Symfony\Contracts\Translation\TranslatorTrait" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/IdentityTranslator.php on line 23 [18-Nov-2025 23:40:48 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\LocaleAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php on line 20 [19-Nov-2025 00:09:38 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php:34 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php on line 34 [19-Nov-2025 05:31:55 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\TranslatorBagInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php on line 17 [19-Nov-2025 06:07:45 UTC] PHP Fatal error: Trait "Symfony\Contracts\Translation\TranslatorTrait" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/IdentityTranslator.php on line 23 [19-Nov-2025 06:11:27 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatableInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php on line 20 [19-Nov-2025 08:19:35 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php:34 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php on line 34 [19-Nov-2025 09:50:49 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php on line 22 [19-Nov-2025 12:32:02 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\TranslatorBagInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php on line 17 [19-Nov-2025 16:32:41 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php on line 22 [24-Nov-2025 10:07:50 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatableInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatableMessage.php on line 20 [24-Nov-2025 10:13:46 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LoggingTranslator.php on line 22 [24-Nov-2025 10:18:32 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/PseudoLocalizationTranslator.php on line 19 [24-Nov-2025 10:18:33 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\LocaleAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/LocaleSwitcher.php on line 20 [24-Nov-2025 11:51:48 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php:34 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Translator.php on line 34 [24-Nov-2025 11:54:58 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\TranslatorBagInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php:17 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/TranslatorBag.php on line 17 [24-Nov-2025 11:55:36 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\MessageCatalogueInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/MessageCatalogue.php on line 20 [24-Nov-2025 12:49:12 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Contracts\Translation\TranslatorInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollectorTranslator.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DataCollectorTranslator.php on line 22 TranslatableMessage.php 0000644 00000003010 15111175546 0011176 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Contracts\Translation\TranslatableInterface; use Symfony\Contracts\Translation\TranslatorInterface; /** * @author Nate Wiebe <nate@northern.co> */ class TranslatableMessage implements TranslatableInterface { private string $message; private array $parameters; private ?string $domain; public function __construct(string $message, array $parameters = [], string $domain = null) { $this->message = $message; $this->parameters = $parameters; $this->domain = $domain; } public function __toString(): string { return $this->getMessage(); } public function getMessage(): string { return $this->message; } public function getParameters(): array { return $this->parameters; } public function getDomain(): ?string { return $this->domain; } public function trans(TranslatorInterface $translator, string $locale = null): string { return $translator->trans($this->getMessage(), array_map( static fn ($parameter) => $parameter instanceof TranslatableInterface ? $parameter->trans($translator, $locale) : $parameter, $this->getParameters() ), $this->getDomain(), $locale); } } DependencyInjection/TranslationExtractorPass.php 0000644 00000002575 15111175546 0016236 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\RuntimeException; use Symfony\Component\DependencyInjection\Reference; /** * Adds tagged translation.extractor services to translation extractor. */ class TranslationExtractorPass implements CompilerPassInterface { /** * @return void */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('translation.extractor')) { return; } $definition = $container->getDefinition('translation.extractor'); foreach ($container->findTaggedServiceIds('translation.extractor', true) as $id => $attributes) { if (!isset($attributes[0]['alias'])) { throw new RuntimeException(sprintf('The alias for the tag "translation.extractor" of service "%s" must be set.', $id)); } $definition->addMethodCall('addExtractor', [$attributes[0]['alias'], new Reference($id)]); } } } DependencyInjection/TranslatorPathsPass.php 0000644 00000011265 15111175547 0015172 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\TraceableValueResolver; /** * @author Yonel Ceruto <yonelceruto@gmail.com> */ class TranslatorPathsPass extends AbstractRecursivePass { private int $level = 0; /** * @var array<string, bool> */ private array $paths = []; /** * @var array<int, Definition> */ private array $definitions = []; /** * @var array<string, array<string, bool>> */ private array $controllers = []; /** * @return void */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('translator')) { return; } foreach ($this->findControllerArguments($container) as $controller => $argument) { $id = substr($controller, 0, strpos($controller, ':') ?: \strlen($controller)); if ($container->hasDefinition($id)) { [$locatorRef] = $argument->getValues(); $this->controllers[(string) $locatorRef][$container->getDefinition($id)->getClass()] = true; } } try { parent::process($container); $paths = []; foreach ($this->paths as $class => $_) { if (($r = $container->getReflectionClass($class)) && !$r->isInterface()) { $paths[] = $r->getFileName(); foreach ($r->getTraits() as $trait) { $paths[] = $trait->getFileName(); } } } if ($paths) { if ($container->hasDefinition('console.command.translation_debug')) { $definition = $container->getDefinition('console.command.translation_debug'); $definition->replaceArgument(6, array_merge($definition->getArgument(6), $paths)); } if ($container->hasDefinition('console.command.translation_extract')) { $definition = $container->getDefinition('console.command.translation_extract'); $definition->replaceArgument(7, array_merge($definition->getArgument(7), $paths)); } } } finally { $this->level = 0; $this->paths = []; $this->definitions = []; } } protected function processValue(mixed $value, bool $isRoot = false): mixed { if ($value instanceof Reference) { if ('translator' === (string) $value) { for ($i = $this->level - 1; $i >= 0; --$i) { $class = $this->definitions[$i]->getClass(); if (ServiceLocator::class === $class) { if (!isset($this->controllers[$this->currentId])) { continue; } foreach ($this->controllers[$this->currentId] as $class => $_) { $this->paths[$class] = true; } } else { $this->paths[$class] = true; } break; } } return $value; } if ($value instanceof Definition) { $this->definitions[$this->level++] = $value; $value = parent::processValue($value, $isRoot); unset($this->definitions[--$this->level]); return $value; } return parent::processValue($value, $isRoot); } private function findControllerArguments(ContainerBuilder $container): array { if (!$container->has('argument_resolver.service')) { return []; } $resolverDef = $container->findDefinition('argument_resolver.service'); if (TraceableValueResolver::class === $resolverDef->getClass()) { $resolverDef = $container->getDefinition($resolverDef->getArgument(0)); } $argument = $resolverDef->getArgument(0); if ($argument instanceof Reference) { $argument = $container->getDefinition($argument); } return $argument->getArgument(0); } } DependencyInjection/TranslationDumperPass.php 0000644 00000002133 15111175547 0015506 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** * Adds tagged translation.formatter services to translation writer. */ class TranslationDumperPass implements CompilerPassInterface { /** * @return void */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('translation.writer')) { return; } $definition = $container->getDefinition('translation.writer'); foreach ($container->findTaggedServiceIds('translation.dumper', true) as $id => $attributes) { $definition->addMethodCall('addDumper', [$attributes[0]['alias'], new Reference($id)]); } } } DependencyInjection/error_log 0000644 00000006400 15111175547 0012411 0 ustar 00 [18-Nov-2025 23:33:28 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php on line 22 [18-Nov-2025 23:35:59 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php on line 21 [19-Nov-2025 03:06:01 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php on line 24 [19-Nov-2025 04:13:27 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPass.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPass.php on line 19 [19-Nov-2025 06:32:37 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\DependencyInjection\Compiler\AbstractRecursivePass" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPathsPass.php on line 24 [19-Nov-2025 06:34:54 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationDumperPass.php on line 21 [19-Nov-2025 06:34:58 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslationExtractorPass.php on line 22 [19-Nov-2025 11:01:33 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPass.php:19 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/DependencyInjection/TranslatorPass.php on line 19 DependencyInjection/TranslatorPass.php 0000644 00000007327 15111175547 0014176 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; class TranslatorPass implements CompilerPassInterface { /** * @return void */ public function process(ContainerBuilder $container) { if (!$container->hasDefinition('translator.default')) { return; } $loaders = []; $loaderRefs = []; foreach ($container->findTaggedServiceIds('translation.loader', true) as $id => $attributes) { $loaderRefs[$id] = new Reference($id); $loaders[$id][] = $attributes[0]['alias']; if (isset($attributes[0]['legacy-alias'])) { $loaders[$id][] = $attributes[0]['legacy-alias']; } } if ($container->hasDefinition('translation.reader')) { $definition = $container->getDefinition('translation.reader'); foreach ($loaders as $id => $formats) { foreach ($formats as $format) { $definition->addMethodCall('addLoader', [$format, $loaderRefs[$id]]); } } } $container ->findDefinition('translator.default') ->replaceArgument(0, ServiceLocatorTagPass::register($container, $loaderRefs)) ->replaceArgument(3, $loaders) ; if ($container->hasDefinition('validator') && $container->hasDefinition('translation.extractor.visitor.constraint')) { $constraintVisitorDefinition = $container->getDefinition('translation.extractor.visitor.constraint'); $constraintClassNames = []; foreach ($container->getDefinitions() as $definition) { if (!$definition->hasTag('validator.constraint_validator')) { continue; } // Resolve constraint validator FQCN even if defined as %foo.validator.class% parameter $className = $container->getParameterBag()->resolveValue($definition->getClass()); // Extraction of the constraint class name from the Constraint Validator FQCN $constraintClassNames[] = str_replace('Validator', '', substr(strrchr($className, '\\'), 1)); } $constraintVisitorDefinition->setArgument(0, $constraintClassNames); } if (!$container->hasParameter('twig.default_path')) { return; } $paths = array_keys($container->getDefinition('twig.template_iterator')->getArgument(1)); if ($container->hasDefinition('console.command.translation_debug')) { $definition = $container->getDefinition('console.command.translation_debug'); $definition->replaceArgument(4, $container->getParameter('twig.default_path')); if (\count($definition->getArguments()) > 6) { $definition->replaceArgument(6, $paths); } } if ($container->hasDefinition('console.command.translation_extract')) { $definition = $container->getDefinition('console.command.translation_extract'); $definition->replaceArgument(5, $container->getParameter('twig.default_path')); if (\count($definition->getArguments()) > 7) { $definition->replaceArgument(7, $paths); } } } } Test/ProviderFactoryTestCase.php 0000644 00000010376 15111175547 0012770 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\Translation\Dumper\XliffFileDumper; use Symfony\Component\Translation\Exception\IncompleteDsnException; use Symfony\Component\Translation\Exception\UnsupportedSchemeException; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\Provider\Dsn; use Symfony\Component\Translation\Provider\ProviderFactoryInterface; use Symfony\Component\Translation\TranslatorBagInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** * A test case to ease testing a translation provider factory. * * @author Mathieu Santostefano <msantostefano@protonmail.com> * * @internal */ abstract class ProviderFactoryTestCase extends TestCase { protected HttpClientInterface $client; protected LoggerInterface|MockObject $logger; protected string $defaultLocale; protected LoaderInterface|MockObject $loader; protected XliffFileDumper|MockObject $xliffFileDumper; protected TranslatorBagInterface|MockObject $translatorBag; abstract public function createFactory(): ProviderFactoryInterface; /** * @return iterable<array{0: bool, 1: string}> */ abstract public static function supportsProvider(): iterable; /** * @return iterable<array{0: string, 1: string}> */ abstract public static function createProvider(): iterable; /** * @return iterable<array{0: string, 1: string|null}> */ public static function unsupportedSchemeProvider(): iterable { return []; } /** * @return iterable<array{0: string, 1: string|null}> */ public static function incompleteDsnProvider(): iterable { return []; } /** * @dataProvider supportsProvider */ public function testSupports(bool $expected, string $dsn) { $factory = $this->createFactory(); $this->assertSame($expected, $factory->supports(new Dsn($dsn))); } /** * @dataProvider createProvider */ public function testCreate(string $expected, string $dsn) { $factory = $this->createFactory(); $provider = $factory->create(new Dsn($dsn)); $this->assertSame($expected, (string) $provider); } /** * @dataProvider unsupportedSchemeProvider */ public function testUnsupportedSchemeException(string $dsn, string $message = null) { $factory = $this->createFactory(); $dsn = new Dsn($dsn); $this->expectException(UnsupportedSchemeException::class); if (null !== $message) { $this->expectExceptionMessage($message); } $factory->create($dsn); } /** * @dataProvider incompleteDsnProvider */ public function testIncompleteDsnException(string $dsn, string $message = null) { $factory = $this->createFactory(); $dsn = new Dsn($dsn); $this->expectException(IncompleteDsnException::class); if (null !== $message) { $this->expectExceptionMessage($message); } $factory->create($dsn); } protected function getClient(): HttpClientInterface { return $this->client ??= new MockHttpClient(); } protected function getLogger(): LoggerInterface { return $this->logger ??= $this->createMock(LoggerInterface::class); } protected function getDefaultLocale(): string { return $this->defaultLocale ??= 'en'; } protected function getLoader(): LoaderInterface { return $this->loader ??= $this->createMock(LoaderInterface::class); } protected function getXliffFileDumper(): XliffFileDumper { return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class); } protected function getTranslatorBag(): TranslatorBagInterface { return $this->translatorBag ??= $this->createMock(TranslatorBagInterface::class); } } Test/ProviderTestCase.php 0000644 00000005066 15111175547 0011440 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\Translation\Dumper\XliffFileDumper; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\Provider\ProviderInterface; use Symfony\Component\Translation\TranslatorBagInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** * A test case to ease testing a translation provider. * * @author Mathieu Santostefano <msantostefano@protonmail.com> * * @internal */ abstract class ProviderTestCase extends TestCase { protected HttpClientInterface $client; protected LoggerInterface|MockObject $logger; protected string $defaultLocale; protected LoaderInterface|MockObject $loader; protected XliffFileDumper|MockObject $xliffFileDumper; protected TranslatorBagInterface|MockObject $translatorBag; abstract public static function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint): ProviderInterface; /** * @return iterable<array{0: ProviderInterface, 1: string}> */ abstract public static function toStringProvider(): iterable; /** * @dataProvider toStringProvider */ public function testToString(ProviderInterface $provider, string $expected) { $this->assertSame($expected, (string) $provider); } protected function getClient(): MockHttpClient { return $this->client ??= new MockHttpClient(); } protected function getLoader(): LoaderInterface { return $this->loader ??= $this->createMock(LoaderInterface::class); } protected function getLogger(): LoggerInterface { return $this->logger ??= $this->createMock(LoggerInterface::class); } protected function getDefaultLocale(): string { return $this->defaultLocale ??= 'en'; } protected function getXliffFileDumper(): XliffFileDumper { return $this->xliffFileDumper ??= $this->createMock(XliffFileDumper::class); } protected function getTranslatorBag(): TranslatorBagInterface { return $this->translatorBag ??= $this->createMock(TranslatorBagInterface::class); } } Test/error_log 0000644 00000002524 15111175547 0007412 0 ustar 00 [19-Nov-2025 12:48:42 UTC] PHP Fatal error: Uncaught Error: Class "PHPUnit\Framework\TestCase" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderTestCase.php:31 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderTestCase.php on line 31 [19-Nov-2025 12:54:39 UTC] PHP Fatal error: Uncaught Error: Class "PHPUnit\Framework\TestCase" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderFactoryTestCase.php:34 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderFactoryTestCase.php on line 34 [19-Nov-2025 18:40:13 UTC] PHP Fatal error: Uncaught Error: Class "PHPUnit\Framework\TestCase" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderFactoryTestCase.php:34 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderFactoryTestCase.php on line 34 [19-Nov-2025 18:40:14 UTC] PHP Fatal error: Uncaught Error: Class "PHPUnit\Framework\TestCase" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderTestCase.php:31 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Test/ProviderTestCase.php on line 31 IdentityTranslator.php 0000644 00000001225 15111175547 0011127 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatorInterface; use Symfony\Contracts\Translation\TranslatorTrait; /** * IdentityTranslator does not translate anything. * * @author Fabien Potencier <fabien@symfony.com> */ class IdentityTranslator implements TranslatorInterface, LocaleAwareInterface { use TranslatorTrait; } CHANGELOG.md 0000644 00000015606 15111175547 0006374 0 ustar 00 CHANGELOG ========= 6.2.7 ----- * [BC BREAK] The following data providers for `ProviderFactoryTestCase` are now static: `supportsProvider()`, `createProvider()`, `unsupportedSchemeProvider()`and `incompleteDsnProvider()` * [BC BREAK] `ProviderTestCase::toStringProvider()` is now static 6.2 --- * Deprecate `PhpStringTokenParser` * Deprecate `PhpExtractor` in favor of `PhpAstExtractor` * Add `PhpAstExtractor` (requires [nikic/php-parser](https://github.com/nikic/php-parser) to be installed) 6.1 --- * Parameters implementing `TranslatableInterface` are processed * Add the file extension to the `XliffFileDumper` constructor 5.4 --- * Add `github` format & autodetection to render errors as annotations when running the XLIFF linter command in a Github Actions environment. * Translation providers are not experimental anymore 5.3 --- * Add `translation:pull` and `translation:push` commands to manage translations with third-party providers * Add `TranslatorBagInterface::getCatalogues` method * Add support to load XLIFF string in `XliffFileLoader` 5.2.0 ----- * added support for calling `trans` with ICU formatted messages * added `PseudoLocalizationTranslator` * added `TranslatableMessage` objects that represent a message that can be translated * added the `t()` function to easily create `TranslatableMessage` objects * Added support for extracting messages from `TranslatableMessage` objects 5.1.0 ----- * added support for `name` attribute on `unit` element from xliff2 to be used as a translation key instead of always the `source` element 5.0.0 ----- * removed support for using `null` as the locale in `Translator` * removed `TranslatorInterface` * removed `MessageSelector` * removed `ChoiceMessageFormatterInterface` * removed `PluralizationRule` * removed `Interval` * removed `transChoice()` methods, use the trans() method instead with a %count% parameter * removed `FileDumper::setBackup()` and `TranslationWriter::disableBackup()` * removed `MessageFormatter::choiceFormat()` * added argument `$filename` to `PhpExtractor::parseTokens()` * removed support for implicit STDIN usage in the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. 4.4.0 ----- * deprecated support for using `null` as the locale in `Translator` * deprecated accepting STDIN implicitly when using the `lint:xliff` command, use `lint:xliff -` (append a dash) instead to make it explicit. * Marked the `TranslationDataCollector` class as `@final`. 4.3.0 ----- * Improved Xliff 1.2 loader to load the original file's metadata * Added `TranslatorPathsPass` 4.2.0 ----- * Started using ICU parent locales as fallback locales. * allow using the ICU message format using domains with the "+intl-icu" suffix * deprecated `Translator::transChoice()` in favor of using `Translator::trans()` with a `%count%` parameter * deprecated `TranslatorInterface` in favor of `Symfony\Contracts\Translation\TranslatorInterface` * deprecated `MessageSelector`, `Interval` and `PluralizationRules`; use `IdentityTranslator` instead * Added `IntlFormatter` and `IntlFormatterInterface` * added support for multiple files and directories in `XliffLintCommand` * Marked `Translator::getFallbackLocales()` and `TranslationDataCollector::getFallbackLocales()` as internal 4.1.0 ----- * The `FileDumper::setBackup()` method is deprecated. * The `TranslationWriter::disableBackup()` method is deprecated. * The `XliffFileDumper` will write "name" on the "unit" node when dumping XLIFF 2.0. 4.0.0 ----- * removed the backup feature of the `FileDumper` class * removed `TranslationWriter::writeTranslations()` method * removed support for passing `MessageSelector` instances to the constructor of the `Translator` class 3.4.0 ----- * Added `TranslationDumperPass` * Added `TranslationExtractorPass` * Added `TranslatorPass` * Added `TranslationReader` and `TranslationReaderInterface` * Added `<notes>` section to the Xliff 2.0 dumper. * Improved Xliff 2.0 loader to load `<notes>` section. * Added `TranslationWriterInterface` * Deprecated `TranslationWriter::writeTranslations` in favor of `TranslationWriter::write` * added support for adding custom message formatter and decoupling the default one. * Added `PhpExtractor` * Added `PhpStringTokenParser` 3.2.0 ----- * Added support for escaping `|` in plural translations with double pipe. 3.1.0 ----- * Deprecated the backup feature of the file dumper classes. 3.0.0 ----- * removed `FileDumper::format()` method. * Changed the visibility of the locale property in `Translator` from protected to private. 2.8.0 ----- * deprecated FileDumper::format(), overwrite FileDumper::formatCatalogue() instead. * deprecated Translator::getMessages(), rely on TranslatorBagInterface::getCatalogue() instead. * added `FileDumper::formatCatalogue` which allows format the catalogue without dumping it into file. * added option `json_encoding` to JsonFileDumper * added options `as_tree`, `inline` to YamlFileDumper * added support for XLIFF 2.0. * added support for XLIFF target and tool attributes. * added message parameters to DataCollectorTranslator. * [DEPRECATION] The `DiffOperation` class has been deprecated and will be removed in Symfony 3.0, since its operation has nothing to do with 'diff', so the class name is misleading. The `TargetOperation` class should be used for this use-case instead. 2.7.0 ----- * added DataCollectorTranslator for collecting the translated messages. 2.6.0 ----- * added possibility to cache catalogues * added TranslatorBagInterface * added LoggingTranslator * added Translator::getMessages() for retrieving the message catalogue as an array 2.5.0 ----- * added relative file path template to the file dumpers * added optional backup to the file dumpers * changed IcuResFileDumper to extend FileDumper 2.3.0 ----- * added classes to make operations on catalogues (like making a diff or a merge on 2 catalogues) * added Translator::getFallbackLocales() * deprecated Translator::setFallbackLocale() in favor of the new Translator::setFallbackLocales() method 2.2.0 ----- * QtTranslationsLoader class renamed to QtFileLoader. QtTranslationsLoader is deprecated and will be removed in 2.3. * [BC BREAK] uniformized the exception thrown by the load() method when an error occurs. The load() method now throws Symfony\Component\Translation\Exception\NotFoundResourceException when a resource cannot be found and Symfony\Component\Translation\Exception\InvalidResourceException when a resource is invalid. * changed the exception class thrown by some load() methods from \RuntimeException to \InvalidArgumentException (IcuDatFileLoader, IcuResFileLoader and QtFileLoader) 2.1.0 ----- * added support for more than one fallback locale * added support for extracting translation messages from templates (Twig and PHP) * added dumpers for translation catalogs * added support for QT, gettext, and ResourceBundles MessageCatalogue.php 0000644 00000023472 15111175547 0010505 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Config\Resource\ResourceInterface; use Symfony\Component\Translation\Exception\LogicException; /** * @author Fabien Potencier <fabien@symfony.com> */ class MessageCatalogue implements MessageCatalogueInterface, MetadataAwareInterface, CatalogueMetadataAwareInterface { private array $messages = []; private array $metadata = []; private array $catalogueMetadata = []; private array $resources = []; private string $locale; private ?MessageCatalogueInterface $fallbackCatalogue = null; private ?self $parent = null; /** * @param array $messages An array of messages classified by domain */ public function __construct(string $locale, array $messages = []) { $this->locale = $locale; $this->messages = $messages; } public function getLocale(): string { return $this->locale; } public function getDomains(): array { $domains = []; foreach ($this->messages as $domain => $messages) { if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); } $domains[$domain] = $domain; } return array_values($domains); } public function all(string $domain = null): array { if (null !== $domain) { // skip messages merge if intl-icu requested explicitly if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { return $this->messages[$domain] ?? []; } return ($this->messages[$domain.self::INTL_DOMAIN_SUFFIX] ?? []) + ($this->messages[$domain] ?? []); } $allMessages = []; foreach ($this->messages as $domain => $messages) { if (str_ends_with($domain, self::INTL_DOMAIN_SUFFIX)) { $domain = substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)); $allMessages[$domain] = $messages + ($allMessages[$domain] ?? []); } else { $allMessages[$domain] = ($allMessages[$domain] ?? []) + $messages; } } return $allMessages; } /** * @return void */ public function set(string $id, string $translation, string $domain = 'messages') { $this->add([$id => $translation], $domain); } public function has(string $id, string $domain = 'messages'): bool { if (isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) { return true; } if (null !== $this->fallbackCatalogue) { return $this->fallbackCatalogue->has($id, $domain); } return false; } public function defines(string $id, string $domain = 'messages'): bool { return isset($this->messages[$domain][$id]) || isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]); } public function get(string $id, string $domain = 'messages'): string { if (isset($this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id])) { return $this->messages[$domain.self::INTL_DOMAIN_SUFFIX][$id]; } if (isset($this->messages[$domain][$id])) { return $this->messages[$domain][$id]; } if (null !== $this->fallbackCatalogue) { return $this->fallbackCatalogue->get($id, $domain); } return $id; } /** * @return void */ public function replace(array $messages, string $domain = 'messages') { unset($this->messages[$domain], $this->messages[$domain.self::INTL_DOMAIN_SUFFIX]); $this->add($messages, $domain); } /** * @return void */ public function add(array $messages, string $domain = 'messages') { $altDomain = str_ends_with($domain, self::INTL_DOMAIN_SUFFIX) ? substr($domain, 0, -\strlen(self::INTL_DOMAIN_SUFFIX)) : $domain.self::INTL_DOMAIN_SUFFIX; foreach ($messages as $id => $message) { unset($this->messages[$altDomain][$id]); $this->messages[$domain][$id] = $message; } if ([] === ($this->messages[$altDomain] ?? null)) { unset($this->messages[$altDomain]); } } /** * @return void */ public function addCatalogue(MessageCatalogueInterface $catalogue) { if ($catalogue->getLocale() !== $this->locale) { throw new LogicException(sprintf('Cannot add a catalogue for locale "%s" as the current locale for this catalogue is "%s".', $catalogue->getLocale(), $this->locale)); } foreach ($catalogue->all() as $domain => $messages) { if ($intlMessages = $catalogue->all($domain.self::INTL_DOMAIN_SUFFIX)) { $this->add($intlMessages, $domain.self::INTL_DOMAIN_SUFFIX); $messages = array_diff_key($messages, $intlMessages); } $this->add($messages, $domain); } foreach ($catalogue->getResources() as $resource) { $this->addResource($resource); } if ($catalogue instanceof MetadataAwareInterface) { $metadata = $catalogue->getMetadata('', ''); $this->addMetadata($metadata); } if ($catalogue instanceof CatalogueMetadataAwareInterface) { $catalogueMetadata = $catalogue->getCatalogueMetadata('', ''); $this->addCatalogueMetadata($catalogueMetadata); } } /** * @return void */ public function addFallbackCatalogue(MessageCatalogueInterface $catalogue) { // detect circular references $c = $catalogue; while ($c = $c->getFallbackCatalogue()) { if ($c->getLocale() === $this->getLocale()) { throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale())); } } $c = $this; do { if ($c->getLocale() === $catalogue->getLocale()) { throw new LogicException(sprintf('Circular reference detected when adding a fallback catalogue for locale "%s".', $catalogue->getLocale())); } foreach ($catalogue->getResources() as $resource) { $c->addResource($resource); } } while ($c = $c->parent); $catalogue->parent = $this; $this->fallbackCatalogue = $catalogue; foreach ($catalogue->getResources() as $resource) { $this->addResource($resource); } } public function getFallbackCatalogue(): ?MessageCatalogueInterface { return $this->fallbackCatalogue; } public function getResources(): array { return array_values($this->resources); } /** * @return void */ public function addResource(ResourceInterface $resource) { $this->resources[$resource->__toString()] = $resource; } public function getMetadata(string $key = '', string $domain = 'messages'): mixed { if ('' == $domain) { return $this->metadata; } if (isset($this->metadata[$domain])) { if ('' == $key) { return $this->metadata[$domain]; } if (isset($this->metadata[$domain][$key])) { return $this->metadata[$domain][$key]; } } return null; } /** * @return void */ public function setMetadata(string $key, mixed $value, string $domain = 'messages') { $this->metadata[$domain][$key] = $value; } /** * @return void */ public function deleteMetadata(string $key = '', string $domain = 'messages') { if ('' == $domain) { $this->metadata = []; } elseif ('' == $key) { unset($this->metadata[$domain]); } else { unset($this->metadata[$domain][$key]); } } public function getCatalogueMetadata(string $key = '', string $domain = 'messages'): mixed { if (!$domain) { return $this->catalogueMetadata; } if (isset($this->catalogueMetadata[$domain])) { if (!$key) { return $this->catalogueMetadata[$domain]; } if (isset($this->catalogueMetadata[$domain][$key])) { return $this->catalogueMetadata[$domain][$key]; } } return null; } /** * @return void */ public function setCatalogueMetadata(string $key, mixed $value, string $domain = 'messages') { $this->catalogueMetadata[$domain][$key] = $value; } /** * @return void */ public function deleteCatalogueMetadata(string $key = '', string $domain = 'messages') { if (!$domain) { $this->catalogueMetadata = []; } elseif (!$key) { unset($this->catalogueMetadata[$domain]); } else { unset($this->catalogueMetadata[$domain][$key]); } } /** * Adds current values with the new values. * * @param array $values Values to add */ private function addMetadata(array $values): void { foreach ($values as $domain => $keys) { foreach ($keys as $key => $value) { $this->setMetadata($key, $value, $domain); } } } private function addCatalogueMetadata(array $values): void { foreach ($values as $domain => $keys) { foreach ($keys as $key => $value) { $this->setCatalogueMetadata($key, $value, $domain); } } } } MetadataAwareInterface.php 0000644 00000002612 15111175547 0011606 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; /** * This interface is used to get, set, and delete metadata about the translation messages. * * @author Fabien Potencier <fabien@symfony.com> */ interface MetadataAwareInterface { /** * Gets metadata for the given domain and key. * * Passing an empty domain will return an array with all metadata indexed by * domain and then by key. Passing an empty key will return an array with all * metadata for the given domain. * * @return mixed The value that was set or an array with the domains/keys or null */ public function getMetadata(string $key = '', string $domain = 'messages'): mixed; /** * Adds metadata to a message domain. * * @return void */ public function setMetadata(string $key, mixed $value, string $domain = 'messages'); /** * Deletes metadata for the given key and domain. * * Passing an empty domain will delete all metadata. Passing an empty key will * delete all metadata for the given domain. * * @return void */ public function deleteMetadata(string $key = '', string $domain = 'messages'); } Translator.php 0000644 00000034152 15111175547 0007422 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation; use Symfony\Component\Config\ConfigCacheFactory; use Symfony\Component\Config\ConfigCacheFactoryInterface; use Symfony\Component\Config\ConfigCacheInterface; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\NotFoundResourceException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\Formatter\IntlFormatterInterface; use Symfony\Component\Translation\Formatter\MessageFormatter; use Symfony\Component\Translation\Formatter\MessageFormatterInterface; use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Contracts\Translation\LocaleAwareInterface; use Symfony\Contracts\Translation\TranslatableInterface; use Symfony\Contracts\Translation\TranslatorInterface; // Help opcache.preload discover always-needed symbols class_exists(MessageCatalogue::class); /** * @author Fabien Potencier <fabien@symfony.com> */ class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface { /** * @var MessageCatalogueInterface[] */ protected $catalogues = []; private string $locale; /** * @var string[] */ private array $fallbackLocales = []; /** * @var LoaderInterface[] */ private array $loaders = []; private array $resources = []; private MessageFormatterInterface $formatter; private ?string $cacheDir; private bool $debug; private array $cacheVary; private ?ConfigCacheFactoryInterface $configCacheFactory; private array $parentLocales; private bool $hasIntlFormatter; /** * @throws InvalidArgumentException If a locale contains invalid characters */ public function __construct(string $locale, MessageFormatterInterface $formatter = null, string $cacheDir = null, bool $debug = false, array $cacheVary = []) { $this->setLocale($locale); $this->formatter = $formatter ??= new MessageFormatter(); $this->cacheDir = $cacheDir; $this->debug = $debug; $this->cacheVary = $cacheVary; $this->hasIntlFormatter = $formatter instanceof IntlFormatterInterface; } /** * @return void */ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory) { $this->configCacheFactory = $configCacheFactory; } /** * Adds a Loader. * * @param string $format The name of the loader (@see addResource()) * * @return void */ public function addLoader(string $format, LoaderInterface $loader) { $this->loaders[$format] = $loader; } /** * Adds a Resource. * * @param string $format The name of the loader (@see addLoader()) * @param mixed $resource The resource name * * @return void * * @throws InvalidArgumentException If the locale contains invalid characters */ public function addResource(string $format, mixed $resource, string $locale, string $domain = null) { $domain ??= 'messages'; $this->assertValidLocale($locale); $locale ?: $locale = class_exists(\Locale::class) ? \Locale::getDefault() : 'en'; $this->resources[$locale][] = [$format, $resource, $domain]; if (\in_array($locale, $this->fallbackLocales)) { $this->catalogues = []; } else { unset($this->catalogues[$locale]); } } /** * @return void */ public function setLocale(string $locale) { $this->assertValidLocale($locale); $this->locale = $locale; } public function getLocale(): string { return $this->locale ?: (class_exists(\Locale::class) ? \Locale::getDefault() : 'en'); } /** * Sets the fallback locales. * * @param string[] $locales * * @return void * * @throws InvalidArgumentException If a locale contains invalid characters */ public function setFallbackLocales(array $locales) { // needed as the fallback locales are linked to the already loaded catalogues $this->catalogues = []; foreach ($locales as $locale) { $this->assertValidLocale($locale); } $this->fallbackLocales = $this->cacheVary['fallback_locales'] = $locales; } /** * Gets the fallback locales. * * @internal */ public function getFallbackLocales(): array { return $this->fallbackLocales; } public function trans(?string $id, array $parameters = [], string $domain = null, string $locale = null): string { if (null === $id || '' === $id) { return ''; } $domain ??= 'messages'; $catalogue = $this->getCatalogue($locale); $locale = $catalogue->getLocale(); while (!$catalogue->defines($id, $domain)) { if ($cat = $catalogue->getFallbackCatalogue()) { $catalogue = $cat; $locale = $catalogue->getLocale(); } else { break; } } $parameters = array_map(fn ($parameter) => $parameter instanceof TranslatableInterface ? $parameter->trans($this, $locale) : $parameter, $parameters); $len = \strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX); if ($this->hasIntlFormatter && ($catalogue->defines($id, $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX) || (\strlen($domain) > $len && 0 === substr_compare($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX, -$len, $len))) ) { return $this->formatter->formatIntl($catalogue->get($id, $domain), $locale, $parameters); } return $this->formatter->format($catalogue->get($id, $domain), $locale, $parameters); } public function getCatalogue(string $locale = null): MessageCatalogueInterface { if (!$locale) { $locale = $this->getLocale(); } else { $this->assertValidLocale($locale); } if (!isset($this->catalogues[$locale])) { $this->loadCatalogue($locale); } return $this->catalogues[$locale]; } public function getCatalogues(): array { return array_values($this->catalogues); } /** * Gets the loaders. * * @return LoaderInterface[] */ protected function getLoaders(): array { return $this->loaders; } /** * @return void */ protected function loadCatalogue(string $locale) { if (null === $this->cacheDir) { $this->initializeCatalogue($locale); } else { $this->initializeCacheCatalogue($locale); } } /** * @return void */ protected function initializeCatalogue(string $locale) { $this->assertValidLocale($locale); try { $this->doLoadCatalogue($locale); } catch (NotFoundResourceException $e) { if (!$this->computeFallbackLocales($locale)) { throw $e; } } $this->loadFallbackCatalogues($locale); } private function initializeCacheCatalogue(string $locale): void { if (isset($this->catalogues[$locale])) { /* Catalogue already initialized. */ return; } $this->assertValidLocale($locale); $cache = $this->getConfigCacheFactory()->cache($this->getCatalogueCachePath($locale), function (ConfigCacheInterface $cache) use ($locale) { $this->dumpCatalogue($locale, $cache); } ); if (isset($this->catalogues[$locale])) { /* Catalogue has been initialized as it was written out to cache. */ return; } /* Read catalogue from cache. */ $this->catalogues[$locale] = include $cache->getPath(); } private function dumpCatalogue(string $locale, ConfigCacheInterface $cache): void { $this->initializeCatalogue($locale); $fallbackContent = $this->getFallbackContent($this->catalogues[$locale]); $content = sprintf(<<<EOF <?php use Symfony\Component\Translation\MessageCatalogue; \$catalogue = new MessageCatalogue('%s', %s); %s return \$catalogue; EOF , $locale, var_export($this->getAllMessages($this->catalogues[$locale]), true), $fallbackContent ); $cache->write($content, $this->catalogues[$locale]->getResources()); } private function getFallbackContent(MessageCatalogue $catalogue): string { $fallbackContent = ''; $current = ''; $replacementPattern = '/[^a-z0-9_]/i'; $fallbackCatalogue = $catalogue->getFallbackCatalogue(); while ($fallbackCatalogue) { $fallback = $fallbackCatalogue->getLocale(); $fallbackSuffix = ucfirst(preg_replace($replacementPattern, '_', $fallback)); $currentSuffix = ucfirst(preg_replace($replacementPattern, '_', $current)); $fallbackContent .= sprintf(<<<'EOF' $catalogue%s = new MessageCatalogue('%s', %s); $catalogue%s->addFallbackCatalogue($catalogue%s); EOF , $fallbackSuffix, $fallback, var_export($this->getAllMessages($fallbackCatalogue), true), $currentSuffix, $fallbackSuffix ); $current = $fallbackCatalogue->getLocale(); $fallbackCatalogue = $fallbackCatalogue->getFallbackCatalogue(); } return $fallbackContent; } private function getCatalogueCachePath(string $locale): string { return $this->cacheDir.'/catalogue.'.$locale.'.'.strtr(substr(base64_encode(hash('sha256', serialize($this->cacheVary), true)), 0, 7), '/', '_').'.php'; } /** * @internal */ protected function doLoadCatalogue(string $locale): void { $this->catalogues[$locale] = new MessageCatalogue($locale); if (isset($this->resources[$locale])) { foreach ($this->resources[$locale] as $resource) { if (!isset($this->loaders[$resource[0]])) { if (\is_string($resource[1])) { throw new RuntimeException(sprintf('No loader is registered for the "%s" format when loading the "%s" resource.', $resource[0], $resource[1])); } throw new RuntimeException(sprintf('No loader is registered for the "%s" format.', $resource[0])); } $this->catalogues[$locale]->addCatalogue($this->loaders[$resource[0]]->load($resource[1], $locale, $resource[2])); } } } private function loadFallbackCatalogues(string $locale): void { $current = $this->catalogues[$locale]; foreach ($this->computeFallbackLocales($locale) as $fallback) { if (!isset($this->catalogues[$fallback])) { $this->initializeCatalogue($fallback); } $fallbackCatalogue = new MessageCatalogue($fallback, $this->getAllMessages($this->catalogues[$fallback])); foreach ($this->catalogues[$fallback]->getResources() as $resource) { $fallbackCatalogue->addResource($resource); } $current->addFallbackCatalogue($fallbackCatalogue); $current = $fallbackCatalogue; } } /** * @return array */ protected function computeFallbackLocales(string $locale) { $this->parentLocales ??= json_decode(file_get_contents(__DIR__.'/Resources/data/parents.json'), true); $originLocale = $locale; $locales = []; while ($locale) { $parent = $this->parentLocales[$locale] ?? null; if ($parent) { $locale = 'root' !== $parent ? $parent : null; } elseif (\function_exists('locale_parse')) { $localeSubTags = locale_parse($locale); $locale = null; if (1 < \count($localeSubTags)) { array_pop($localeSubTags); $locale = locale_compose($localeSubTags) ?: null; } } elseif ($i = strrpos($locale, '_') ?: strrpos($locale, '-')) { $locale = substr($locale, 0, $i); } else { $locale = null; } if (null !== $locale) { $locales[] = $locale; } } foreach ($this->fallbackLocales as $fallback) { if ($fallback === $originLocale) { continue; } $locales[] = $fallback; } return array_unique($locales); } /** * Asserts that the locale is valid, throws an Exception if not. * * @return void * * @throws InvalidArgumentException If the locale contains invalid characters */ protected function assertValidLocale(string $locale) { if (!preg_match('/^[a-z0-9@_\\.\\-]*$/i', $locale)) { throw new InvalidArgumentException(sprintf('Invalid "%s" locale.', $locale)); } } /** * Provides the ConfigCache factory implementation, falling back to a * default implementation if necessary. */ private function getConfigCacheFactory(): ConfigCacheFactoryInterface { $this->configCacheFactory ??= new ConfigCacheFactory($this->debug); return $this->configCacheFactory; } private function getAllMessages(MessageCatalogueInterface $catalogue): array { $allMessages = []; foreach ($catalogue->all() as $domain => $messages) { if ($intlMessages = $catalogue->all($domain.MessageCatalogue::INTL_DOMAIN_SUFFIX)) { $allMessages[$domain.MessageCatalogue::INTL_DOMAIN_SUFFIX] = $intlMessages; $messages = array_diff_key($messages, $intlMessages); } if ($messages) { $allMessages[$domain] = $messages; } } return $allMessages; } } Dumper/PoFileDumper.php 0000644 00000007623 15111175547 0011063 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * PoFileDumper generates a gettext formatted string representation of a message catalogue. * * @author Stealth35 */ class PoFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $output = 'msgid ""'."\n"; $output .= 'msgstr ""'."\n"; $output .= '"Content-Type: text/plain; charset=UTF-8\n"'."\n"; $output .= '"Content-Transfer-Encoding: 8bit\n"'."\n"; $output .= '"Language: '.$messages->getLocale().'\n"'."\n"; $output .= "\n"; $newLine = false; foreach ($messages->all($domain) as $source => $target) { if ($newLine) { $output .= "\n"; } else { $newLine = true; } $metadata = $messages->getMetadata($source, $domain); if (isset($metadata['comments'])) { $output .= $this->formatComments($metadata['comments']); } if (isset($metadata['flags'])) { $output .= $this->formatComments(implode(',', (array) $metadata['flags']), ','); } if (isset($metadata['sources'])) { $output .= $this->formatComments(implode(' ', (array) $metadata['sources']), ':'); } $sourceRules = $this->getStandardRules($source); $targetRules = $this->getStandardRules($target); if (2 == \count($sourceRules) && [] !== $targetRules) { $output .= sprintf('msgid "%s"'."\n", $this->escape($sourceRules[0])); $output .= sprintf('msgid_plural "%s"'."\n", $this->escape($sourceRules[1])); foreach ($targetRules as $i => $targetRule) { $output .= sprintf('msgstr[%d] "%s"'."\n", $i, $this->escape($targetRule)); } } else { $output .= sprintf('msgid "%s"'."\n", $this->escape($source)); $output .= sprintf('msgstr "%s"'."\n", $this->escape($target)); } } return $output; } private function getStandardRules(string $id): array { // Partly copied from TranslatorTrait::trans. $parts = []; if (preg_match('/^\|++$/', $id)) { $parts = explode('|', $id); } elseif (preg_match_all('/(?:\|\||[^\|])++/', $id, $matches)) { $parts = $matches[0]; } $intervalRegexp = <<<'EOF' /^(?P<interval> ({\s* (\-?\d+(\.\d+)?[\s*,\s*\-?\d+(\.\d+)?]*) \s*}) | (?P<left_delimiter>[\[\]]) \s* (?P<left>-Inf|\-?\d+(\.\d+)?) \s*,\s* (?P<right>\+?Inf|\-?\d+(\.\d+)?) \s* (?P<right_delimiter>[\[\]]) )\s*(?P<message>.*?)$/xs EOF; $standardRules = []; foreach ($parts as $part) { $part = trim(str_replace('||', '|', $part)); if (preg_match($intervalRegexp, $part)) { // Explicit rule is not a standard rule. return []; } else { $standardRules[] = $part; } } return $standardRules; } protected function getExtension(): string { return 'po'; } private function escape(string $str): string { return addcslashes($str, "\0..\37\42\134"); } private function formatComments(string|array $comments, string $prefix = ''): ?string { $output = null; foreach ((array) $comments as $comment) { $output .= sprintf('#%s %s'."\n", $prefix, $comment); } return $output; } } Dumper/IniFileDumper.php 0000644 00000001666 15111175547 0011225 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * IniFileDumper generates an ini formatted string representation of a message catalogue. * * @author Stealth35 */ class IniFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $output = ''; foreach ($messages->all($domain) as $source => $target) { $escapeTarget = str_replace('"', '\"', $target); $output .= $source.'="'.$escapeTarget."\"\n"; } return $output; } protected function getExtension(): string { return 'ini'; } } Dumper/XliffFileDumper.php 0000644 00000021613 15111175547 0011550 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\MessageCatalogue; /** * XliffFileDumper generates xliff files from a message catalogue. * * @author Michel Salib <michelsalib@hotmail.com> */ class XliffFileDumper extends FileDumper { public function __construct( private string $extension = 'xlf', ) { } public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $xliffVersion = '1.2'; if (\array_key_exists('xliff_version', $options)) { $xliffVersion = $options['xliff_version']; } if (\array_key_exists('default_locale', $options)) { $defaultLocale = $options['default_locale']; } else { $defaultLocale = \Locale::getDefault(); } if ('1.2' === $xliffVersion) { return $this->dumpXliff1($defaultLocale, $messages, $domain, $options); } if ('2.0' === $xliffVersion) { return $this->dumpXliff2($defaultLocale, $messages, $domain); } throw new InvalidArgumentException(sprintf('No support implemented for dumping XLIFF version "%s".', $xliffVersion)); } protected function getExtension(): string { return $this->extension; } private function dumpXliff1(string $defaultLocale, MessageCatalogue $messages, ?string $domain, array $options = []): string { $toolInfo = ['tool-id' => 'symfony', 'tool-name' => 'Symfony']; if (\array_key_exists('tool_info', $options)) { $toolInfo = array_merge($toolInfo, $options['tool_info']); } $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; $xliff = $dom->appendChild($dom->createElement('xliff')); $xliff->setAttribute('version', '1.2'); $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:1.2'); $xliffFile = $xliff->appendChild($dom->createElement('file')); $xliffFile->setAttribute('source-language', str_replace('_', '-', $defaultLocale)); $xliffFile->setAttribute('target-language', str_replace('_', '-', $messages->getLocale())); $xliffFile->setAttribute('datatype', 'plaintext'); $xliffFile->setAttribute('original', 'file.ext'); $xliffHead = $xliffFile->appendChild($dom->createElement('header')); $xliffTool = $xliffHead->appendChild($dom->createElement('tool')); foreach ($toolInfo as $id => $value) { $xliffTool->setAttribute($id, $value); } if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) { $xliffPropGroup = $xliffHead->appendChild($dom->createElement('prop-group')); foreach ($catalogueMetadata as $key => $value) { $xliffProp = $xliffPropGroup->appendChild($dom->createElement('prop')); $xliffProp->setAttribute('prop-type', $key); $xliffProp->appendChild($dom->createTextNode($value)); } } $xliffBody = $xliffFile->appendChild($dom->createElement('body')); foreach ($messages->all($domain) as $source => $target) { $translation = $dom->createElement('trans-unit'); $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); $translation->setAttribute('resname', $source); $s = $translation->appendChild($dom->createElement('source')); $s->appendChild($dom->createTextNode($source)); // Does the target contain characters requiring a CDATA section? $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); $targetElement = $dom->createElement('target'); $metadata = $messages->getMetadata($source, $domain); if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { foreach ($metadata['target-attributes'] as $name => $value) { $targetElement->setAttribute($name, $value); } } $t = $translation->appendChild($targetElement); $t->appendChild($text); if ($this->hasMetadataArrayInfo('notes', $metadata)) { foreach ($metadata['notes'] as $note) { if (!isset($note['content'])) { continue; } $n = $translation->appendChild($dom->createElement('note')); $n->appendChild($dom->createTextNode($note['content'])); if (isset($note['priority'])) { $n->setAttribute('priority', $note['priority']); } if (isset($note['from'])) { $n->setAttribute('from', $note['from']); } } } $xliffBody->appendChild($translation); } return $dom->saveXML(); } private function dumpXliff2(string $defaultLocale, MessageCatalogue $messages, ?string $domain): string { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; $xliff = $dom->appendChild($dom->createElement('xliff')); $xliff->setAttribute('xmlns', 'urn:oasis:names:tc:xliff:document:2.0'); $xliff->setAttribute('version', '2.0'); $xliff->setAttribute('srcLang', str_replace('_', '-', $defaultLocale)); $xliff->setAttribute('trgLang', str_replace('_', '-', $messages->getLocale())); $xliffFile = $xliff->appendChild($dom->createElement('file')); if (str_ends_with($domain, MessageCatalogue::INTL_DOMAIN_SUFFIX)) { $xliffFile->setAttribute('id', substr($domain, 0, -\strlen(MessageCatalogue::INTL_DOMAIN_SUFFIX)).'.'.$messages->getLocale()); } else { $xliffFile->setAttribute('id', $domain.'.'.$messages->getLocale()); } if ($catalogueMetadata = $messages->getCatalogueMetadata('', $domain) ?? []) { $xliff->setAttribute('xmlns:m', 'urn:oasis:names:tc:xliff:metadata:2.0'); $xliffMetadata = $xliffFile->appendChild($dom->createElement('m:metadata')); foreach ($catalogueMetadata as $key => $value) { $xliffMeta = $xliffMetadata->appendChild($dom->createElement('prop')); $xliffMeta->setAttribute('type', $key); $xliffMeta->appendChild($dom->createTextNode($value)); } } foreach ($messages->all($domain) as $source => $target) { $translation = $dom->createElement('unit'); $translation->setAttribute('id', strtr(substr(base64_encode(hash('sha256', $source, true)), 0, 7), '/+', '._')); if (\strlen($source) <= 80) { $translation->setAttribute('name', $source); } $metadata = $messages->getMetadata($source, $domain); // Add notes section if ($this->hasMetadataArrayInfo('notes', $metadata)) { $notesElement = $dom->createElement('notes'); foreach ($metadata['notes'] as $note) { $n = $dom->createElement('note'); $n->appendChild($dom->createTextNode($note['content'] ?? '')); unset($note['content']); foreach ($note as $name => $value) { $n->setAttribute($name, $value); } $notesElement->appendChild($n); } $translation->appendChild($notesElement); } $segment = $translation->appendChild($dom->createElement('segment')); $s = $segment->appendChild($dom->createElement('source')); $s->appendChild($dom->createTextNode($source)); // Does the target contain characters requiring a CDATA section? $text = 1 === preg_match('/[&<>]/', $target) ? $dom->createCDATASection($target) : $dom->createTextNode($target); $targetElement = $dom->createElement('target'); if ($this->hasMetadataArrayInfo('target-attributes', $metadata)) { foreach ($metadata['target-attributes'] as $name => $value) { $targetElement->setAttribute($name, $value); } } $t = $segment->appendChild($targetElement); $t->appendChild($text); $xliffFile->appendChild($translation); } return $dom->saveXML(); } private function hasMetadataArrayInfo(string $key, array $metadata = null): bool { return is_iterable($metadata[$key] ?? null); } } Dumper/IcuResFileDumper.php 0000644 00000005526 15111175550 0011671 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * IcuResDumper generates an ICU ResourceBundle formatted string representation of a message catalogue. * * @author Stealth35 */ class IcuResFileDumper extends FileDumper { protected $relativePathTemplate = '%domain%/%locale%.%extension%'; public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $data = $indexes = $resources = ''; foreach ($messages->all($domain) as $source => $target) { $indexes .= pack('v', \strlen($data) + 28); $data .= $source."\0"; } $data .= $this->writePadding($data); $keyTop = $this->getPosition($data); foreach ($messages->all($domain) as $source => $target) { $resources .= pack('V', $this->getPosition($data)); $data .= pack('V', \strlen($target)) .mb_convert_encoding($target."\0", 'UTF-16LE', 'UTF-8') .$this->writePadding($data) ; } $resOffset = $this->getPosition($data); $data .= pack('v', \count($messages->all($domain))) .$indexes .$this->writePadding($data) .$resources ; $bundleTop = $this->getPosition($data); $root = pack('V7', $resOffset + (2 << 28), // Resource Offset + Resource Type 6, // Index length $keyTop, // Index keys top $bundleTop, // Index resources top $bundleTop, // Index bundle top \count($messages->all($domain)), // Index max table length 0 // Index attributes ); $header = pack('vC2v4C12@32', 32, // Header size 0xDA, 0x27, // Magic number 1 and 2 20, 0, 0, 2, // Rest of the header, ..., Size of a char 0x52, 0x65, 0x73, 0x42, // Data format identifier 1, 2, 0, 0, // Data version 1, 4, 0, 0 // Unicode version ); return $header.$root.$data; } private function writePadding(string $data): ?string { $padding = \strlen($data) % 4; return $padding ? str_repeat("\xAA", 4 - $padding) : null; } private function getPosition(string $data): float|int { return (\strlen($data) + 28) / 4; } protected function getExtension(): string { return 'res'; } } Dumper/JsonFileDumper.php 0000644 00000001505 15111175550 0011401 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * JsonFileDumper generates an json formatted string representation of a message catalogue. * * @author singles */ class JsonFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $flags = $options['json_encoding'] ?? \JSON_PRETTY_PRINT; return json_encode($messages->all($domain), $flags); } protected function getExtension(): string { return 'json'; } } Dumper/error_log 0000644 00000015072 15111175550 0007723 0 ustar 00 [20-Nov-2025 04:04:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/YamlFileDumper.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/YamlFileDumper.php on line 24 [20-Nov-2025 04:06:07 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/XliffFileDumper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/XliffFileDumper.php on line 22 [20-Nov-2025 04:11:39 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/CsvFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/CsvFileDumper.php on line 21 [20-Nov-2025 04:12:42 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/JsonFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/JsonFileDumper.php on line 21 [20-Nov-2025 04:13:45 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PhpFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PhpFileDumper.php on line 21 [20-Nov-2025 04:15:50 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/IniFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/IniFileDumper.php on line 21 [20-Nov-2025 04:38:00 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/MoFileDumper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/MoFileDumper.php on line 22 [20-Nov-2025 04:38:22 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PoFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PoFileDumper.php on line 21 [20-Nov-2025 04:45:04 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/QtFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/QtFileDumper.php on line 21 [20-Nov-2025 08:21:31 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/CsvFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/CsvFileDumper.php on line 21 [20-Nov-2025 08:23:29 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/JsonFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/JsonFileDumper.php on line 21 [20-Nov-2025 08:24:27 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PhpFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PhpFileDumper.php on line 21 [20-Nov-2025 08:26:33 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/IniFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/IniFileDumper.php on line 21 [20-Nov-2025 08:27:31 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/XliffFileDumper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/XliffFileDumper.php on line 22 [20-Nov-2025 08:30:29 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/YamlFileDumper.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/YamlFileDumper.php on line 24 [20-Nov-2025 08:49:28 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Translation\Dumper\DumperInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/FileDumper.php:26 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/FileDumper.php on line 26 [20-Nov-2025 09:12:55 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/QtFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/QtFileDumper.php on line 21 [20-Nov-2025 09:13:01 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/MoFileDumper.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/MoFileDumper.php on line 22 [20-Nov-2025 09:13:04 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Translation\Dumper\FileDumper" not found in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PoFileDumper.php:21 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/translation/Dumper/PoFileDumper.php on line 21 Dumper/YamlFileDumper.php 0000644 00000003001 15111175550 0011363 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\Exception\LogicException; use Symfony\Component\Translation\MessageCatalogue; use Symfony\Component\Translation\Util\ArrayConverter; use Symfony\Component\Yaml\Yaml; /** * YamlFileDumper generates yaml files from a message catalogue. * * @author Michel Salib <michelsalib@hotmail.com> */ class YamlFileDumper extends FileDumper { private string $extension; public function __construct(string $extension = 'yml') { $this->extension = $extension; } public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { if (!class_exists(Yaml::class)) { throw new LogicException('Dumping translations in the YAML format requires the Symfony Yaml component.'); } $data = $messages->all($domain); if (isset($options['as_tree']) && $options['as_tree']) { $data = ArrayConverter::expandToTree($data); } if (isset($options['inline']) && ($inline = (int) $options['inline']) > 0) { return Yaml::dump($data, $inline); } return Yaml::dump($data); } protected function getExtension(): string { return $this->extension; } } Dumper/CsvFileDumper.php 0000644 00000002555 15111175550 0011231 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * CsvFileDumper generates a csv formatted string representation of a message catalogue. * * @author Stealth35 */ class CsvFileDumper extends FileDumper { private string $delimiter = ';'; private string $enclosure = '"'; public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $handle = fopen('php://memory', 'r+'); foreach ($messages->all($domain) as $source => $target) { fputcsv($handle, [$source, $target], $this->delimiter, $this->enclosure); } rewind($handle); $output = stream_get_contents($handle); fclose($handle); return $output; } /** * Sets the delimiter and escape character for CSV. * * @return void */ public function setCsvControl(string $delimiter = ';', string $enclosure = '"') { $this->delimiter = $delimiter; $this->enclosure = $enclosure; } protected function getExtension(): string { return 'csv'; } } Dumper/MoFileDumper.php 0000644 00000004350 15111175550 0011044 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\Loader\MoFileLoader; use Symfony\Component\Translation\MessageCatalogue; /** * MoFileDumper generates a gettext formatted string representation of a message catalogue. * * @author Stealth35 */ class MoFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $sources = $targets = $sourceOffsets = $targetOffsets = ''; $offsets = []; $size = 0; foreach ($messages->all($domain) as $source => $target) { $offsets[] = array_map('strlen', [$sources, $source, $targets, $target]); $sources .= "\0".$source; $targets .= "\0".$target; ++$size; } $header = [ 'magicNumber' => MoFileLoader::MO_LITTLE_ENDIAN_MAGIC, 'formatRevision' => 0, 'count' => $size, 'offsetId' => MoFileLoader::MO_HEADER_SIZE, 'offsetTranslated' => MoFileLoader::MO_HEADER_SIZE + (8 * $size), 'sizeHashes' => 0, 'offsetHashes' => MoFileLoader::MO_HEADER_SIZE + (16 * $size), ]; $sourcesSize = \strlen($sources); $sourcesStart = $header['offsetHashes'] + 1; foreach ($offsets as $offset) { $sourceOffsets .= $this->writeLong($offset[1]) .$this->writeLong($offset[0] + $sourcesStart); $targetOffsets .= $this->writeLong($offset[3]) .$this->writeLong($offset[2] + $sourcesStart + $sourcesSize); } $output = implode('', array_map($this->writeLong(...), $header)) .$sourceOffsets .$targetOffsets .$sources .$targets ; return $output; } protected function getExtension(): string { return 'mo'; } private function writeLong(mixed $str): string { return pack('V*', $str); } } Dumper/DumperInterface.php 0000644 00000001373 15111175550 0011573 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * DumperInterface is the interface implemented by all translation dumpers. * There is no common option. * * @author Michel Salib <michelsalib@hotmail.com> */ interface DumperInterface { /** * Dumps the message catalogue. * * @param array $options Options that are used by the dumper * * @return void */ public function dump(MessageCatalogue $messages, array $options = []); } Dumper/FileDumper.php 0000644 00000007072 15111175550 0010554 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\Exception\InvalidArgumentException; use Symfony\Component\Translation\Exception\RuntimeException; use Symfony\Component\Translation\MessageCatalogue; /** * FileDumper is an implementation of DumperInterface that dump a message catalogue to file(s). * * Options: * - path (mandatory): the directory where the files should be saved * * @author Michel Salib <michelsalib@hotmail.com> */ abstract class FileDumper implements DumperInterface { /** * A template for the relative paths to files. * * @var string */ protected $relativePathTemplate = '%domain%.%locale%.%extension%'; /** * Sets the template for the relative paths to files. * * @param string $relativePathTemplate A template for the relative paths to files * * @return void */ public function setRelativePathTemplate(string $relativePathTemplate) { $this->relativePathTemplate = $relativePathTemplate; } /** * @return void */ public function dump(MessageCatalogue $messages, array $options = []) { if (!\array_key_exists('path', $options)) { throw new InvalidArgumentException('The file dumper needs a path option.'); } // save a file for each domain foreach ($messages->getDomains() as $domain) { $fullpath = $options['path'].'/'.$this->getRelativePath($domain, $messages->getLocale()); if (!file_exists($fullpath)) { $directory = \dirname($fullpath); if (!file_exists($directory) && !@mkdir($directory, 0777, true)) { throw new RuntimeException(sprintf('Unable to create directory "%s".', $directory)); } } $intlDomain = $domain.MessageCatalogue::INTL_DOMAIN_SUFFIX; $intlMessages = $messages->all($intlDomain); if ($intlMessages) { $intlPath = $options['path'].'/'.$this->getRelativePath($intlDomain, $messages->getLocale()); file_put_contents($intlPath, $this->formatCatalogue($messages, $intlDomain, $options)); $messages->replace([], $intlDomain); try { if ($messages->all($domain)) { file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); } continue; } finally { $messages->replace($intlMessages, $intlDomain); } } file_put_contents($fullpath, $this->formatCatalogue($messages, $domain, $options)); } } /** * Transforms a domain of a message catalogue to its string representation. */ abstract public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string; /** * Gets the file extension of the dumper. */ abstract protected function getExtension(): string; /** * Gets the relative file path using the template. */ private function getRelativePath(string $domain, string $locale): string { return strtr($this->relativePathTemplate, [ '%domain%' => $domain, '%locale%' => $locale, '%extension%' => $this->getExtension(), ]); } } Dumper/PhpFileDumper.php 0000644 00000001430 15111175550 0011214 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * PhpFileDumper generates PHP files from a message catalogue. * * @author Michel Salib <michelsalib@hotmail.com> */ class PhpFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { return "<?php\n\nreturn ".var_export($messages->all($domain), true).";\n"; } protected function getExtension(): string { return 'php'; } } Dumper/QtFileDumper.php 0000644 00000003534 15111175550 0011060 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Translation\Dumper; use Symfony\Component\Translation\MessageCatalogue; /** * QtFileDumper generates ts files from a message catalogue. * * @author Benjamin Eberlei <kontakt@beberlei.de> */ class QtFileDumper extends FileDumper { public function formatCatalogue(MessageCatalogue $messages, string $domain, array $options = []): string { $dom = new \DOMDocument('1.0', 'utf-8'); $dom->formatOutput = true; $ts = $dom->appendChild($dom->createElement('TS')); $context = $ts->appendChild($dom->createElement('context')); $context->appendChild($dom->createElement('name', $domain)); foreach ($messages->all($domain) as $source => $target) { $message = $context->appendChild($dom->createElement('message')); $metadata = $messages->getMetadata($source, $domain); if (isset($metadata['sources'])) { foreach ((array) $metadata['sources'] as $location) { $loc = explode(':', $location, 2); $location = $message->appendChild($dom->createElement('location')); $location->setAttribute('filename', $loc[0]); if (isset($loc[1])) { $location->setAttribute('line', $loc[1]); } } } $message->appendChild($dom->createElement('source', $source)); $message->appendChild($dom->createElement('translation', $target)); } return $dom->saveXML(); } protected function getExtension(): string { return 'ts'; } }
Simpan